Merge bk://oe-devel.bkbits.net/openembedded
authornslu2-linux.adm@bkbits.net <nslu2-linux.adm@bkbits.net>
Wed, 30 Mar 2005 13:19:04 +0000 (13:19 +0000)
committernslu2-linux.adm@bkbits.net <nslu2-linux.adm@bkbits.net>
Wed, 30 Mar 2005 13:19:04 +0000 (13:19 +0000)
into bkbits.net:/repos/n/nslu2-linux/openembedded

2005/03/30 14:00:48+02:00 mn-solutions.de!schurig
MNCI "Ramses": newest bluez for the kernel

2005/03/30 13:59:44+02:00 mn-solutions.de!schurig
bluez-utils install the binaries into /{s}bin, not /usr/{s}bin, this patch fixes the scripts

2005/03/30 13:54:17+02:00 utwente.nl!koen
Merge bk://oe-devel@oe-devel.bkbits.net/openembedded
into bitbake.utwente.nl:/home/koen/OE/openembedded

2005/03/30 13:53:47+02:00 utwente.nl!koen
preferred-gpe-versions.inc:
  add working minimo CVSDATE

BKrev: 424aa748Ew_6rzmEpNqOmQsr4KaxYw

conf/distro/preferred-gpe-versions.inc
packages/bluez-utils/bluez-utils-common_2.14.inc
packages/bluez-utils/files/base.patch [new file with mode: 0644]
packages/bluez-utils/files/hcid.conf
packages/bluez-utils/files/openslug/hcid.conf
packages/bluez-utils/files/ramses/bluetooth.default [new file with mode: 0644]
packages/bluez-utils/files/ramses/hcid.conf [new file with mode: 0644]
packages/linux/mnci-ramses-2.4.21-rmk2-pxa1/mnci-combined.patch
packages/linux/mnci-ramses_2.4.21-rmk2-pxa1.bb

index afd60d0..337a0a2 100644 (file)
@@ -4,6 +4,7 @@
 
 #work around some breakages
 CVSDATE_xserver-kdrive=20050207
+CVSDATE_minimo=20050316
 
 #specify versions, as the header says :)
 PREFERRED_VERSION_libmatchbox ?= "1.6"
index bc92e28..08489c7 100644 (file)
@@ -11,6 +11,7 @@ FILES_${PN}-ciptool = "/bin/ciptool"
 FILES_${PN}-bluepin = "/bin/bluepin"
 
 SRC_URI = "http://bluez.sourceforge.net/download/bluez-utils-${PV}.tar.gz \
+       file://base.patch;patch=1 \
        file://hcid.conf \
        file://bluetooth.default"
 
diff --git a/packages/bluez-utils/files/base.patch b/packages/bluez-utils/files/base.patch
new file mode 100644 (file)
index 0000000..e69de29
index 6a1e06f..547602d 100644 (file)
@@ -23,7 +23,7 @@ options {
        pairing multi;
 
        # PIN helper
-       pin_helper /usr/bin/bluepin;
+       pin_helper /bin/bluepin;
 
        # D-Bus PIN helper
        # dbus_pin_helper;
index e69de29..21cf44a 100644 (file)
@@ -0,0 +1,70 @@
+#
+# HCI daemon configuration file.
+#
+# $Id$
+#
+
+# HCId options
+options {
+       # Automatically initialize new devices
+       autoinit yes;
+
+       # Security Manager mode
+       #   none - Security manager disabled
+       #   auto - Use local PIN for incoming connections
+       #   user - Always ask user for a PIN
+       #
+       security auto;
+
+       # Pairing mode
+       #   none  - Pairing disabled
+       #   multi - Allow pairing with already paired devices
+       #   once  - Pair once and deny successive attempts
+       pairing multi;
+
+       # PIN helper
+       # pin_helper /bin/bluepin;
+
+       # D-Bus PIN helper
+       # dbus_pin_helper;
+}
+
+# Default settings for HCI devices
+device {
+       # Local device name
+       #   %d - device id
+       #   %h - host name
+       name "%h";
+
+       # Local device class
+       class 0x820100;
+
+       # Default packet type
+       #pkt_type DH1,DM1,HV1;
+
+       # Inquiry and Page scan
+       iscan enable; pscan enable;
+
+       # Default link mode
+       #   none   - no specific policy 
+       #   accept - always accept incoming connections
+       #   master - become master on incoming connections,
+       #            deny role switch on outgoing connections
+       #
+       lm accept,master;
+
+       # Default link policy
+       #   none    - no specific policy
+       #   rswitch - allow role switch
+       #   hold    - allow hold mode
+       #   sniff   - allow sniff mode
+       #   park    - allow park mode
+       #
+       #lp hold,sniff;
+       #
+       lp rswitch,hold,sniff,park;
+
+       # Authentication and Encryption
+       auth enable;
+       encrypt enable;
+}
diff --git a/packages/bluez-utils/files/ramses/bluetooth.default b/packages/bluez-utils/files/ramses/bluetooth.default
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/packages/bluez-utils/files/ramses/hcid.conf b/packages/bluez-utils/files/ramses/hcid.conf
new file mode 100644 (file)
index 0000000..e69de29
index 2f80d09..d617ac8 100644 (file)
@@ -28,6 +28,7 @@
 #   vmalloc.patch
 #   usb-sl811.patch
 #   orinoco013e.patch
+#   bluetooth.patch
 #   ramses.patch
 #   ramses-ac97.patch
 #   ramses-keyb.patch
 # Patch managed by http://www.holgerschurig.de/patcher.html
 #
 
+--- linux-2.4.21/CREDITS~bluetooth
++++ linux-2.4.21/CREDITS
+@@ -1348,7 +1348,11 @@
+ N: Marcel Holtmann
+ E: marcel@holtmann.org
+ W: http://www.holtmann.org
+-D: Author of the Linux Bluetooth Subsystem PC Card drivers
++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
+@@ -2624,6 +2628,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Ă¡
+--- linux-2.4.21/Documentation/Configure.help~bluetooth
++++ linux-2.4.21/Documentation/Configure.help
+@@ -14071,6 +14071,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
+@@ -21670,21 +21679,21 @@
+   Linux Bluetooth subsystem consist of several layers:
+                BlueZ Core (HCI device and connection manager, scheduler)
+-               HCI Device drivers (interface to the hardware)
+-               L2CAP Module (L2CAP protocol)
+-               SCO Module (SCO links)
+-               RFCOMM Module (RFCOMM protocol)
+-               BNEP Module (BNEP protocol)
++               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 BlueZ 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 BlueZ Core as module (bluez.o) say M here.
++  For more information, see <http://www.bluez.org/>.
+ L2CAP protocol support
+ CONFIG_BLUEZ_L2CAP
+@@ -21697,7 +21706,7 @@
+ SCO links support
+ CONFIG_BLUEZ_SCO
+-  SCO link provides voice transport over Bluetooth. SCO support is
++  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
+@@ -21705,7 +21714,7 @@
+ RFCOMM protocol support
+ CONFIG_BLUEZ_RFCOMM
+-  RFCOMM provides connection oriented stream transport. RFCOMM
++  RFCOMM provides connection oriented stream transport.  RFCOMM
+   support is required for Dialup Networking, OBEX and other Bluetooth
+   applications.
+@@ -21719,12 +21728,8 @@
+ BNEP protocol support
+ CONFIG_BLUEZ_BNEP
+   BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet
+-  emulation layer on top of Bluetooth. BNEP is required for Bluetooth
+-  PAN (Personal Area Network).
+-
+-  To use BNEP, you will need user-space utilities provided in the 
+-  BlueZ-PAN package.
+-  For more information, see <http://bluez.sourceforge.net>.
++  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).
+@@ -21737,6 +21742,24 @@
+ 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.
+@@ -21781,7 +21804,7 @@
+   kernel or say M to compile it as module (hci_usb.o).
+ HCI USB SCO (voice) support
+-CONFIG_BLUEZ_USB_SCO
++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
+@@ -21789,14 +21812,6 @@
+  
+   Say Y here to compile support for HCI SCO data.
+  
+-HCI USB zero packet support
+-CONFIG_BLUEZ_USB_ZERO_PACKET
+-  This option is provided only as a work around for buggy Bluetooth USB 
+-  devices. Do NOT enable it unless you know for sure that your device 
+-  requires zero packets.
+- 
+-  Most people should say N here.
+-
+ HCI VHCI Virtual HCI device driver
+ CONFIG_BLUEZ_HCIVHCI
+   Bluetooth Virtual HCI device driver.
+@@ -21805,6 +21820,16 @@
+   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.
+@@ -21824,9 +21849,6 @@
+      3Com Bluetooth Card (3CRWB6096)
+      HP Bluetooth Card
+-  The HCI BT3C driver uses external firmware loader program provided in
+-  the BlueFW package. For more information, see <http://bluez.sf.net>.
+-
+   Say Y here to compile support for HCI BT3C devices into the
+   kernel or say M to compile it as module (bt3c_cs.o).
+@@ -26815,6 +26837,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.
++
+ NatSemi SCx200 support
+ CONFIG_SCx200
+   This provides basic support for the National Semiconductor SCx200
 --- /dev/null
 +++ linux-2.4.21/Documentation/DocBook/librs.tmpl
 @@ -0,0 +1,287 @@
 +      </para>
 +  </chapter>
 +</book>
+--- linux-2.4.21/Documentation/devices.txt~bluetooth
++++ linux-2.4.21/Documentation/devices.txt
+@@ -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
+--- /dev/null
++++ linux-2.4.21/Documentation/firmware_class/README
+@@ -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.
+--- /dev/null
++++ linux-2.4.21/Documentation/firmware_class/firmware_sample_driver.c
+@@ -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");
+--- /dev/null
++++ linux-2.4.21/Documentation/firmware_class/hotplug-script
+@@ -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
++#
+--- linux-2.4.21/MAINTAINERS~bluetooth
++++ linux-2.4.21/MAINTAINERS
+@@ -302,16 +302,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 SUBSYSTEM (PC Card Drivers)
++BLUETOOTH RFCOMM LAYER
+ P:    Marcel Holtmann
+ M:    marcel@holtmann.org
+-W:    http://www.holtmann.org/linux/bluetooth/
++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
+ BONDING DRIVER
 --- linux-2.4.21/Makefile~linux-mkdep
 +++ linux-2.4.21/Makefile
 @@ -14,10 +14,11 @@
  
 --- /dev/null
 +++ linux-2.4.21/arch/arm/def-configs/ramses
-@@ -0,0 +1,1174 @@
+@@ -0,0 +1,1177 @@
 +#
 +# Automatically generated by make menuconfig: don't edit
 +#
 +# CONFIG_INPUT_MOUSEDEV is not set
 +# CONFIG_INPUT_JOYDEV is not set
 +CONFIG_INPUT_EVDEV=y
++CONFIG_INPUT_UINPUT=m
 +# CONFIG_INPUT_MX1TS is not set
 +
 +#
 +CONFIG_BLUEZ_L2CAP=m
 +CONFIG_BLUEZ_SCO=m
 +CONFIG_BLUEZ_RFCOMM=m
-+# CONFIG_BLUEZ_RFCOMM_TTY is not set
++CONFIG_BLUEZ_RFCOMM_TTY=y
 +CONFIG_BLUEZ_BNEP=m
 +CONFIG_BLUEZ_BNEP_MC_FILTER=y
-+# CONFIG_BLUEZ_BNEP_PROTO_FILTER is not set
++CONFIG_BLUEZ_BNEP_PROTO_FILTER=y
++CONFIG_BLUEZ_HIDP=m
 +
 +#
 +# Bluetooth device drivers
 +#
 +CONFIG_BLUEZ_HCIUSB=m
-+CONFIG_BLUEZ_USB_SCO=y
-+CONFIG_BLUEZ_USB_ZERO_PACKET=y
++CONFIG_BLUEZ_HCIUSB_SCO=y
 +CONFIG_BLUEZ_HCIUART=m
 +CONFIG_BLUEZ_HCIUART_H4=y
 +CONFIG_BLUEZ_HCIUART_BCSP=y
-+CONFIG_BLUEZ_HCIUART_BCSP_TXCRC=y
++# CONFIG_BLUEZ_HCIUART_BCSP_TXCRC is not set
++CONFIG_BLUEZ_HCIBFUSB=m
 +CONFIG_BLUEZ_HCIDTL1=m
 +CONFIG_BLUEZ_HCIBT3C=m
 +CONFIG_BLUEZ_HCIBLUECARD=m
 +CONFIG_BLUEZ_HCIBTUART=m
-+# CONFIG_BLUEZ_HCIVHCI is not set
++CONFIG_BLUEZ_HCIVHCI=m
 +
 +#
 +# Kernel hacking
 +CONFIG_ZLIB_INFLATE=y
 +CONFIG_ZLIB_DEFLATE=y
 +# CONFIG_REED_SOLOMON is not set
++CONFIG_FW_LOADER=m
 --- linux-2.4.21/arch/arm/mach-pxa/Makefile~pm
 +++ linux-2.4.21/arch/arm/mach-pxa/Makefile
 @@ -14,8 +14,11 @@
 +#endif
 +#define USE_UCB
 +//#define PFI_LED
-+//#define PFI_TURNOFF
++#define PFI_TURNOFF
 +
 +#include <asm/types.h>
 +#include <asm/setup.h>
                 pstr = pxa_usb_kmalloc_string_descriptor( "PXA USB NIC" );
                 if ( pstr ) {
                          pxa_usb_set_string_descriptor( 1, pstr );
---- linux-2.4.21/drivers/char/Config.in~i2c-ds1337
-+++ linux-2.4.21/drivers/char/Config.in
-@@ -164,6 +164,7 @@
- if [ "$CONFIG_I2C" != "n" ]; then
-     dep_tristate '  DS1307 RTC' CONFIG_I2C_DS1307 $CONFIG_I2C
-+    dep_tristate '  DS1337 RTC' CONFIG_I2C_DS1337 $CONFIG_I2C
+--- linux-2.4.21/drivers/bluetooth/Config.in~bluetooth
++++ linux-2.4.21/drivers/bluetooth/Config.in
+@@ -7,8 +7,7 @@
+ dep_tristate 'HCI USB driver' CONFIG_BLUEZ_HCIUSB $CONFIG_BLUEZ $CONFIG_USB
+ if [ "$CONFIG_BLUEZ_HCIUSB" != "n" ]; then
+-   bool '  SCO (voice) support'  CONFIG_BLUEZ_USB_SCO
+-   bool '  USB zero packet support'  CONFIG_BLUEZ_USB_ZERO_PACKET
++   bool '  SCO (voice) support'  CONFIG_BLUEZ_HCIUSB_SCO
  fi
  
- source drivers/l3/Config.in
---- linux-2.4.21/drivers/char/Makefile~i2c-ds1337
-+++ linux-2.4.21/drivers/char/Makefile
-@@ -21,10 +21,11 @@
- # All of the (potential) objects that export symbols.
- # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'.
--export-objs     :=    busmouse.o console.o keyboard.o sysrq.o \
-+export-objs     :=    vt.o busmouse.o console.o keyboard.o sysrq.o \
-                       misc.o pty.o random.o selection.o serial.o \
-                       sonypi.o tty_io.o tty_ioctl.o generic_serial.o \
--                      au1000_gpio.o hp_psaux.o nvram.o scx200.o
-+                      au1000_gpio.o hp_psaux.o nvram.o scx200.o \
-+                      input_keyb.o
- mod-subdirs   :=      joystick ftape drm drm-4.0 pcmcia
-@@ -129,6 +130,11 @@
-   ifeq ($(CONFIG_SA1100_CERF_CPLD),y)
-     KEYBD    += cerf_keyb.o
-   endif
-+  ifeq ($(CONFIG_ARCH_RAMSES),y)
-+    KEYMAP   = german.o
-+    KEYBD    += input_keyb.o
-+    obj-m    += sysctl.o
-+  endif
-   ifeq ($(CONFIG_ARCH_FORTUNET),y)
-     KEYMAP   := defkeymap.o
-   endif
-@@ -337,6 +343,7 @@
- # I2C char devices
- obj-$(CONFIG_I2C_DS1307) += ds1307.o
-+obj-$(CONFIG_I2C_DS1337) += ds1337.o
- subdir-$(CONFIG_MWAVE) += mwave
- ifeq ($(CONFIG_MWAVE),y)
-@@ -373,3 +380,6 @@
+ dep_tristate 'HCI UART driver' CONFIG_BLUEZ_HCIUART $CONFIG_BLUEZ
+@@ -18,6 +17,8 @@
+    dep_bool '  Transmit CRC with every BCSP packet' CONFIG_BLUEZ_HCIUART_BCSP_TXCRC $CONFIG_BLUEZ_HCIUART_BCSP
+ fi
  
- qtronixmap.c: qtronixmap.map
-       set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@
++dep_tristate 'HCI BlueFRITZ! USB driver' CONFIG_BLUEZ_HCIBFUSB $CONFIG_BLUEZ $CONFIG_USB
 +
-+german.c: german.map
-+      set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@
---- linux-2.4.21/drivers/char/console.c~keyb-module
-+++ linux-2.4.21/drivers/char/console.c
-@@ -150,7 +150,7 @@
- static int con_open(struct tty_struct *, struct file *);
- static void vc_init(unsigned int console, unsigned int rows,
-                   unsigned int cols, int do_clear);
--static void blank_screen(unsigned long dummy);
-+//static void blank_screen(unsigned long dummy);
- static void gotoxy(int currcons, int new_x, int new_y);
- static void save_cur(int currcons);
- static void reset_terminal(int currcons, int do_clear);
-@@ -158,7 +158,7 @@
- static void set_vesa_blanking(unsigned long arg);
- static void set_cursor(int currcons);
- static void hide_cursor(int currcons);
--static void unblank_screen_t(unsigned long dummy);
-+//static void unblank_screen_t(unsigned long dummy);
- static void console_callback(void *ignored);
- static int printable;         /* Is console ready for printing? */
-@@ -167,7 +167,7 @@
- int console_blanked;
- static int vesa_blank_mode; /* 0:none 1:suspendV 2:suspendH 3:powerdown */
--static int blankinterval = 10*60*HZ;
-+//static int blankinterval = 10*60*HZ;
- static int vesa_off_interval;
- static struct tq_struct console_callback_tq = {
-@@ -209,9 +209,9 @@
-  * Hook so that the power management routines can (un)blank
-  * the console on our behalf.
-  */
--int (*console_blank_hook)(int);
-+//int (*console_blank_hook)(int);
--static struct timer_list console_timer;
-+//static struct timer_list console_timer;
- /*
-  *    Low-Level Functions
-@@ -543,7 +543,7 @@
- static void set_cursor(int currcons)
- {
--    if (!IS_FG || console_blanked || vcmode == KD_GRAPHICS)
-+    if (!IS_FG || vcmode == KD_GRAPHICS)
-       return;
-     if (deccm) {
-       if (currcons == sel_cons)
-@@ -1287,7 +1287,7 @@
-                       update_attr(currcons);
-                       break;
-               case 9: /* set blanking interval */
--                      blankinterval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ;
-+                      //blankinterval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ;
-                       poke_blanked_console();
-                       break;
-               case 10: /* set bell frequency in Hz */
-@@ -2575,11 +2575,11 @@
-       if (tty_register_driver(&console_driver))
-               panic("Couldn't register console driver\n");
--      init_timer(&console_timer);
--      console_timer.function = blank_screen;
--      if (blankinterval) {
--              mod_timer(&console_timer, jiffies + blankinterval);
--      }
-+      //init_timer(&console_timer);
-+      //console_timer.function = blank_screen;
-+      //if (blankinterval) {
-+      //      mod_timer(&console_timer, jiffies + blankinterval);
-+      //}
-       /*
-        * kmalloc is not running yet - we use the bootmem allocator.
-@@ -2744,11 +2744,12 @@
-  */
- static void vesa_powerdown_screen(unsigned long dummy)
- {
--      console_timer.function = unblank_screen_t;
-+      //console_timer.function = unblank_screen_t;
-       vesa_powerdown();
- }
-+#if 0
- static void timer_do_blank_screen(int entering_gfx, int from_timer_handler)
- {
-       int currcons = fg_console;
-@@ -2797,12 +2798,14 @@
-       if (vesa_blank_mode)
-               sw->con_blank(vc_cons[currcons].d, vesa_blank_mode + 1);
- }
-+#endif
- void do_blank_screen(int entering_gfx)
- {
--      timer_do_blank_screen(entering_gfx, 0);
-+      //timer_do_blank_screen(entering_gfx, 0);
- }
-+#if 0
- /*
-  * This is a timer handler
-  */
-@@ -2810,12 +2813,14 @@
- {
-       unblank_screen();
- }
-+#endif
- /*
-  * Called by timer as well as from vt_console_driver
-  */
- void unblank_screen(void)
- {
-+#if 0
-       int currcons;
-       if (!console_blanked)
-@@ -2842,6 +2847,7 @@
-               /* Low-level driver cannot restore -> do it ourselves */
-               update_screen(fg_console);
-       set_cursor(fg_console);
-+#endif
- }
+ dep_tristate 'HCI DTL1 (PC Card) driver' CONFIG_BLUEZ_HCIDTL1 $CONFIG_PCMCIA $CONFIG_BLUEZ
  
- /*
-@@ -2849,11 +2855,12 @@
-  */
- static void blank_screen(unsigned long dummy)
- {
--      timer_do_blank_screen(0, 1);
-+      //timer_do_blank_screen(0, 1);
- }
- void poke_blanked_console(void)
- {
-+#if 0
-       del_timer(&console_timer);
-       if (!vt_cons[fg_console] || vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
-               return;
-@@ -2863,6 +2870,7 @@
-       } else if (blankinterval) {
-               mod_timer(&console_timer, jiffies + blankinterval);
-       }
-+#endif
- }
+ dep_tristate 'HCI BT3C (PC Card) driver' CONFIG_BLUEZ_HCIBT3C $CONFIG_PCMCIA $CONFIG_BLUEZ
+--- linux-2.4.21/drivers/bluetooth/Makefile~bluetooth
++++ linux-2.4.21/drivers/bluetooth/Makefile
+@@ -14,6 +14,8 @@
+ uart-$(CONFIG_BLUEZ_HCIUART_H4)               += hci_h4.o
+ uart-$(CONFIG_BLUEZ_HCIUART_BCSP)     += hci_bcsp.o
  
- /*
-@@ -3088,7 +3096,7 @@
-               unblank_screen();
-               break;
-       case PM_SUSPEND:
--              do_blank_screen(0);
-+              //do_blank_screen(0);
-               break;
-       }
-       return 0;
-@@ -3106,7 +3114,8 @@
- EXPORT_SYMBOL(video_scan_lines);
- EXPORT_SYMBOL(vc_resize);
- EXPORT_SYMBOL(fg_console);
--EXPORT_SYMBOL(console_blank_hook);
-+//EXPORT_SYMBOL(console_blank_hook);
-+EXPORT_SYMBOL(console_driver);
- #ifdef CONFIG_VT
- EXPORT_SYMBOL(vt_cons);
- #endif
++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
 --- /dev/null
-+++ linux-2.4.21/drivers/char/ds1337.c
-@@ -0,0 +1,545 @@
++++ linux-2.4.21/drivers/bluetooth/Makefile.lib
+@@ -0,0 +1,2 @@
++obj-$(CONFIG_BLUEZ_HCIBFUSB)  += firmware_class.o
++obj-$(CONFIG_BLUEZ_HCIBT3C)   += firmware_class.o
+--- /dev/null
++++ linux-2.4.21/drivers/bluetooth/bfusb.c
+@@ -0,0 +1,782 @@
 +/*
-+ * ds1337.c
 + *
-+ * Device driver for Dallas Semiconductor's Real Time Controller DS1337.
++ *  AVM BlueFRITZ! USB driver
 + *
-+ * Copyright (C) 2003 M&N Logistik-Lösungen Online GmbH
++ *  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 version 2 as
-+ * published by the Free Software Foundation.
 + *
-+ * Documentation for this Chip: http://pdfserv.maxim-ic.com/arpdf/DS1337.pdf
++ *  This program is free software; you can redistribute it and/or modify
++ *  it under the terms of the 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/version.h>
 +
 +#include <linux/kernel.h>
-+#include <linux/poll.h>
-+#include <linux/i2c.h>
-+#include <linux/slab.h>
 +#include <linux/init.h>
-+#include <linux/rtc.h>
-+#include <linux/string.h>
-+#include <linux/miscdevice.h>
-+#include <linux/proc_fs.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/skbuff.h>
 +
-+#include "ds1337.h"
++#include <linux/firmware.h>
++#include <linux/usb.h>
 +
-+//#define DEBUG 1
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/hci_core.h>
 +
-+#if DEBUG
-+static unsigned int rtc_debug = DEBUG;
-+#else
-+#define rtc_debug 0           /* gcc will remove all the debug code for us */
++#ifndef CONFIG_BLUEZ_HCIBFUSB_DEBUG
++#undef  BT_DBG
++#define BT_DBG(D...)
 +#endif
 +
-+static unsigned short slave_address = DS1337_I2C_SLAVE_ADDR;
-+struct i2c_driver ds1337_driver;
-+struct i2c_client *ds1337_i2c_client = 0;
-+static spinlock_t ds1337_rtc_lock = SPIN_LOCK_UNLOCKED;
++#define VERSION "1.1"
 +
-+static unsigned short ignore[] = { I2C_CLIENT_END };
-+static unsigned short normal_addr[] = { DS1337_I2C_SLAVE_ADDR, I2C_CLIENT_END };
++static struct usb_device_id bfusb_table[] = {
++      /* AVM BlueFRITZ! USB */
++      { USB_DEVICE(0x057c, 0x2200) },
 +
-+static int ds1337_rtc_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
-+static int ds1337_rtc_noop(struct inode *inode, struct file *file);
++      { }     /* Terminating entry */
++};
 +
-+static int ds1337_probe(struct i2c_adapter *adap);
-+static int ds1337_detach(struct i2c_client *client);
-+static int ds1337_command(struct i2c_client *client, unsigned int cmd, void *arg);
++MODULE_DEVICE_TABLE(usb, bfusb_table);
 +
 +
-+static struct i2c_client_address_data addr_data = {
-+    .normal_i2c =     normal_addr,
-+    .normal_i2c_range =       ignore,
-+    .probe =          ignore,
-+    .probe_range =    ignore,
-+    .ignore =         ignore,
-+    .ignore_range =   ignore,
-+    .force =          ignore,
-+};
++#define BFUSB_MAX_BLOCK_SIZE  256
 +
-+static struct file_operations rtc_fops = {
-+    .owner =          THIS_MODULE,
-+    .ioctl =          ds1337_rtc_ioctl,
-+    .open =           ds1337_rtc_noop,
-+    .release =                ds1337_rtc_noop,
-+};
++#define BFUSB_BLOCK_TIMEOUT   (HZ * 3)
 +
-+static struct miscdevice ds1337_rtc_miscdev = {
-+    RTC_MINOR,
-+    "rtc",
-+    &rtc_fops
-+};
++#define BFUSB_TX_PROCESS      1
++#define BFUSB_TX_WAKEUP               2
 +
++#define BFUSB_MAX_BULK_TX     1
++#define BFUSB_MAX_BULK_RX     1
 +
-+struct i2c_driver ds1337_driver = {
-+    .name =           "DS1337",
-+    .id =             I2C_DRIVERID_DS1337,
-+    .flags =          I2C_DF_NOTIFY,
-+    .attach_adapter = ds1337_probe,
-+    .detach_client =  ds1337_detach,
-+    .command =                ds1337_command
++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;
 +};
 +
-+#define DAT(x) ((unsigned int)((x)->data))    /* keep the control register info */
++struct bfusb_scb {
++      struct urb *urb;
++};
 +
++static void bfusb_tx_complete(struct urb *urb);
++static void bfusb_rx_complete(struct urb *urb);
 +
-+static int ds1337_readram(char *buf, int len)
++static struct urb *bfusb_get_completed(struct bfusb *bfusb)
 +{
-+    unsigned long flags;
-+    unsigned char ad[1] = { 0 };
-+    int ret;
-+    struct i2c_msg msgs[2] = {
-+      {ds1337_i2c_client->addr, 0, 1, ad},
-+      {ds1337_i2c_client->addr, I2C_M_RD, len, buf}
-+    };
++      struct sk_buff *skb;
++      struct urb *urb = NULL;
 +
-+    spin_lock_irqsave(&ds1337_rtc_lock, flags);
-+    ret = i2c_transfer(ds1337_i2c_client->adapter, msgs, 2);
-+    spin_unlock_irqrestore(&ds1337_rtc_lock, flags);
++      BT_DBG("bfusb %p", bfusb);
 +
-+    return ret;
-+}
++      skb = skb_dequeue(&bfusb->completed_q);
++      if (skb) {
++              urb = ((struct bfusb_scb *) skb->cb)->urb;
++              kfree_skb(skb);
++      }
 +
++      return urb;
++}
 +
-+static void ds1337_setreg(struct i2c_client *c, unsigned char reg, unsigned char val)
++static inline void bfusb_unlink_urbs(struct bfusb *bfusb)
 +{
-+    unsigned char buf[2];
-+    buf[0] = reg;
-+    buf[1] = val;
-+    i2c_master_send(c, (char *) buf, 2);
++      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 ds1337_attach(struct i2c_adapter *adap, int addr,
-+                       unsigned short flags, int kind)
++
++static int bfusb_send_bulk(struct bfusb *bfusb, struct sk_buff *skb)
 +{
-+    struct i2c_client *c;
-+    unsigned char buf[DS1337_MEM_SIZE], ad[1] = { 7 };
-+    struct i2c_msg msgs[2] = {
-+      {addr, 0, 1, ad},
-+      {addr, I2C_M_RD, 1, buf}
-+    };
-+    int ret;
++      struct bfusb_scb *scb = (void *) skb->cb;
++      struct urb *urb = bfusb_get_completed(bfusb);
++      int err, pipe;
 +
-+    if (rtc_debug>1)
-+      printk("%s(adap,%d,%d,%d)\n", __FUNCTION__, addr, flags, kind);
++      BT_DBG("bfusb %p skb %p len %d", bfusb, skb, skb->len);
 +
-+    c = (struct i2c_client *) kmalloc(sizeof(*c), GFP_KERNEL);
-+    if (!c)
-+      return -ENOMEM;
++      if (!urb && !(urb = usb_alloc_urb(0)))
++              return -ENOMEM;
 +
-+    strcpy(c->name, "DS1337");
-+    c->id = ds1337_driver.id;
-+    c->flags = 0;
-+    c->addr = addr;
-+    c->adapter = adap;
-+    c->driver = &ds1337_driver;
-+    c->data = NULL;
++      pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep);
 +
-+    ret = i2c_transfer(c->adapter, msgs, 2);
++      FILL_BULK_URB(urb, bfusb->udev, pipe, skb->data, skb->len,
++                      bfusb_tx_complete, skb);
 +
-+    if (ret == 2) {
-+      DAT(c) = buf[0];
-+    } else
-+      printk("ds1337_attach(): i2c_transfer() returned %d.\n", ret);
++      urb->transfer_flags = USB_QUEUE_BULK;
 +
-+    ds1337_i2c_client = c;
++      scb->urb = urb;
 +
-+    ds1337_readram(buf, DS1337_MEM_SIZE);
++      skb_queue_tail(&bfusb->pending_q, skb);
 +
-+                                              // set 24 hour mode
-+    ds1337_setreg(c, 0x2, buf[2] | DS1337_HOUR24);
-+                                              // INTCN sets INTB to alarm2 (disables SQW)
-+    ds1337_setreg(c, 0x5, buf[5] & 0x7f);     // clear century
-+    ds1337_setreg(c, 0x7, 0x00);              // clear Alarm 1 seconds
-+    ds1337_setreg(c, 0x8, 0x00);              // clear Alarm 1 minutes
-+    ds1337_setreg(c, 0x9, 0x40);              // clear Alarm 1 hours, 24 hour on
-+    ds1337_setreg(c, 0xA, 0x00);              // clear Alarm 1 date
-+    ds1337_setreg(c, 0xB, 0x00);              // clear Alarm 2 minutes
-+    ds1337_setreg(c, 0xC, 0x40);              // clear Alarm 2 hours, 24 hour on
-+    ds1337_setreg(c, 0xD, 0x00);              // clear Alarm 2 date
-+    ds1337_setreg(c, 0xe, 4);                 // nEOSC enabled
-+    ds1337_setreg(c, 0xf, 0);                 // clear OSF, A2F, A1F
++      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 i2c_attach_client(c);
++      return err;
 +}
 +
-+
-+static int ds1337_probe(struct i2c_adapter *adap)
++static void bfusb_tx_wakeup(struct bfusb *bfusb)
 +{
-+    if (rtc_debug>1)
-+      printk("%s()\n", __FUNCTION__);
++      struct sk_buff *skb;
 +
-+    return i2c_probe(adap, &addr_data, ds1337_attach);
-+}
++      BT_DBG("bfusb %p", bfusb);
 +
++      if (test_and_set_bit(BFUSB_TX_PROCESS, &bfusb->state)) {
++              set_bit(BFUSB_TX_WAKEUP, &bfusb->state);
++              return;
++      }
 +
-+static int ds1337_detach(struct i2c_client *client)
-+{
-+    if (rtc_debug>1)
-+      printk("%s()\n", __FUNCTION__);
++      do {
++              clear_bit(BFUSB_TX_WAKEUP, &bfusb->state);
 +
-+    i2c_detach_client(client);
++              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;
++                      }
++              }
 +
-+    return 0;
-+}
++      } while (test_bit(BFUSB_TX_WAKEUP, &bfusb->state));
 +
++      clear_bit(BFUSB_TX_PROCESS, &bfusb->state);
++}
 +
-+static void ds1337_convert_to_time(struct rtc_time *dt, char *buf)
++static void bfusb_tx_complete(struct urb *urb)
 +{
-+    if (rtc_debug>1)
-+      printk("%s()\n", __FUNCTION__);
++      struct sk_buff *skb = (struct sk_buff *) urb->context;
++      struct bfusb *bfusb = (struct bfusb *) skb->dev;
 +
-+    dt->tm_sec = BCD_TO_BIN(buf[0]);
-+    dt->tm_min = BCD_TO_BIN(buf[1]);
-+    dt->tm_hour = DS1337_HOURS_24(buf[2]);
++      BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len);
 +
-+    dt->tm_mday = BCD_TO_BIN(buf[4]);
-+    /* dt->tm_mon is zero-based */
-+    dt->tm_mon = BCD_TO_BIN(buf[5]) - 1;
-+    /* year is 1900 + dt->tm_year */
-+    dt->tm_year = BCD_TO_BIN(buf[6]) + 100;
++      atomic_dec(&bfusb->pending_tx);
 +
-+    if (rtc_debug > 2) {
-+      printk("ds1337_get_datetime: year = %d\n", dt->tm_year);
-+      printk("ds1337_get_datetime: mon  = %d\n", dt->tm_mon);
-+      printk("ds1337_get_datetime: mday = %d\n", dt->tm_mday);
-+      printk("ds1337_get_datetime: hour = %d\n", dt->tm_hour);
-+      printk("ds1337_get_datetime: min  = %d\n", dt->tm_min);
-+      printk("ds1337_get_datetime: sec  = %d\n", dt->tm_sec);
-+    }
-+}
++      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++;
 +
-+static int ds1337_get_datetime(struct i2c_client *client,
-+                             struct rtc_time *dt)
-+{
-+    unsigned char buf[7], addr[1] = { 0 };
-+    struct i2c_msg msgs[2] = {
-+      {client->addr, 0, 1, addr},
-+      {client->addr, I2C_M_RD, 7, buf}
-+    };
-+    int ret = -EIO;
++      read_lock(&bfusb->lock);
 +
-+    if (rtc_debug)
-+      printk("%s()\n", __FUNCTION__);
++      skb_unlink(skb);
++      skb_queue_tail(&bfusb->completed_q, skb);
 +
-+    memset(buf, 0, sizeof(buf));
++      bfusb_tx_wakeup(bfusb);
 +
-+    ret = i2c_transfer(client->adapter, msgs, 2);
++      read_unlock(&bfusb->lock);
++}
 +
-+    if (ret == 2) {
-+      ds1337_convert_to_time(dt, buf);
-+      ret = 0;
-+    } else
-+      printk("ds1337_get_datetime(), i2c_transfer() returned %d\n", ret);
 +
-+    return ret;
-+}
++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);
 +
-+static int ds1337_set_datetime(struct i2c_client *client,
-+                             struct rtc_time *dt, int datetoo)
-+{
-+    unsigned char buf[8];
-+    int ret, len = 4;
++      if (!urb && !(urb = usb_alloc_urb(0)))
++              return -ENOMEM;
 +
-+    if (rtc_debug)
-+      printk("%s()\n", __FUNCTION__);
++      if (!(skb = bluez_skb_alloc(size, GFP_ATOMIC))) {
++              usb_free_urb(urb);
++              return -ENOMEM;
++      }
 +
-+    if (rtc_debug > 2) {
-+      printk("ds1337_set_datetime: tm_year = %d\n", dt->tm_year);
-+      printk("ds1337_set_datetime: tm_mon  = %d\n", dt->tm_mon);
-+      printk("ds1337_set_datetime: tm_mday = %d\n", dt->tm_mday);
-+      printk("ds1337_set_datetime: tm_hour = %d\n", dt->tm_hour);
-+      printk("ds1337_set_datetime: tm_min  = %d\n", dt->tm_min);
-+      printk("ds1337_set_datetime: tm_sec  = %d\n", dt->tm_sec);
-+    }
++      skb->dev = (void *) bfusb;
 +
-+    buf[0] = 0;                       /* register address on DS1337 */
-+    buf[1] = (BIN_TO_BCD(dt->tm_sec));
-+    buf[2] = (BIN_TO_BCD(dt->tm_min));
-+    buf[3] = (BIN_TO_BCD(dt->tm_hour)) | DS1337_HOUR24;
++      scb = (struct bfusb_scb *) skb->cb;
++      scb->urb = urb;
 +
-+    if (datetoo) {
-+      len = 8;
-+      /* we skip buf[4] as we don't use day-of-week. */
-+      buf[5] = (BIN_TO_BCD(dt->tm_mday));
-+      buf[6] = (BIN_TO_BCD(dt->tm_mon + 1));
-+      /* The year only ranges from 0-99, we are being passed an offset from 1900,
-+       * and the chip calulates leap years based on 2000, thus we adjust by 100.
-+       */
-+      buf[7] = (BIN_TO_BCD(dt->tm_year - 100));
-+    }
-+    ret = i2c_master_send(client, (char *) buf, len);
-+    if (ret == len)
-+      ret = 0;
-+    else
-+      printk("ds1337_set_datetime(), i2c_master_send() returned %d\n",
-+             ret);
++      pipe = usb_rcvbulkpipe(bfusb->udev, bfusb->bulk_in_ep);
 +
++      FILL_BULK_URB(urb, bfusb->udev, pipe, skb->data, size,
++                      bfusb_rx_complete, skb);
 +
-+    return ret;
-+}
++      urb->transfer_flags = USB_QUEUE_BULK;
 +
++      skb_queue_tail(&bfusb->pending_q, skb);
 +
-+#if 0
-+static int ds1337_get_ctrl(struct i2c_client *client, unsigned char *ctrl)
++      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)
 +{
-+    *ctrl = DAT(client);
++      BT_DBG("bfusb %p hdr 0x%02x data %p len %d", bfusb, hdr, data, len);
 +
-+    if (rtc_debug)
-+      printk("%s():%d\n", __FUNCTION__, *ctrl);
++      if (hdr & 0x10) {
++              BT_ERR("%s error in block", bfusb->hdev.name);
++              if (bfusb->reassembly)
++                      kfree_skb(bfusb->reassembly);
++              bfusb->reassembly = NULL;
++              return -EIO;
++      }
 +
-+    return 0;
-+}
++      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;
++              }
 +
-+static int ds1337_set_ctrl(struct i2c_client *client, unsigned char *cinfo)
++              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)
 +{
-+    unsigned char buf[2];
-+    int ret;
++      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;
 +
-+    if (rtc_debug)
-+      printk("%s(%d)\n", __FUNCTION__, *cinfo);
++      BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len);
 +
-+    buf[0] = 7;                       /* control register address on DS1337 */
-+    buf[1] = *cinfo;
-+    /* save the control reg info in the client data field so that get_ctrl
-+     * function doesn't have to do an I2C transfer to get it.
-+     */
-+    DAT(client) = buf[1];
++      read_lock(&bfusb->lock);
 +
-+    ret = i2c_master_send(client, (char *) buf, 2);
++      if (!test_bit(HCI_RUNNING, &bfusb->hdev.flags))
++              goto unlock;
 +
-+    return ret;
++      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);
 +}
-+#endif
 +
 +
-+static int ds1337_command(struct i2c_client *client, unsigned int cmd,
-+                        void *arg)
++static int bfusb_open(struct hci_dev *hdev)
 +{
-+    if (rtc_debug)
-+      printk("%s(client,,%u,arg)\n", __FUNCTION__, cmd);
++      struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
++      unsigned long flags;
++      int i, err;
 +
-+    switch (cmd) {
-+    case DS1337_GETDATETIME:
-+      return ds1337_get_datetime(client, arg);
++      BT_DBG("hdev %p bfusb %p", hdev, bfusb);
 +
-+    case DS1337_SETTIME:
-+      return ds1337_set_datetime(client, arg, 0);
++      if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
++              return 0;
 +
-+    case DS1337_SETDATETIME:
-+      return ds1337_set_datetime(client, arg, 1);
++      MOD_INC_USE_COUNT;
 +
-+    default:
-+      return -EINVAL;
-+    }
-+}
++      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;
++      }
 +
-+static int ds1337_rtc_noop(struct inode *inode, struct file *file)
++      write_unlock_irqrestore(&bfusb->lock, flags);
++
++      return err;
++}
++
++static int bfusb_flush(struct hci_dev *hdev)
 +{
-+    return 0;
++      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;
 +
-+static int ds1337_rtc_ioctl(struct inode *inode, struct file *file,
-+                          unsigned int cmd, unsigned long arg)
++      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)
 +{
-+    unsigned long flags;
-+    struct rtc_time wtime;
-+    int status = 0;
++      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;
 +
-+    if (rtc_debug)
-+      printk("%s()\n", __FUNCTION__);
++      BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, skb->pkt_type, skb->len);
 +
-+    switch (cmd) {
-+    default:
-+    case RTC_UIE_ON:          // mask ints from RTC updates
-+    case RTC_UIE_OFF:
-+    case RTC_PIE_ON:          // allow periodic interrupts
-+    case RTC_PIE_OFF:
-+    case RTC_AIE_ON:          // mask alarm int enable bit
-+    case RTC_AIE_OFF:
-+    case RTC_ALM_SET:
-+      /*
-+       * This expects a struct rtc_time. Writing 0xff means
-+       * "don't care" or "match all". Only the tm_hour,
-+       * tm_min and tm_sec are used.
-+       */
-+    case RTC_ALM_READ:
-+      // get_rtc_alm_time(&wtime);
-+    case RTC_IRQP_READ:               // Read the periodic IRQ rate
-+    case RTC_IRQP_SET:                // Set periodic IRQ rate
-+    case RTC_EPOCH_READ:
-+      // return put_user (epoch, (unsigned long *)arg);
-+    case RTC_EPOCH_SET:
-+    case RTC_WKALM_SET:
-+    case RTC_WKALM_RD:
-+      status = -EINVAL;
-+      break;
++      if (!hdev) {
++              BT_ERR("Frame for unknown HCI device (hdev=NULL)");
++              return -ENODEV;
++      }
 +
-+    case RTC_RD_TIME:
-+      spin_lock_irqsave(&ds1337_rtc_lock, flags);
-+      ds1337_command(ds1337_i2c_client, DS1337_GETDATETIME, &wtime);
-+      spin_unlock_irqrestore(&ds1337_rtc_lock, flags);
++      if (!test_bit(HCI_RUNNING, &hdev->flags))
++              return -EBUSY;
 +
-+      if (copy_to_user((void *) arg, &wtime, sizeof(struct rtc_time)))
-+          status = -EFAULT;
-+      break;
++      bfusb = (struct bfusb *) hdev->driver_data;
 +
-+    case RTC_SET_TIME:
-+      if (!capable(CAP_SYS_TIME)) {
-+          status = -EACCES;
-+          break;
++      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;
 +      }
 +
-+      if (copy_from_user
-+          (&wtime, (struct rtc_time *) arg, sizeof(struct rtc_time))) {
-+          status = -EFAULT;
-+          break;
++      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;
 +      }
 +
-+      spin_lock_irqsave(&ds1337_rtc_lock, flags);
-+      ds1337_command(ds1337_i2c_client, DS1337_SETDATETIME, &wtime);
-+      spin_unlock_irqrestore(&ds1337_rtc_lock, flags);
-+      break;
-+    }
++      /* 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);
++      }
 +
-+    return status;
++      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);
 +
-+static char *ds1337_mon2str(unsigned int mon)
++      kfree(bfusb);
++}
++
++static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
 +{
-+    char *mon2str[12] = {
-+      "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-+      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-+    };
-+    if (mon > 11)
-+      return "error";
-+    else
-+      return mon2str[mon];
++      return -ENOIOCTLCMD;
 +}
 +
 +
-+static int ds1337_rtc_proc_output(char *buf)
++static int bfusb_load_firmware(struct bfusb *bfusb, unsigned char *firmware, int count)
 +{
-+#define CHECK(ctrl,bit) ((ctrl & bit) ? "yes" : "no")
++      unsigned char *buf;
++      int err, pipe, len, size, sent = 0;
 +
-+    unsigned char ram[DS1337_MEM_SIZE];
-+    int ret;
++      BT_DBG("bfusb %p udev %p firmware %p count %d", bfusb, bfusb->udev, firmware, count);
 +
-+    char *p = buf;
++      BT_INFO("BlueFRITZ! USB loading firmware");
 +
-+    ret = ds1337_readram(ram, DS1337_MEM_SIZE);
-+    if (ret > 0) {
-+#ifdef DEBUG
-+      int i;
-+      char text[9];
-+#endif
-+      struct rtc_time dt;
++      if (usb_set_configuration(bfusb->udev, 1) < 0) {
++              BT_ERR("Can't change to loading configuration");
++              return -EBUSY;
++      }
 +
-+      p += sprintf(p, "DS1337 (i2c Serial Real Time Clock)\n");
++      buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_ATOMIC);
++      if (!buf) {
++              BT_ERR("Can't allocate memory chunk for firmware");
++              return -ENOMEM;
++      }
 +
-+      ds1337_convert_to_time(&dt, ram);
-+      p += sprintf(p, "Date/Time: %02d-%s-%04d %02d:%02d:%02d\n",
-+                   dt.tm_mday, ds1337_mon2str(dt.tm_mon),
-+                   dt.tm_year + 1900, dt.tm_hour, dt.tm_min, dt.tm_sec);
++      pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep);
 +
-+#ifdef DEBUG
-+      p += sprintf(p, "RAM dump:\n");
-+      text[8] = '\0';
-+      for (i = 0; i < DS1337_MEM_SIZE; i++) {
-+          if ((i % 8) == 0)
-+              p += sprintf(p, "%02X: ", i);
-+          p += sprintf(p, "%02X ", ram[i]);
++      while (count) {
++              size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE + 3);
 +
-+          if ((ram[i] < 32) || (ram[i] > 126))
-+              ram[i] = '.';
-+          text[i % 8] = ram[i];
-+          if ((i % 8) == 7)
-+              p += sprintf(p, "%s\n", text);
++              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;
 +      }
-+      p += sprintf(p, "\n");
-+#endif
-+    } else {
-+      p += sprintf(p, "Failed to read RTC memory!\n");
-+    }
 +
-+    return p - buf;
-+}
++      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;
++      }
 +
-+static int ds1337_rtc_read_proc(char *page, char **start, off_t off,
-+                              int count, int *eof, void *data)
-+{
-+    int len = ds1337_rtc_proc_output(page);
++      BT_INFO("BlueFRITZ! USB device ready");
 +
-+    if (len <= off + count)
-+      *eof = 1;
-+    *start = page + off;
-+    len -= off;
-+    if (len > count)
-+      len = count;
-+    if (len < 0)
-+      len = 0;
-+    return len;
-+}
++      kfree(buf);
++      return 0;
 +
++error:
++      kfree(buf);
 +
-+static __init int ds1337_init(void)
++      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)
 +{
-+    int retval = 0;
++      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;
 +
-+    if (rtc_debug>1)
-+      printk("%s()\n", __FUNCTION__);
++      BT_DBG("udev %p ifnum %d id %p", udev, ifnum, id);
 +
-+    if (slave_address != 0xffff) {
-+      normal_addr[0] = slave_address;
-+    }
++      /* Check number of endpoints */
++      iface = &udev->actconfig->interface[0];
++      iface_desc = &iface->altsetting[0];
 +
-+    if (normal_addr[0] == 0xffff) {
-+      printk(KERN_ERR
-+             "I2C: Invalid slave address for DS1337 RTC (%#x)\n",
-+             normal_addr[0]);
-+      return -EINVAL;
-+    }
++      if (iface_desc->bNumEndpoints < 2)
++              return NULL;
 +
-+    retval = i2c_add_driver(&ds1337_driver);
++      bulk_out_ep = &iface_desc->endpoint[0];
++      bulk_in_ep  = &iface_desc->endpoint[1];
 +
-+    if (retval == 0) {
-+      misc_register(&ds1337_rtc_miscdev);
-+      create_proc_read_entry(DS1337_PROC_NAME, 0, 0,
-+                             ds1337_rtc_read_proc, NULL);
-+      printk("I2C: DS1337 RTC driver loaded\n");
-+    }
-+    return retval;
-+}
++      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;
++      }
 +
-+static __exit void ds1337_exit(void)
++      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)
 +{
-+    if (rtc_debug>1)
-+      printk("%s()\n", __FUNCTION__);
++      struct bfusb *bfusb = (struct bfusb *) ptr;
++      struct hci_dev *hdev = &bfusb->hdev;
 +
-+    remove_proc_entry(DS1337_PROC_NAME, NULL);
-+    misc_deregister(&ds1337_rtc_miscdev);
-+    i2c_del_driver(&ds1337_driver);
++      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,
++};
 +
-+module_init(ds1337_init);
-+module_exit(ds1337_exit);
++static int __init bfusb_init(void)
++{
++      int err;
 +
-+MODULE_PARM(slave_address, "i");
-+MODULE_PARM_DESC(slave_address, "I2C slave address for DS1337 RTC");
++      BT_INFO("BlueFRITZ! USB driver ver %s", VERSION);
++      BT_INFO("Copyright (C) 2003 Marcel Holtmann <marcel@holtmann.org>");
 +
-+MODULE_AUTHOR("M&N Logistik-Lösungen Online GmbH");
-+MODULE_LICENSE("GPL");
---- /dev/null
-+++ linux-2.4.21/drivers/char/ds1337.h
-@@ -0,0 +1,43 @@
-+/*
-+ * ds1337.h
-+ *
-+ * Copyright (C) 2003 M&N Logistik-Lösungen Online GmbH
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License version 2 as
-+ * published by the Free Software Foundation.
-+ *
-+ */
-+#ifndef DS1337_H
-+#define DS1337_H
++      if ((err = usb_register(&bfusb_driver)) < 0)
++              BT_ERR("Failed to register BlueFRITZ! USB driver");
 +
-+#define DS1337_I2C_SLAVE_ADDR 0x68
-+//#define DS1337_RAM_ADDR_START       0x10
-+//#define DS1337_RAM_ADDR_END 0x10
-+#define DS1337_MEM_SIZE               0x10
++      return err;
++}
 +
-+#define DS1337_PROC_NAME      "driver/ds1337"
++static void __exit bfusb_cleanup(void)
++{
++      usb_deregister(&bfusb_driver);
++}
 +
-+struct rtc_mem {
-+      unsigned int    loc;
-+      unsigned int    nr;
-+      unsigned char   *data;
-+};
++module_init(bfusb_init);
++module_exit(bfusb_cleanup);
 +
-+#define DS1337_GETDATETIME    0
-+#define DS1337_SETTIME                1
-+#define DS1337_SETDATETIME    2
++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
++MODULE_DESCRIPTION("BlueFRITZ! USB driver ver " VERSION);
++MODULE_LICENSE("GPL");
+--- linux-2.4.21/drivers/bluetooth/bluecard_cs.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/bluecard_cs.c
+@@ -803,6 +803,9 @@
+       unsigned int iobase = info->link.io.BasePort1;
+       struct hci_dev *hdev = &(info->hdev);
++      if (info->link.state & DEV_CONFIG_PENDING)
++              return -ENODEV;
 +
-+#define DS1337_RATE_1HZ       0x00    /* Rate Select     1 Hz */
-+#define DS1337_RATE_4096HZ    0x01    /* Rate Select  4096 kHz */
-+#define DS1337_RATE_8192HZ    0x02    /* Rate Select  8192 kHz */
-+#define DS1337_RATE_32768HZ   0x03    /* Rate Select 32768 kHz */
+       bluecard_hci_close(hdev);
+       clear_bit(CARD_READY, &(info->hw_state));
+--- linux-2.4.21/drivers/bluetooth/bt3c_cs.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/bt3c_cs.c
+@@ -24,8 +24,6 @@
+ #include <linux/config.h>
+ #include <linux/module.h>
+-#define __KERNEL_SYSCALLS__
+-
+ #include <linux/kernel.h>
+ #include <linux/kmod.h>
+ #include <linux/init.h>
+@@ -48,6 +46,8 @@
+ #include <asm/bitops.h>
+ #include <asm/io.h>
++#include <linux/firmware.h>
 +
-+#define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10)
-+#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10)
+ #include <pcmcia/version.h>
+ #include <pcmcia/cs_types.h>
+ #include <pcmcia/cs.h>
+@@ -485,78 +485,101 @@
+-/* ======================== User mode firmware loader ======================== */
++/* ======================== Card services HCI interaction ======================== */
+-#define FW_LOADER  "/sbin/bluefw"
+-static int errno;
++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;
+-static int bt3c_fw_loader_exec(void *dev)
+-{
+-      char *argv[] = { FW_LOADER, "pccard", dev, NULL };
+-      char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
+-      int err;
++      /* Reset */
+-      err = exec_usermodehelper(FW_LOADER, argv, envp);
+-      if (err)
+-              printk(KERN_WARNING "bt3c_cs: Failed to exec \"%s pccard %s\".\n", FW_LOADER, (char *)dev);
++      bt3c_io_write(iobase, 0x8040, 0x0404);
++      bt3c_io_write(iobase, 0x8040, 0x0400);
+-      return err;
+-}
++      udelay(1);
++      bt3c_io_write(iobase, 0x8040, 0x0404);
+-static int bt3c_firmware_load(bt3c_info_t *info)
+-{
+-      sigset_t tmpsig;
+-      char dev[16];
+-      pid_t pid;
+-      int result;
++      udelay(17);
+-      /* Check if root fs is mounted */
+-      if (!current->fs->root) {
+-              printk(KERN_WARNING "bt3c_cs: Root filesystem is not mounted.\n");
+-              return -EPERM;
+-      }
++      /* Load */
+-      sprintf(dev, "%04x", info->link.io.BasePort1);
++      while (count) {
++              if (ptr[0] != 'S') {
++                      printk(KERN_WARNING "bt3c_cs: Bad address in firmware.\n");
++                      err = -EFAULT;
++                      goto error;
++              }
+-      pid = kernel_thread(bt3c_fw_loader_exec, (void *)dev, 0);
+-      if (pid < 0) {
+-              printk(KERN_WARNING "bt3c_cs: Forking of kernel thread failed (errno=%d).\n", -pid);
+-              return pid;
+-      }
++              memset(b, 0, sizeof(b));
++              memcpy(b, ptr + 2, 2);
++              size = simple_strtol(b, NULL, 16);
+-      /* Block signals, everything but SIGKILL/SIGSTOP */
+-      spin_lock_irq(&current->sigmask_lock);
+-      tmpsig = current->blocked;
+-      siginitsetinv(&current->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP));
+-      recalc_sigpending(current);
+-      spin_unlock_irq(&current->sigmask_lock);
++              memset(b, 0, sizeof(b));
++              memcpy(b, ptr + 4, 8);
++              addr = simple_strtol(b, NULL, 16);
+-      result = waitpid(pid, NULL, __WCLONE);
++              memset(b, 0, sizeof(b));
++              memcpy(b, ptr + (size * 2) + 2, 2);
++              fcs = simple_strtol(b, NULL, 16);
+-      /* Allow signals again */
+-      spin_lock_irq(&current->sigmask_lock);
+-      current->blocked = tmpsig;
+-      recalc_sigpending(current);
+-      spin_unlock_irq(&current->sigmask_lock);
++              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 (result != pid) {
+-              printk(KERN_WARNING "bt3c_cs: Waiting for pid %d failed (errno=%d).\n", pid, -result);
+-              return -result;
++              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);
++                      }
++              }
 +
-+#define DS1337_HOUR12         0x40
-+#define DS1337_HOUR24         0x00
-+#define DS1337_HOURS_24(val)  BCD_TO_BIN((val & 0x3f))
++              ptr   += (size * 2) + 6;
++              count -= (size * 2) + 6;
+       }
+-      return 0;
+-}
++      udelay(17);
++      /* Boot */
++      bt3c_address(iobase, 0x3000);
++      outb(inb(iobase + CONTROL) | 0x40, iobase + CONTROL);
+-/* ======================== Card services HCI interaction ======================== */
++error:
++      udelay(17);
 +
-+#endif
---- /dev/null
-+++ linux-2.4.21/drivers/char/german.map
-@@ -0,0 +1,528 @@
-+keymaps 0-2,4-6,8-10,12
-+keycode   1 = Escape           Escape          
-+      alt     keycode   1 = Meta_Escape     
-+      shift   alt     keycode   1 = Meta_Escape     
-+keycode   2 = one              exclam          
-+      alt     keycode   2 = Meta_one        
-+      shift   alt     keycode   2 = Meta_exclam     
++      /* 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;
+@@ -570,8 +593,22 @@
+       /* Load firmware */
+-      if ((err = bt3c_firmware_load(info)) < 0)
++      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 */
+@@ -606,6 +643,9 @@
+ {
+       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)
+--- linux-2.4.21/drivers/bluetooth/btuart_cs.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/btuart_cs.c
+@@ -556,6 +556,9 @@
+       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);
+--- linux-2.4.21/drivers/bluetooth/dtl1_cs.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/dtl1_cs.c
+@@ -535,6 +535,9 @@
+       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);
+--- linux-2.4.21/drivers/bluetooth/hci_bcsp.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/hci_bcsp.c
+@@ -34,7 +34,6 @@
+ #include <linux/module.h>
+ #include <linux/version.h>
+-#include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/init.h>
+ #include <linux/sched.h>
+@@ -635,7 +634,8 @@
+       struct sk_buff *skb;
+       unsigned long flags;
+-      BT_ERR("Timeout, retransmitting %u pkts", bcsp->unack.qlen);
++      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) {
+--- linux-2.4.21/drivers/bluetooth/hci_ldisc.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/hci_ldisc.c
+@@ -33,7 +33,6 @@
+ #include <linux/module.h>
+ #include <linux/version.h>
+-#include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/init.h>
+ #include <linux/sched.h>
+--- linux-2.4.21/drivers/bluetooth/hci_uart.h~bluetooth
++++ linux-2.4.21/drivers/bluetooth/hci_uart.h
+@@ -35,11 +35,12 @@
+ #define HCIUARTGETPROTO       _IOR('U', 201, int)
+ /* UART protocols */
+-#define HCI_UART_MAX_PROTO    3
++#define HCI_UART_MAX_PROTO    4
+ #define HCI_UART_H4   0
+ #define HCI_UART_BCSP 1
+-#define HCI_UART_NCSP 2
++#define HCI_UART_3WIRE        2
++#define HCI_UART_H4DS 3
+ #ifdef __KERNEL__
+ struct hci_uart;
+--- linux-2.4.21/drivers/bluetooth/hci_usb.c~bluetooth
++++ linux-2.4.21/drivers/bluetooth/hci_usb.c
+@@ -30,7 +30,7 @@
+  *
+  * $Id$    
+  */
+-#define VERSION "2.4"
++#define VERSION "2.7"
+ #include <linux/config.h>
+ #include <linux/module.h>
+@@ -62,7 +62,7 @@
+ #define BT_DMP( A... )
+ #endif
+-#ifndef CONFIG_BLUEZ_USB_ZERO_PACKET
++#ifndef CONFIG_BLUEZ_HCIUSB_ZERO_PACKET
+ #undef  USB_ZERO_PACKET
+ #define USB_ZERO_PACKET 0
+ #endif
+@@ -73,20 +73,39 @@
+       /* Generic Bluetooth USB device */
+       { USB_DEVICE_INFO(HCI_DEV_CLASS, HCI_DEV_SUBCLASS, HCI_DEV_PROTOCOL) },
+-      /* Ericsson with non-standard id */
+-      { USB_DEVICE(0x0bdb, 0x1002) },
++      /* 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, bluetooth_ids);
+-static struct usb_device_id ignore_ids[] = {
++static struct usb_device_id blacklist_ids[] = {
+       /* Broadcom BCM2033 without firmware */
+-      { USB_DEVICE(0x0a5c, 0x2033) },
++      { USB_DEVICE(0x0a5c, 0x2033), driver_info: HCI_IGNORE },
++
++      /* 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 */
+ };
+@@ -133,6 +152,7 @@
+       return _urb_dequeue(__completed_q(husb, type)); 
+ }
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ static void __fill_isoc_desc(struct urb *urb, int len, int mtu)
+ {
+       int offset = 0, i;
+@@ -152,6 +172,7 @@
+       }
+       urb->number_of_packets = i;
+ }
++#endif
+ static int hci_usb_intr_rx_submit(struct hci_usb *husb)
+ {
+@@ -229,7 +250,7 @@
+       return err;
+ }
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ static int hci_usb_isoc_rx_submit(struct hci_usb *husb)
+ {
+       struct _urb *_urb;
+@@ -300,8 +321,10 @@
+               for (i = 0; i < HCI_MAX_BULK_RX; i++)
+                       hci_usb_bulk_rx_submit(husb);
+-#ifdef CONFIG_BLUEZ_USB_SCO
+-              hci_usb_isoc_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);
+@@ -426,7 +449,7 @@
+       } else
+               dr = (void *) _urb->urb.setup_packet;
+-      dr->bRequestType = HCI_CTRL_REQ;
++      dr->bRequestType = husb->ctrl_req;
+       dr->bRequest = 0;
+       dr->wIndex   = 0;
+       dr->wValue   = 0;
+@@ -467,7 +490,7 @@
+       return __tx_submit(husb, _urb);
+ }
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+ static inline int hci_usb_send_isoc(struct hci_usb *husb, struct sk_buff *skb)
+ {
+       struct _urb *_urb = __get_completed(husb, skb->pkt_type);
+@@ -518,10 +541,10 @@
+                               skb_queue_head(q, skb);
+               }
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+               /* Process SCO queue */
+               q = __transmit_q(husb, HCI_SCODATA_PKT);
+-              if (!atomic_read(__pending_tx(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);
+@@ -577,7 +600,7 @@
+               hdev->stat.acl_tx++;
+               break;
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+       case HCI_SCODATA_PKT:
+               hdev->stat.sco_tx++;
+               break;
+@@ -627,7 +650,7 @@
+                               } else
+                                       return -EILSEQ;
+                               break;
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+                       case HCI_SCODATA_PKT:
+                               if (count >= HCI_SCO_HDR_SIZE) {
+                                       hci_sco_hdr *h = data;
+@@ -638,7 +661,7 @@
+ #endif
+                       }
+                       BT_DBG("new packet len %d", len);
+-                              
++
+                       skb = bluez_skb_alloc(len, GFP_ATOMIC);
+                       if (!skb) {
+                               BT_ERR("%s no memory for the packet", husb->hdev.name);
+@@ -683,16 +706,16 @@
+       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 (!test_bit(HCI_RUNNING, &hdev->flags))
+-              return;
+-
+       read_lock(&husb->completion_lock);
++      if (!test_bit(HCI_RUNNING, &hdev->flags))
++              goto unlock;
++
+       if (urb->status || !count)
+               goto resubmit;
+       if (_urb->type == HCI_SCODATA_PKT) {
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#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,
+@@ -724,6 +747,8 @@
+               BT_DBG("%s urb %p type %d resubmit status %d", hdev->name, urb,
+                               _urb->type, err);
+       }
++
++unlock:
+       read_unlock(&husb->completion_lock);
+ }
+@@ -786,8 +811,14 @@
+       iface = &udev->actconfig->interface[0];
+-      /* Check our black list */
+-      if (usb_match_id(udev, iface, ignore_ids))
++      if (!id->driver_info) {
++              const struct usb_device_id *match;
++              match = usb_match_id(udev, iface, blacklist_ids);
++              if (match)
++                      id = match;
++      }
++
++      if (id->driver_info & HCI_IGNORE)
+               return NULL;
+       /* Check number of endpoints */
+@@ -827,9 +858,9 @@
+                                               bulk_out_ep[i] = ep;
+                                       break;
+-#ifdef CONFIG_BLUEZ_USB_SCO
++#ifdef CONFIG_BLUEZ_HCIUSB_SCO
+                               case USB_ENDPOINT_XFER_ISOC:
+-                                      if (ep->wMaxPacketSize < size)
++                                      if (ep->wMaxPacketSize < size || a > 2)
+                                               break;
+                                       size = ep->wMaxPacketSize;
+@@ -853,8 +884,8 @@
+               goto done;
+       }
+-#ifdef CONFIG_BLUEZ_USB_SCO
+-      if (!isoc_in_ep[1] || !isoc_out_ep[1]) {
++#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;
+       }
+@@ -872,7 +903,12 @@
+       husb->bulk_in_ep  = bulk_in_ep[0];
+       husb->intr_in_ep  = intr_in_ep[0];
+-#ifdef CONFIG_BLUEZ_USB_SCO
++      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)) {
+@@ -906,6 +942,9 @@
+       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) {
+               BT_ERR("Can't register HCI device");
+               goto probe_error;
+@@ -968,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");
+--- linux-2.4.21/drivers/bluetooth/hci_usb.h~bluetooth
++++ linux-2.4.21/drivers/bluetooth/hci_usb.h
+@@ -35,12 +35,21 @@
+ #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 {
+@@ -119,6 +128,8 @@
+       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
+--- linux-2.4.21/drivers/char/Config.in~i2c-ds1337
++++ linux-2.4.21/drivers/char/Config.in
+@@ -164,6 +164,7 @@
+ if [ "$CONFIG_I2C" != "n" ]; then
+     dep_tristate '  DS1307 RTC' CONFIG_I2C_DS1307 $CONFIG_I2C
++    dep_tristate '  DS1337 RTC' CONFIG_I2C_DS1337 $CONFIG_I2C
+ fi
+ source drivers/l3/Config.in
+--- linux-2.4.21/drivers/char/Makefile~i2c-ds1337
++++ linux-2.4.21/drivers/char/Makefile
+@@ -21,10 +21,11 @@
+ # All of the (potential) objects that export symbols.
+ # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'.
+-export-objs     :=    busmouse.o console.o keyboard.o sysrq.o \
++export-objs     :=    vt.o busmouse.o console.o keyboard.o sysrq.o \
+                       misc.o pty.o random.o selection.o serial.o \
+                       sonypi.o tty_io.o tty_ioctl.o generic_serial.o \
+-                      au1000_gpio.o hp_psaux.o nvram.o scx200.o
++                      au1000_gpio.o hp_psaux.o nvram.o scx200.o \
++                      input_keyb.o
+ mod-subdirs   :=      joystick ftape drm drm-4.0 pcmcia
+@@ -129,6 +130,11 @@
+   ifeq ($(CONFIG_SA1100_CERF_CPLD),y)
+     KEYBD    += cerf_keyb.o
+   endif
++  ifeq ($(CONFIG_ARCH_RAMSES),y)
++    KEYMAP   = german.o
++    KEYBD    += input_keyb.o
++    obj-m    += sysctl.o
++  endif
+   ifeq ($(CONFIG_ARCH_FORTUNET),y)
+     KEYMAP   := defkeymap.o
+   endif
+@@ -337,6 +343,7 @@
+ # I2C char devices
+ obj-$(CONFIG_I2C_DS1307) += ds1307.o
++obj-$(CONFIG_I2C_DS1337) += ds1337.o
+ subdir-$(CONFIG_MWAVE) += mwave
+ ifeq ($(CONFIG_MWAVE),y)
+@@ -373,3 +380,6 @@
+ qtronixmap.c: qtronixmap.map
+       set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@
++
++german.c: german.map
++      set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@
+--- linux-2.4.21/drivers/char/console.c~keyb-module
++++ linux-2.4.21/drivers/char/console.c
+@@ -150,7 +150,7 @@
+ static int con_open(struct tty_struct *, struct file *);
+ static void vc_init(unsigned int console, unsigned int rows,
+                   unsigned int cols, int do_clear);
+-static void blank_screen(unsigned long dummy);
++//static void blank_screen(unsigned long dummy);
+ static void gotoxy(int currcons, int new_x, int new_y);
+ static void save_cur(int currcons);
+ static void reset_terminal(int currcons, int do_clear);
+@@ -158,7 +158,7 @@
+ static void set_vesa_blanking(unsigned long arg);
+ static void set_cursor(int currcons);
+ static void hide_cursor(int currcons);
+-static void unblank_screen_t(unsigned long dummy);
++//static void unblank_screen_t(unsigned long dummy);
+ static void console_callback(void *ignored);
+ static int printable;         /* Is console ready for printing? */
+@@ -167,7 +167,7 @@
+ int console_blanked;
+ static int vesa_blank_mode; /* 0:none 1:suspendV 2:suspendH 3:powerdown */
+-static int blankinterval = 10*60*HZ;
++//static int blankinterval = 10*60*HZ;
+ static int vesa_off_interval;
+ static struct tq_struct console_callback_tq = {
+@@ -209,9 +209,9 @@
+  * Hook so that the power management routines can (un)blank
+  * the console on our behalf.
+  */
+-int (*console_blank_hook)(int);
++//int (*console_blank_hook)(int);
+-static struct timer_list console_timer;
++//static struct timer_list console_timer;
+ /*
+  *    Low-Level Functions
+@@ -543,7 +543,7 @@
+ static void set_cursor(int currcons)
+ {
+-    if (!IS_FG || console_blanked || vcmode == KD_GRAPHICS)
++    if (!IS_FG || vcmode == KD_GRAPHICS)
+       return;
+     if (deccm) {
+       if (currcons == sel_cons)
+@@ -1287,7 +1287,7 @@
+                       update_attr(currcons);
+                       break;
+               case 9: /* set blanking interval */
+-                      blankinterval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ;
++                      //blankinterval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ;
+                       poke_blanked_console();
+                       break;
+               case 10: /* set bell frequency in Hz */
+@@ -2575,11 +2575,11 @@
+       if (tty_register_driver(&console_driver))
+               panic("Couldn't register console driver\n");
+-      init_timer(&console_timer);
+-      console_timer.function = blank_screen;
+-      if (blankinterval) {
+-              mod_timer(&console_timer, jiffies + blankinterval);
+-      }
++      //init_timer(&console_timer);
++      //console_timer.function = blank_screen;
++      //if (blankinterval) {
++      //      mod_timer(&console_timer, jiffies + blankinterval);
++      //}
+       /*
+        * kmalloc is not running yet - we use the bootmem allocator.
+@@ -2744,11 +2744,12 @@
+  */
+ static void vesa_powerdown_screen(unsigned long dummy)
+ {
+-      console_timer.function = unblank_screen_t;
++      //console_timer.function = unblank_screen_t;
+       vesa_powerdown();
+ }
++#if 0
+ static void timer_do_blank_screen(int entering_gfx, int from_timer_handler)
+ {
+       int currcons = fg_console;
+@@ -2797,12 +2798,14 @@
+       if (vesa_blank_mode)
+               sw->con_blank(vc_cons[currcons].d, vesa_blank_mode + 1);
+ }
++#endif
+ void do_blank_screen(int entering_gfx)
+ {
+-      timer_do_blank_screen(entering_gfx, 0);
++      //timer_do_blank_screen(entering_gfx, 0);
+ }
++#if 0
+ /*
+  * This is a timer handler
+  */
+@@ -2810,12 +2813,14 @@
+ {
+       unblank_screen();
+ }
++#endif
+ /*
+  * Called by timer as well as from vt_console_driver
+  */
+ void unblank_screen(void)
+ {
++#if 0
+       int currcons;
+       if (!console_blanked)
+@@ -2842,6 +2847,7 @@
+               /* Low-level driver cannot restore -> do it ourselves */
+               update_screen(fg_console);
+       set_cursor(fg_console);
++#endif
+ }
+ /*
+@@ -2849,11 +2855,12 @@
+  */
+ static void blank_screen(unsigned long dummy)
+ {
+-      timer_do_blank_screen(0, 1);
++      //timer_do_blank_screen(0, 1);
+ }
+ void poke_blanked_console(void)
+ {
++#if 0
+       del_timer(&console_timer);
+       if (!vt_cons[fg_console] || vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
+               return;
+@@ -2863,6 +2870,7 @@
+       } else if (blankinterval) {
+               mod_timer(&console_timer, jiffies + blankinterval);
+       }
++#endif
+ }
+ /*
+@@ -3088,7 +3096,7 @@
+               unblank_screen();
+               break;
+       case PM_SUSPEND:
+-              do_blank_screen(0);
++              //do_blank_screen(0);
+               break;
+       }
+       return 0;
+@@ -3106,7 +3114,8 @@
+ EXPORT_SYMBOL(video_scan_lines);
+ EXPORT_SYMBOL(vc_resize);
+ EXPORT_SYMBOL(fg_console);
+-EXPORT_SYMBOL(console_blank_hook);
++//EXPORT_SYMBOL(console_blank_hook);
++EXPORT_SYMBOL(console_driver);
+ #ifdef CONFIG_VT
+ EXPORT_SYMBOL(vt_cons);
+ #endif
+--- /dev/null
++++ linux-2.4.21/drivers/char/ds1337.c
+@@ -0,0 +1,545 @@
++/*
++ * ds1337.c
++ *
++ * Device driver for Dallas Semiconductor's Real Time Controller DS1337.
++ *
++ * Copyright (C) 2003 M&N Logistik-Lösungen Online GmbH
++ *
++ * 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.
++ *
++ * Documentation for this Chip: http://pdfserv.maxim-ic.com/arpdf/DS1337.pdf
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/version.h>
++
++#include <linux/kernel.h>
++#include <linux/poll.h>
++#include <linux/i2c.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/rtc.h>
++#include <linux/string.h>
++#include <linux/miscdevice.h>
++#include <linux/proc_fs.h>
++
++#include "ds1337.h"
++
++//#define DEBUG 1
++
++#if DEBUG
++static unsigned int rtc_debug = DEBUG;
++#else
++#define rtc_debug 0           /* gcc will remove all the debug code for us */
++#endif
++
++static unsigned short slave_address = DS1337_I2C_SLAVE_ADDR;
++struct i2c_driver ds1337_driver;
++struct i2c_client *ds1337_i2c_client = 0;
++static spinlock_t ds1337_rtc_lock = SPIN_LOCK_UNLOCKED;
++
++static unsigned short ignore[] = { I2C_CLIENT_END };
++static unsigned short normal_addr[] = { DS1337_I2C_SLAVE_ADDR, I2C_CLIENT_END };
++
++static int ds1337_rtc_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
++static int ds1337_rtc_noop(struct inode *inode, struct file *file);
++
++static int ds1337_probe(struct i2c_adapter *adap);
++static int ds1337_detach(struct i2c_client *client);
++static int ds1337_command(struct i2c_client *client, unsigned int cmd, void *arg);
++
++
++static struct i2c_client_address_data addr_data = {
++    .normal_i2c =     normal_addr,
++    .normal_i2c_range =       ignore,
++    .probe =          ignore,
++    .probe_range =    ignore,
++    .ignore =         ignore,
++    .ignore_range =   ignore,
++    .force =          ignore,
++};
++
++static struct file_operations rtc_fops = {
++    .owner =          THIS_MODULE,
++    .ioctl =          ds1337_rtc_ioctl,
++    .open =           ds1337_rtc_noop,
++    .release =                ds1337_rtc_noop,
++};
++
++static struct miscdevice ds1337_rtc_miscdev = {
++    RTC_MINOR,
++    "rtc",
++    &rtc_fops
++};
++
++
++struct i2c_driver ds1337_driver = {
++    .name =           "DS1337",
++    .id =             I2C_DRIVERID_DS1337,
++    .flags =          I2C_DF_NOTIFY,
++    .attach_adapter = ds1337_probe,
++    .detach_client =  ds1337_detach,
++    .command =                ds1337_command
++};
++
++#define DAT(x) ((unsigned int)((x)->data))    /* keep the control register info */
++
++
++static int ds1337_readram(char *buf, int len)
++{
++    unsigned long flags;
++    unsigned char ad[1] = { 0 };
++    int ret;
++    struct i2c_msg msgs[2] = {
++      {ds1337_i2c_client->addr, 0, 1, ad},
++      {ds1337_i2c_client->addr, I2C_M_RD, len, buf}
++    };
++
++    spin_lock_irqsave(&ds1337_rtc_lock, flags);
++    ret = i2c_transfer(ds1337_i2c_client->adapter, msgs, 2);
++    spin_unlock_irqrestore(&ds1337_rtc_lock, flags);
++
++    return ret;
++}
++
++
++static void ds1337_setreg(struct i2c_client *c, unsigned char reg, unsigned char val)
++{
++    unsigned char buf[2];
++    buf[0] = reg;
++    buf[1] = val;
++    i2c_master_send(c, (char *) buf, 2);
++}
++
++static int ds1337_attach(struct i2c_adapter *adap, int addr,
++                       unsigned short flags, int kind)
++{
++    struct i2c_client *c;
++    unsigned char buf[DS1337_MEM_SIZE], ad[1] = { 7 };
++    struct i2c_msg msgs[2] = {
++      {addr, 0, 1, ad},
++      {addr, I2C_M_RD, 1, buf}
++    };
++    int ret;
++
++    if (rtc_debug>1)
++      printk("%s(adap,%d,%d,%d)\n", __FUNCTION__, addr, flags, kind);
++
++    c = (struct i2c_client *) kmalloc(sizeof(*c), GFP_KERNEL);
++    if (!c)
++      return -ENOMEM;
++
++    strcpy(c->name, "DS1337");
++    c->id = ds1337_driver.id;
++    c->flags = 0;
++    c->addr = addr;
++    c->adapter = adap;
++    c->driver = &ds1337_driver;
++    c->data = NULL;
++
++    ret = i2c_transfer(c->adapter, msgs, 2);
++
++    if (ret == 2) {
++      DAT(c) = buf[0];
++    } else
++      printk("ds1337_attach(): i2c_transfer() returned %d.\n", ret);
++
++    ds1337_i2c_client = c;
++
++    ds1337_readram(buf, DS1337_MEM_SIZE);
++
++                                              // set 24 hour mode
++    ds1337_setreg(c, 0x2, buf[2] | DS1337_HOUR24);
++                                              // INTCN sets INTB to alarm2 (disables SQW)
++    ds1337_setreg(c, 0x5, buf[5] & 0x7f);     // clear century
++    ds1337_setreg(c, 0x7, 0x00);              // clear Alarm 1 seconds
++    ds1337_setreg(c, 0x8, 0x00);              // clear Alarm 1 minutes
++    ds1337_setreg(c, 0x9, 0x40);              // clear Alarm 1 hours, 24 hour on
++    ds1337_setreg(c, 0xA, 0x00);              // clear Alarm 1 date
++    ds1337_setreg(c, 0xB, 0x00);              // clear Alarm 2 minutes
++    ds1337_setreg(c, 0xC, 0x40);              // clear Alarm 2 hours, 24 hour on
++    ds1337_setreg(c, 0xD, 0x00);              // clear Alarm 2 date
++    ds1337_setreg(c, 0xe, 4);                 // nEOSC enabled
++    ds1337_setreg(c, 0xf, 0);                 // clear OSF, A2F, A1F
++
++    return i2c_attach_client(c);
++}
++
++
++static int ds1337_probe(struct i2c_adapter *adap)
++{
++    if (rtc_debug>1)
++      printk("%s()\n", __FUNCTION__);
++
++    return i2c_probe(adap, &addr_data, ds1337_attach);
++}
++
++
++static int ds1337_detach(struct i2c_client *client)
++{
++    if (rtc_debug>1)
++      printk("%s()\n", __FUNCTION__);
++
++    i2c_detach_client(client);
++
++    return 0;
++}
++
++
++static void ds1337_convert_to_time(struct rtc_time *dt, char *buf)
++{
++    if (rtc_debug>1)
++      printk("%s()\n", __FUNCTION__);
++
++    dt->tm_sec = BCD_TO_BIN(buf[0]);
++    dt->tm_min = BCD_TO_BIN(buf[1]);
++    dt->tm_hour = DS1337_HOURS_24(buf[2]);
++
++    dt->tm_mday = BCD_TO_BIN(buf[4]);
++    /* dt->tm_mon is zero-based */
++    dt->tm_mon = BCD_TO_BIN(buf[5]) - 1;
++    /* year is 1900 + dt->tm_year */
++    dt->tm_year = BCD_TO_BIN(buf[6]) + 100;
++
++    if (rtc_debug > 2) {
++      printk("ds1337_get_datetime: year = %d\n", dt->tm_year);
++      printk("ds1337_get_datetime: mon  = %d\n", dt->tm_mon);
++      printk("ds1337_get_datetime: mday = %d\n", dt->tm_mday);
++      printk("ds1337_get_datetime: hour = %d\n", dt->tm_hour);
++      printk("ds1337_get_datetime: min  = %d\n", dt->tm_min);
++      printk("ds1337_get_datetime: sec  = %d\n", dt->tm_sec);
++    }
++}
++
++
++static int ds1337_get_datetime(struct i2c_client *client,
++                             struct rtc_time *dt)
++{
++    unsigned char buf[7], addr[1] = { 0 };
++    struct i2c_msg msgs[2] = {
++      {client->addr, 0, 1, addr},
++      {client->addr, I2C_M_RD, 7, buf}
++    };
++    int ret = -EIO;
++
++    if (rtc_debug)
++      printk("%s()\n", __FUNCTION__);
++
++    memset(buf, 0, sizeof(buf));
++
++    ret = i2c_transfer(client->adapter, msgs, 2);
++
++    if (ret == 2) {
++      ds1337_convert_to_time(dt, buf);
++      ret = 0;
++    } else
++      printk("ds1337_get_datetime(), i2c_transfer() returned %d\n", ret);
++
++    return ret;
++}
++
++
++static int ds1337_set_datetime(struct i2c_client *client,
++                             struct rtc_time *dt, int datetoo)
++{
++    unsigned char buf[8];
++    int ret, len = 4;
++
++    if (rtc_debug)
++      printk("%s()\n", __FUNCTION__);
++
++    if (rtc_debug > 2) {
++      printk("ds1337_set_datetime: tm_year = %d\n", dt->tm_year);
++      printk("ds1337_set_datetime: tm_mon  = %d\n", dt->tm_mon);
++      printk("ds1337_set_datetime: tm_mday = %d\n", dt->tm_mday);
++      printk("ds1337_set_datetime: tm_hour = %d\n", dt->tm_hour);
++      printk("ds1337_set_datetime: tm_min  = %d\n", dt->tm_min);
++      printk("ds1337_set_datetime: tm_sec  = %d\n", dt->tm_sec);
++    }
++
++    buf[0] = 0;                       /* register address on DS1337 */
++    buf[1] = (BIN_TO_BCD(dt->tm_sec));
++    buf[2] = (BIN_TO_BCD(dt->tm_min));
++    buf[3] = (BIN_TO_BCD(dt->tm_hour)) | DS1337_HOUR24;
++
++    if (datetoo) {
++      len = 8;
++      /* we skip buf[4] as we don't use day-of-week. */
++      buf[5] = (BIN_TO_BCD(dt->tm_mday));
++      buf[6] = (BIN_TO_BCD(dt->tm_mon + 1));
++      /* The year only ranges from 0-99, we are being passed an offset from 1900,
++       * and the chip calulates leap years based on 2000, thus we adjust by 100.
++       */
++      buf[7] = (BIN_TO_BCD(dt->tm_year - 100));
++    }
++    ret = i2c_master_send(client, (char *) buf, len);
++    if (ret == len)
++      ret = 0;
++    else
++      printk("ds1337_set_datetime(), i2c_master_send() returned %d\n",
++             ret);
++
++
++    return ret;
++}
++
++
++#if 0
++static int ds1337_get_ctrl(struct i2c_client *client, unsigned char *ctrl)
++{
++    *ctrl = DAT(client);
++
++    if (rtc_debug)
++      printk("%s():%d\n", __FUNCTION__, *ctrl);
++
++    return 0;
++}
++
++
++static int ds1337_set_ctrl(struct i2c_client *client, unsigned char *cinfo)
++{
++    unsigned char buf[2];
++    int ret;
++
++    if (rtc_debug)
++      printk("%s(%d)\n", __FUNCTION__, *cinfo);
++
++    buf[0] = 7;                       /* control register address on DS1337 */
++    buf[1] = *cinfo;
++    /* save the control reg info in the client data field so that get_ctrl
++     * function doesn't have to do an I2C transfer to get it.
++     */
++    DAT(client) = buf[1];
++
++    ret = i2c_master_send(client, (char *) buf, 2);
++
++    return ret;
++}
++#endif
++
++
++static int ds1337_command(struct i2c_client *client, unsigned int cmd,
++                        void *arg)
++{
++    if (rtc_debug)
++      printk("%s(client,,%u,arg)\n", __FUNCTION__, cmd);
++
++    switch (cmd) {
++    case DS1337_GETDATETIME:
++      return ds1337_get_datetime(client, arg);
++
++    case DS1337_SETTIME:
++      return ds1337_set_datetime(client, arg, 0);
++
++    case DS1337_SETDATETIME:
++      return ds1337_set_datetime(client, arg, 1);
++
++    default:
++      return -EINVAL;
++    }
++}
++
++
++static int ds1337_rtc_noop(struct inode *inode, struct file *file)
++{
++    return 0;
++}
++
++
++static int ds1337_rtc_ioctl(struct inode *inode, struct file *file,
++                          unsigned int cmd, unsigned long arg)
++{
++    unsigned long flags;
++    struct rtc_time wtime;
++    int status = 0;
++
++    if (rtc_debug)
++      printk("%s()\n", __FUNCTION__);
++
++    switch (cmd) {
++    default:
++    case RTC_UIE_ON:          // mask ints from RTC updates
++    case RTC_UIE_OFF:
++    case RTC_PIE_ON:          // allow periodic interrupts
++    case RTC_PIE_OFF:
++    case RTC_AIE_ON:          // mask alarm int enable bit
++    case RTC_AIE_OFF:
++    case RTC_ALM_SET:
++      /*
++       * This expects a struct rtc_time. Writing 0xff means
++       * "don't care" or "match all". Only the tm_hour,
++       * tm_min and tm_sec are used.
++       */
++    case RTC_ALM_READ:
++      // get_rtc_alm_time(&wtime);
++    case RTC_IRQP_READ:               // Read the periodic IRQ rate
++    case RTC_IRQP_SET:                // Set periodic IRQ rate
++    case RTC_EPOCH_READ:
++      // return put_user (epoch, (unsigned long *)arg);
++    case RTC_EPOCH_SET:
++    case RTC_WKALM_SET:
++    case RTC_WKALM_RD:
++      status = -EINVAL;
++      break;
++
++    case RTC_RD_TIME:
++      spin_lock_irqsave(&ds1337_rtc_lock, flags);
++      ds1337_command(ds1337_i2c_client, DS1337_GETDATETIME, &wtime);
++      spin_unlock_irqrestore(&ds1337_rtc_lock, flags);
++
++      if (copy_to_user((void *) arg, &wtime, sizeof(struct rtc_time)))
++          status = -EFAULT;
++      break;
++
++    case RTC_SET_TIME:
++      if (!capable(CAP_SYS_TIME)) {
++          status = -EACCES;
++          break;
++      }
++
++      if (copy_from_user
++          (&wtime, (struct rtc_time *) arg, sizeof(struct rtc_time))) {
++          status = -EFAULT;
++          break;
++      }
++
++      spin_lock_irqsave(&ds1337_rtc_lock, flags);
++      ds1337_command(ds1337_i2c_client, DS1337_SETDATETIME, &wtime);
++      spin_unlock_irqrestore(&ds1337_rtc_lock, flags);
++      break;
++    }
++
++    return status;
++}
++
++
++static char *ds1337_mon2str(unsigned int mon)
++{
++    char *mon2str[12] = {
++      "Jan", "Feb", "Mar", "Apr", "May", "Jun",
++      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
++    };
++    if (mon > 11)
++      return "error";
++    else
++      return mon2str[mon];
++}
++
++
++static int ds1337_rtc_proc_output(char *buf)
++{
++#define CHECK(ctrl,bit) ((ctrl & bit) ? "yes" : "no")
++
++    unsigned char ram[DS1337_MEM_SIZE];
++    int ret;
++
++    char *p = buf;
++
++    ret = ds1337_readram(ram, DS1337_MEM_SIZE);
++    if (ret > 0) {
++#ifdef DEBUG
++      int i;
++      char text[9];
++#endif
++      struct rtc_time dt;
++
++      p += sprintf(p, "DS1337 (i2c Serial Real Time Clock)\n");
++
++      ds1337_convert_to_time(&dt, ram);
++      p += sprintf(p, "Date/Time: %02d-%s-%04d %02d:%02d:%02d\n",
++                   dt.tm_mday, ds1337_mon2str(dt.tm_mon),
++                   dt.tm_year + 1900, dt.tm_hour, dt.tm_min, dt.tm_sec);
++
++#ifdef DEBUG
++      p += sprintf(p, "RAM dump:\n");
++      text[8] = '\0';
++      for (i = 0; i < DS1337_MEM_SIZE; i++) {
++          if ((i % 8) == 0)
++              p += sprintf(p, "%02X: ", i);
++          p += sprintf(p, "%02X ", ram[i]);
++
++          if ((ram[i] < 32) || (ram[i] > 126))
++              ram[i] = '.';
++          text[i % 8] = ram[i];
++          if ((i % 8) == 7)
++              p += sprintf(p, "%s\n", text);
++      }
++      p += sprintf(p, "\n");
++#endif
++    } else {
++      p += sprintf(p, "Failed to read RTC memory!\n");
++    }
++
++    return p - buf;
++}
++
++
++static int ds1337_rtc_read_proc(char *page, char **start, off_t off,
++                              int count, int *eof, void *data)
++{
++    int len = ds1337_rtc_proc_output(page);
++
++    if (len <= off + count)
++      *eof = 1;
++    *start = page + off;
++    len -= off;
++    if (len > count)
++      len = count;
++    if (len < 0)
++      len = 0;
++    return len;
++}
++
++
++static __init int ds1337_init(void)
++{
++    int retval = 0;
++
++    if (rtc_debug>1)
++      printk("%s()\n", __FUNCTION__);
++
++    if (slave_address != 0xffff) {
++      normal_addr[0] = slave_address;
++    }
++
++    if (normal_addr[0] == 0xffff) {
++      printk(KERN_ERR
++             "I2C: Invalid slave address for DS1337 RTC (%#x)\n",
++             normal_addr[0]);
++      return -EINVAL;
++    }
++
++    retval = i2c_add_driver(&ds1337_driver);
++
++    if (retval == 0) {
++      misc_register(&ds1337_rtc_miscdev);
++      create_proc_read_entry(DS1337_PROC_NAME, 0, 0,
++                             ds1337_rtc_read_proc, NULL);
++      printk("I2C: DS1337 RTC driver loaded\n");
++    }
++    return retval;
++}
++
++
++static __exit void ds1337_exit(void)
++{
++    if (rtc_debug>1)
++      printk("%s()\n", __FUNCTION__);
++
++    remove_proc_entry(DS1337_PROC_NAME, NULL);
++    misc_deregister(&ds1337_rtc_miscdev);
++    i2c_del_driver(&ds1337_driver);
++}
++
++
++module_init(ds1337_init);
++module_exit(ds1337_exit);
++
++MODULE_PARM(slave_address, "i");
++MODULE_PARM_DESC(slave_address, "I2C slave address for DS1337 RTC");
++
++MODULE_AUTHOR("M&N Logistik-Lösungen Online GmbH");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.4.21/drivers/char/ds1337.h
+@@ -0,0 +1,43 @@
++/*
++ * ds1337.h
++ *
++ * Copyright (C) 2003 M&N Logistik-Lösungen Online GmbH
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ */
++#ifndef DS1337_H
++#define DS1337_H
++
++#define DS1337_I2C_SLAVE_ADDR 0x68
++//#define DS1337_RAM_ADDR_START       0x10
++//#define DS1337_RAM_ADDR_END 0x10
++#define DS1337_MEM_SIZE               0x10
++
++#define DS1337_PROC_NAME      "driver/ds1337"
++
++struct rtc_mem {
++      unsigned int    loc;
++      unsigned int    nr;
++      unsigned char   *data;
++};
++
++#define DS1337_GETDATETIME    0
++#define DS1337_SETTIME                1
++#define DS1337_SETDATETIME    2
++
++#define DS1337_RATE_1HZ       0x00    /* Rate Select     1 Hz */
++#define DS1337_RATE_4096HZ    0x01    /* Rate Select  4096 kHz */
++#define DS1337_RATE_8192HZ    0x02    /* Rate Select  8192 kHz */
++#define DS1337_RATE_32768HZ   0x03    /* Rate Select 32768 kHz */
++
++#define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10)
++#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10)
++
++#define DS1337_HOUR12         0x40
++#define DS1337_HOUR24         0x00
++#define DS1337_HOURS_24(val)  BCD_TO_BIN((val & 0x3f))
++
++#endif
+--- /dev/null
++++ linux-2.4.21/drivers/char/german.map
+@@ -0,0 +1,528 @@
++keymaps 0-2,4-6,8-10,12
++keycode   1 = Escape           Escape          
++      alt     keycode   1 = Meta_Escape     
++      shift   alt     keycode   1 = Meta_Escape     
++keycode   2 = one              exclam          
++      alt     keycode   2 = Meta_one        
++      shift   alt     keycode   2 = Meta_exclam     
 +keycode   3 = two              quotedbl         twosuperior      nul             
 +      alt     keycode   3 = Meta_two        
 +      shift   alt     keycode   3 = Meta_quotedbl   
  static void change_speed(struct async_struct *info, struct termios *old);
  static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
  
-@@ -325,46 +165,82 @@
+@@ -325,46 +165,86 @@
        { 0, 0}
  };
  
 +              flags:                  ASYNC_SKIP_TEST,
 +      }, {
 +              type:                   PORT_UNKNOWN,
++              baud_base:              115200,
 +      }, {
 +              type:                   PORT_UNKNOWN,
++              baud_base:              115200,
 +      }, {
 +              type:                   PORT_UNKNOWN,
++              baud_base:              115200,
 +      }, {
 +              type:                   PORT_UNKNOWN,
++              baud_base:              115200,
 +      }
  };
  
  
  #ifndef PREPARE_FUNC
  #define PREPARE_FUNC(dev)  (dev->prepare)
-@@ -403,39 +279,21 @@
+@@ -403,39 +283,21 @@
  #endif
  
  
                return readl((unsigned long) info->iomem_base +
                             (offset<<info->iomem_reg_shift));
        default:
-@@ -447,17 +305,14 @@
+@@ -447,17 +309,14 @@
                                int value)
  {
        switch (info->io_type) {
                writel(value, (unsigned long) info->iomem_base +
                              (offset<<info->iomem_reg_shift));
                break;
-@@ -509,9 +364,6 @@
+@@ -509,9 +368,6 @@
        struct async_struct *info = (struct async_struct *)tty->driver_data;
        unsigned long flags;
  
        save_flags(flags); cli();
        if (info->IER & UART_IER_THRI) {
                info->IER &= ~UART_IER_THRI;
-@@ -529,9 +381,6 @@
+@@ -529,9 +385,6 @@
        struct async_struct *info = (struct async_struct *)tty->driver_data;
        unsigned long flags;
        
        save_flags(flags); cli();
        if (info->xmit.head != info->xmit.tail
            && info->xmit.buf
-@@ -689,11 +538,7 @@
+@@ -689,11 +542,7 @@
  #endif
                *status = serial_inp(info, UART_LSR);
        } while ((*status & UART_LSR_DR) && (max_count-- > 0));
  }
  
  static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
-@@ -758,11 +603,6 @@
+@@ -758,11 +607,6 @@
                        icount->dsr++;
                if (status & UART_MSR_DDCD) {
                        icount->dcd++;
                }
                if (status & UART_MSR_DCTS)
                        icount->cts++;
-@@ -810,120 +650,23 @@
+@@ -810,120 +654,23 @@
        }
  }
  
        do {
                status = serial_inp(info, UART_LSR);
  #ifdef SERIAL_DEBUG_INTR
-@@ -932,120 +675,23 @@
+@@ -932,120 +679,23 @@
                if (status & UART_LSR_DR)
                        receive_chars(info, &status, regs);
                check_modem_status(info);
  
  /*
   * -------------------------------------------------------------------
-@@ -1107,22 +753,6 @@
+@@ -1107,22 +757,6 @@
                        if (!info)
                                continue;
                        save_flags(flags); cli();
                                rs_interrupt_single(i, NULL, NULL);
                        restore_flags(flags);
                }
-@@ -1132,11 +762,7 @@
+@@ -1132,11 +766,7 @@
  
        if (IRQ_ports[0]) {
                save_flags(flags); cli();
                restore_flags(flags);
  
                mod_timer(&serial_timer, jiffies + IRQ_timeout[0]);
-@@ -1177,50 +803,6 @@
+@@ -1177,50 +807,6 @@
        IRQ_timeout[irq] = (timeout > 3) ? timeout-2 : 1;
  }
  
  static int startup(struct async_struct * info)
  {
        unsigned long flags;
-@@ -1228,9 +810,6 @@
+@@ -1228,9 +814,6 @@
        void (*handler)(int, void *, struct pt_regs *);
        struct serial_state *state= info->state;
        unsigned long page;
  
        page = get_zeroed_page(GFP_KERNEL);
        if (!page)
-@@ -1258,6 +837,22 @@
+@@ -1258,6 +841,22 @@
        printk("starting up ttys%d (irq %d)...", info->line, state->irq);
  #endif
  
        if (uart_config[state->type].flags & UART_STARTECH) {
                /* Wake up UART */
                serial_outp(info, UART_LCR, 0xBF);
-@@ -1305,25 +900,12 @@
+@@ -1305,25 +904,12 @@
                serial_outp(info, UART_LCR, 0);
        }
  
                        case (long)&STUART: CKEN |= CKEN5_STUART; break;
                }
        }
-@@ -1344,6 +926,7 @@
+@@ -1344,6 +930,7 @@
        /*
         * Clear the interrupt registers.
         */
        (void) serial_inp(info, UART_LSR);
        (void) serial_inp(info, UART_RX);
        (void) serial_inp(info, UART_IIR);
-@@ -1371,18 +954,8 @@
+@@ -1371,18 +958,8 @@
        if (state->irq && (!IRQ_ports[state->irq] ||
                          !IRQ_ports[state->irq]->next_port)) {
                if (IRQ_ports[state->irq]) {
                } else 
                        handler = rs_interrupt_single;
  
-@@ -1417,12 +990,6 @@
+@@ -1417,12 +994,6 @@
        info->MCR = 0;
        if (info->tty->termios->c_cflag & CBAUD)
                info->MCR = UART_MCR_DTR | UART_MCR_RTS;
        {
                if (state->irq != 0)
                        info->MCR |= UART_MCR_OUT2;
-@@ -1437,18 +1004,9 @@
+@@ -1437,18 +1008,9 @@
         */
        info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
        if (pxa_port(state->type))
        /*
         * And clear the interrupt registers again for luck.
         */
-@@ -1469,7 +1027,6 @@
+@@ -1469,7 +1031,6 @@
        /*
         * Set up the tty->alt_speed kludge
         */
        if (info->tty) {
                if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
                        info->tty->alt_speed = 57600;
-@@ -1480,7 +1037,6 @@
+@@ -1480,7 +1041,6 @@
                if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
                        info->tty->alt_speed = 460800;
        }
        
        /*
         * and set the speed of the serial port
-@@ -1516,6 +1072,30 @@
+@@ -1516,6 +1076,30 @@
               state->irq);
  #endif
        
        save_flags(flags); cli(); /* Disable interrupts */
  
        /*
-@@ -1561,13 +1141,6 @@
+@@ -1561,13 +1145,6 @@
  
        info->IER = 0;
        serial_outp(info, UART_IER, 0x00);      /* disable all intrs */
                info->MCR &= ~UART_MCR_OUT2;
                if (pxa_buggy_port(state->type))
                        info->MCR ^= UART_MCR_OUT2;
-@@ -1586,16 +1159,6 @@
+@@ -1586,16 +1163,6 @@
                                     UART_FCR_CLEAR_XMIT));
        serial_outp(info, UART_FCR, 0);
  
  #ifdef CONFIG_ARCH_PXA
        if (state->type == PORT_PXA
  #ifdef CONFIG_SERIAL_CONSOLE
-@@ -1634,37 +1197,6 @@
+@@ -1634,37 +1201,6 @@
        restore_flags(flags);
  }
  
  /*
   * This routine is called to set the UART divisor registers to match
   * the specified baud rate for a serial port.
-@@ -1711,12 +1243,6 @@
+@@ -1711,12 +1247,6 @@
        baud = tty_get_baud_rate(info->tty);
        if (!baud)
                baud = 9600;    /* B0 transition handled in rs_set_termios */
        baud_base = info->state->baud_base;
        if (info->state->type == PORT_16C950) {
                if (baud <= baud_base)
-@@ -1778,10 +1304,6 @@
+@@ -1778,10 +1308,6 @@
        if (uart_config[info->state->type].flags & UART_USE_FIFO) {
                if ((info->state->baud_base / quot) < 2400)
                        fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
                else
                        fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
        }
-@@ -1864,9 +1386,6 @@
+@@ -1864,9 +1390,6 @@
        struct async_struct *info = (struct async_struct *)tty->driver_data;
        unsigned long flags;
  
        if (!tty || !info->xmit.buf)
                return;
  
-@@ -1888,9 +1407,6 @@
+@@ -1888,9 +1411,6 @@
        struct async_struct *info = (struct async_struct *)tty->driver_data;
        unsigned long flags;
                                
        if (info->xmit.head == info->xmit.tail
            || tty->stopped
            || tty->hw_stopped
-@@ -1900,8 +1416,6 @@
+@@ -1900,8 +1420,6 @@
        save_flags(flags); cli();
        info->IER |= UART_IER_THRI;
        serial_out(info, UART_IER, info->IER);
        restore_flags(flags);
  }
  
-@@ -1912,9 +1426,6 @@
+@@ -1912,9 +1430,6 @@
        struct async_struct *info = (struct async_struct *)tty->driver_data;
        unsigned long flags;
                                
        if (!tty || !info->xmit.buf || !tmp_buf)
                return 0;
  
-@@ -1978,11 +1489,6 @@
+@@ -1978,11 +1493,6 @@
            && !(info->IER & UART_IER_THRI)) {
                info->IER |= UART_IER_THRI;
                serial_out(info, UART_IER, info->IER);
        }
        return ret;
  }
-@@ -1991,8 +1497,6 @@
+@@ -1991,8 +1501,6 @@
  {
        struct async_struct *info = (struct async_struct *)tty->driver_data;
  
        return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
  }
  
-@@ -2000,8 +1504,6 @@
+@@ -2000,8 +1508,6 @@
  {
        struct async_struct *info = (struct async_struct *)tty->driver_data;
                                
        return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
  }
  
-@@ -2010,8 +1512,6 @@
+@@ -2010,8 +1516,6 @@
        struct async_struct *info = (struct async_struct *)tty->driver_data;
        unsigned long flags;
        
        save_flags(flags); cli();
        info->xmit.head = info->xmit.tail = 0;
        restore_flags(flags);
-@@ -2032,16 +1532,11 @@
+@@ -2032,16 +1536,11 @@
  {
        struct async_struct *info = (struct async_struct *)tty->driver_data;
  
        }
  }
  
-@@ -2064,9 +1559,6 @@
+@@ -2064,9 +1563,6 @@
               tty->ldisc.chars_in_buffer(tty));
  #endif
  
        if (I_IXOFF(tty))
                rs_send_xchar(tty, STOP_CHAR(tty));
  
-@@ -2089,9 +1581,6 @@
+@@ -2089,9 +1585,6 @@
               tty->ldisc.chars_in_buffer(tty));
  #endif
  
        if (I_IXOFF(tty)) {
                if (info->x_char)
                        info->x_char = 0;
-@@ -2134,7 +1623,6 @@
+@@ -2134,7 +1627,6 @@
        tmp.close_delay = state->close_delay;
        tmp.closing_wait = state->closing_wait;
        tmp.custom_divisor = state->custom_divisor;
        tmp.io_type = state->io_type;
        if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
                return -EFAULT;
-@@ -2160,8 +1648,7 @@
+@@ -2160,8 +1652,7 @@
                new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET;
  
        change_irq = new_serial.irq != state->irq;
    
        if (!capable(CAP_SYS_ADMIN)) {
                if (change_irq || change_port ||
-@@ -2198,7 +1685,6 @@
+@@ -2198,7 +1689,6 @@
        if (new_serial.type) {
                for (i = 0 ; i < NR_PORTS; i++)
                        if ((state != &rs_table[i]) &&
                            (rs_table[i].port == new_port) &&
                            rs_table[i].type)
                                return -EADDRINUSE;
-@@ -2220,18 +1706,11 @@
+@@ -2220,18 +1710,11 @@
        state->custom_divisor = new_serial.custom_divisor;
        state->close_delay = new_serial.close_delay * HZ/100;
        state->closing_wait = new_serial.closing_wait * HZ/100;
                release_region(state->port,8);
        }
        state->type = new_serial.type;
-@@ -2243,31 +1722,19 @@
+@@ -2243,31 +1726,19 @@
                shutdown(info);
                state->irq = new_serial.irq;
                info->port = state->port = new_port;
                        if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
                                info->tty->alt_speed = 57600;
                        if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-@@ -2276,7 +1743,6 @@
+@@ -2276,7 +1747,6 @@
                                info->tty->alt_speed = 230400;
                        if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
                                info->tty->alt_speed = 460800;
                        change_speed(info, 0);
                }
        } else
-@@ -2414,60 +1880,14 @@
+@@ -2414,60 +1884,14 @@
        return 0;
  }
  
        if (!CONFIGURED_SERIAL_PORT(info))
                return;
        save_flags(flags); cli();
-@@ -2478,121 +1898,6 @@
+@@ -2478,121 +1902,6 @@
        serial_out(info, UART_LCR, info->LCR);
        restore_flags(flags);
  }
  
  static int rs_ioctl(struct tty_struct *tty, struct file * file,
                    unsigned int cmd, unsigned long arg)
-@@ -2601,12 +1906,6 @@
+@@ -2601,12 +1910,6 @@
        struct async_icount cprev, cnow;        /* kernel counter temps */
        struct serial_icounter_struct icount;
        unsigned long flags;
  
        if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
            (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
-@@ -2616,45 +1915,6 @@
+@@ -2616,45 +1919,6 @@
        }
        
        switch (cmd) {
                case TIOCMGET:
                        return get_modem_info(info, (unsigned int *) arg);
                case TIOCMBIS:
-@@ -2667,9 +1927,6 @@
+@@ -2667,9 +1931,6 @@
                case TIOCSSERIAL:
                        return set_serial_info(info,
                                               (struct serial_struct *) arg);
                case TIOCSERGETLSR: /* Get line status register */
                        return get_lsr_info(info, (unsigned int *) arg);
  
-@@ -2679,15 +1936,6 @@
+@@ -2679,15 +1940,6 @@
                                return -EFAULT;
                        return 0;
                                
                /*
                 * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
                 * - mask passed in arg for lines of interest
-@@ -2754,6 +2002,39 @@
+@@ -2754,6 +2006,39 @@
                        printk ("TIOCSER?WILD ioctl obsolete, ignored.\n");
                        return 0;
  
                default:
                        return -ENOIOCTLCMD;
                }
-@@ -2801,18 +2082,6 @@
+@@ -2801,18 +2086,6 @@
                tty->hw_stopped = 0;
                rs_start(tty);
        }
  }
  
  /*
-@@ -2831,9 +2100,6 @@
+@@ -2831,9 +2104,6 @@
        struct serial_state *state;
        unsigned long flags;
  
        state = info->state;
        
        save_flags(flags); cli();
-@@ -2933,10 +2199,7 @@
+@@ -2933,10 +2203,7 @@
  {
        struct async_struct * info = (struct async_struct *)tty->driver_data;
        unsigned long orig_jiffies, char_time;
  
        if (info->state->type == PORT_UNKNOWN)
                return;
-@@ -2974,9 +2237,11 @@
+@@ -2974,9 +2241,11 @@
        printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time);
        printk("jiff=%lu...", jiffies);
  #endif
  #endif
                set_current_state(TASK_INTERRUPTIBLE);
                schedule_timeout(char_time);
-@@ -2986,8 +2251,9 @@
+@@ -2986,8 +2255,9 @@
                        break;
        }
  #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
  }
  
  /*
-@@ -2998,9 +2264,6 @@
+@@ -2998,9 +2268,6 @@
        struct async_struct * info = (struct async_struct *)tty->driver_data;
        struct serial_state *state = info->state;
        
        state = info->state;
        
        rs_flush_buffer(tty);
-@@ -3036,12 +2299,8 @@
+@@ -3036,12 +2303,8 @@
            (info->flags & ASYNC_CLOSING)) {
                if (info->flags & ASYNC_CLOSING)
                        interruptible_sleep_on(&info->close_wait);
        }
  
        /*
-@@ -3114,14 +2373,10 @@
+@@ -3114,14 +2377,10 @@
                set_current_state(TASK_INTERRUPTIBLE);
                if (tty_hung_up_p(filp) ||
                    !(info->flags & ASYNC_INITIALIZED)) {
                        break;
                }
                if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
-@@ -3223,16 +2478,12 @@
+@@ -3223,16 +2482,12 @@
        }
        tty->driver_data = info;
        info->tty = tty;
  
        /*
         *      This relies on lock_kernel() stuff so wants tidying for 2.5
-@@ -3254,12 +2505,8 @@
+@@ -3254,12 +2509,8 @@
            (info->flags & ASYNC_CLOSING)) {
                if (info->flags & ASYNC_CLOSING)
                        interruptible_sleep_on(&info->close_wait);
        }
  
        /*
-@@ -3313,17 +2560,14 @@
+@@ -3313,17 +2564,14 @@
        int     ret;
        unsigned long flags;
  
  
        /*
         * Figure out the current RS-232 lines
-@@ -3334,7 +2578,6 @@
+@@ -3334,7 +2582,6 @@
                info->magic = SERIAL_MAGIC;
                info->port = state->port;
                info->flags = state->flags;
                info->io_type = state->io_type;
                info->iomem_base = state->iomem_base;
                info->iomem_reg_shift = state->iomem_reg_shift;
-@@ -3389,13 +2632,13 @@
+@@ -3389,13 +2636,13 @@
  }
  
  static int rs_read_proc(char *page, char **start, off_t off, int count,
        for (i = 0; i < NR_PORTS && len < 4000; i++) {
                l = line_info(page + len, &rs_table[i]);
                len += l;
-@@ -3423,125 +2666,212 @@
+@@ -3423,125 +2670,212 @@
   */
  
  /*
  }
  
  /*
-@@ -3894,1760 +3224,8 @@
+@@ -3894,1762 +3228,10 @@
        restore_flags(flags);
  }
  
 -      p = ioremap(pci_resource_start(dev, 0), 0x80);
 -      writel(enable ? irq_config : 0x00, (unsigned long)p + 0x4c);
 -      iounmap(p);
--
 -      if (!enable)
 -              pci_write_config_byte(dev, PCI_COMMAND,
 -                                    data & ~pci_config);
 -      return 0;
 -}
--
--
--/*
+ /*
 - * SIIG serial cards have an PCI interface chip which also controls
 - * the UART clocking frequency. Each UART can be clocked independently
 - * (except cards equiped with 4 UARTs) and initial clocking settings
 -#endif
 -      { SPCI_FL_BASE0, 1, 115200,                       /* pbn_xircom_combo */
 -              0, 0, pci_xircom_fn },
+-
 -      { SPCI_FL_BASE2, 1, 460800,                        /* pbn_siig10x_0 */
 -              0, 0, pci_siig10x_fn },
 -      { SPCI_FL_BASE2, 1, 921600,                        /* pbn_siig10x_1 */
 -              0, 0, pci_siig20x_fn },
 -      { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600,   /* pbn_siix20x_4 */
 -              0, 0, pci_siig20x_fn },
+-
 -      { SPCI_FL_BASE0, 4, 921600, /* IOMEM */            /* pbn_computone_4 */
 -              0x40, 2, NULL, 0x200 },
 -      { SPCI_FL_BASE0, 6, 921600, /* IOMEM */            /* pbn_computone_6 */
 -      rs_table[i].closing_wait = req->closing_wait;
 -      return(0);
 -}
- /*
+-
+-/*
   * register_serial and unregister_serial allows for 16x50 serial ports to be
-@@ -5734,11 +3312,8 @@
+  * configured at run-time, to support PCMCIA modems.
+  */
+@@ -5734,11 +3316,8 @@
        }
        restore_flags(flags);
  
              state->iomem_base ? "iomem" : "port",
              state->iomem_base ? (unsigned long)state->iomem_base :
              state->port, state->irq, uart_config[state->type].name);
-@@ -5746,7 +3321,7 @@
+@@ -5746,7 +3325,7 @@
                           serial_driver.minor_start + state->line); 
        tty_register_devfs(&callout_driver, 0,
                           callout_driver.minor_start + state->line);
  }
  
  /**
-@@ -5785,7 +3360,6 @@
+@@ -5785,7 +3364,6 @@
        int i;
        struct async_struct *info;
  
        del_timer_sync(&serial_timer);
        save_flags(flags); cli();
          remove_bh(SERIAL_BH);
-@@ -5803,41 +3377,31 @@
+@@ -5803,41 +3381,31 @@
                        kfree(info);
                }
                if ((rs_table[i].type != PORT_UNKNOWN) && rs_table[i].port) {
  }
  
  module_init(rs_init);
-@@ -5946,7 +3510,7 @@
+@@ -5946,7 +3514,7 @@
        static struct async_struct *info;
        struct serial_state *state;
        unsigned cval;
        int     bits = 8;
        int     parity = 'n';
        int     doflow = 0;
-@@ -5954,6 +3518,8 @@
+@@ -5954,6 +3522,8 @@
        int     quot = 0;
        char    *s;
  
        if (options) {
                baud = simple_strtoul(options, NULL, 10);
                s = options;
-@@ -6028,19 +3594,12 @@
+@@ -6028,19 +3598,12 @@
        info->state = state;
        info->port = state->port;
        info->flags = state->flags;
        if (cflag & PARENB)
                cval |= UART_LCR_PARITY;
        if (!(cflag & PARODD))
-@@ -6082,10 +3641,15 @@
+@@ -6082,10 +3645,15 @@
   */
  void __init serial_console_init(void)
  {
  dep_tristate '  Mouse support' CONFIG_INPUT_MOUSEDEV $CONFIG_INPUT
  if [ "$CONFIG_INPUT_MOUSEDEV" != "n" ]; then
     int '   Horizontal screen resolution' CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024
---- linux-2.4.21/drivers/input/Makefile~ramses-keyb
+@@ -14,6 +16,7 @@
+ 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
+ dep_tristate '  MX1 touchscreen support' CONFIG_INPUT_MX1TS $CONFIG_INPUT_MOUSEDEV $CONFIG_ARCH_MX1ADS
+ endmenu
+--- linux-2.4.21/drivers/input/Makefile~bluetooth
 +++ linux-2.4.21/drivers/input/Makefile
 @@ -8,7 +8,7 @@
  
  
  # Object file lists.
  
-@@ -21,10 +21,12 @@
+@@ -21,10 +21,13 @@
  
  obj-$(CONFIG_INPUT)           += input.o
  obj-$(CONFIG_INPUT_KEYBDEV)   += keybdev.o
  obj-$(CONFIG_INPUT_MOUSEDEV)  += mousedev.o
  obj-$(CONFIG_INPUT_JOYDEV)    += joydev.o
  obj-$(CONFIG_INPUT_EVDEV)     += evdev.o
++obj-$(CONFIG_INPUT_UINPUT)    += uinput.o
  obj-$(CONFIG_INPUT_MX1TS)     += mx1ts.o
 +obj-$(CONFIG_INPUT_RAMSES_WEDGE) += wedge.o
  
  # The global Rules.make.
  
+--- linux-2.4.21/drivers/input/keybdev.c~bluetooth
++++ linux-2.4.21/drivers/input/keybdev.c
+@@ -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));
+-
+       }
+ }
+@@ -203,6 +205,12 @@
+ //    printk(KERN_INFO "keybdev.c: Adding keyboard: input%d\n", dev->number);
+       kbd_refresh_leds();
++      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;
+ }
 --- /dev/null
 +++ linux-2.4.21/drivers/input/ramses_cellmap.h
 @@ -0,0 +1,34 @@
 +
 +#endif
 --- /dev/null
++++ linux-2.4.21/drivers/input/uinput.c
+@@ -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);
++
+--- /dev/null
 +++ linux-2.4.21/drivers/input/wedge.c
 @@ -0,0 +1,241 @@
 +/*
 +
 +MODULE_DESCRIPTION("virtual keyboard wedge");
 +MODULE_LICENSE("GPL");
+--- linux-2.4.21/drivers/isdn/avmb1/capidrv.c~bluetooth
++++ linux-2.4.21/drivers/isdn/avmb1/capidrv.c
+@@ -523,13 +523,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++;
+ }
+@@ -2188,10 +2200,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;
+--- linux-2.4.21/drivers/isdn/avmb1/kcapi.c~bluetooth
++++ linux-2.4.21/drivers/isdn/avmb1/kcapi.c
+@@ -546,7 +546,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) {
+@@ -714,12 +720,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
+@@ -872,16 +882,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);
 --- linux-2.4.21/drivers/misc/Makefile~wedge
 +++ linux-2.4.21/drivers/misc/Makefile
 @@ -12,7 +12,7 @@
  
        /* pass ownership to the completion handler */
        urb->complete (urb);
+--- linux-2.4.21/drivers/usb/hid-core.c~bluetooth
++++ linux-2.4.21/drivers/usb/hid-core.c
+@@ -211,6 +211,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 */
 --- linux-2.4.21/drivers/usb/host/Config.in~usb-sl811
 +++ linux-2.4.21/drivers/usb/host/Config.in
 @@ -13,6 +13,9 @@
 +                              NEED_OP (t + 3 - 1);
 +                              goto copy_match;
 +
-+                      }
-+                      else if (t >= 32)
++                      }
++                      else if (t >= 32)
++                      {
++                              t &= 31;
++                              if (t == 0)
++                              {
++                                      NEED_IP (1);
++                                      while (*ip == 0)
++                                      {
++                                              t += 255;
++                                              ip++;
++                                              NEED_IP (1);
++                                      }
++                                      t += 31 + *ip++;
++                              }
++
++                              m_pos = op - 1;
++                              m_pos -= (ip[0] >> 2) + (ip[1] << 6);
++
++                              ip += 2;
++                      }
++                      else if (t >= 16)
++                      {
++                              m_pos = op;
++                              m_pos -= (t & 8) << 11;
++
++                              t &= 7;
++                              if (t == 0)
++                              {
++                                      NEED_IP (1);
++                                      while (*ip == 0)
++                                      {
++                                              t += 255;
++                                              ip++;
++                                              NEED_IP (1);
++                                      }
++                                      t += 7 + *ip++;
++                              }
++
++                              m_pos -= (ip[0] >> 2) + (ip[1] << 6);
++
++                              ip += 2;
++                              if (m_pos == op)
++                                      goto eof_found;
++                              m_pos -= 0x4000;
++                      }
++                      else
++                      {
++
++                              m_pos = op - 1;
++                              m_pos -= t >> 2;
++                              m_pos -= *ip++ << 2;
++                              TEST_LOOKBEHIND (m_pos, out);
++                              NEED_OP (2);
++                              *op++ = *m_pos++;
++                              *op++ = *m_pos;
++
++                              goto match_done;
++                      }
++
++                      TEST_LOOKBEHIND (m_pos, out);
++                      NEED_OP (t + 3 - 1);
++                      if (t >= 2 * 4 - (3 - 1)
++                          && PTR_ALIGNED2_4 (op, m_pos))
++                      {
++                              COPY4 (op, m_pos);
++                              op += 4;
++                              m_pos += 4;
++                              t -= 4 - (3 - 1);
++                              do
++                              {
++                                      COPY4 (op, m_pos);
++                                      op += 4;
++                                      m_pos += 4;
++                                      t -= 4;
++                              }
++                              while (t >= 4);
++                              if (t > 0)
++                                      do
++                                              *op++ = *m_pos++;
++                                      while (--t > 0);
++                      }
++                      else
++
++                      {
++                            copy_match:
++                              *op++ = *m_pos++;
++                              *op++ = *m_pos++;
++                              do
++                                      *op++ = *m_pos++;
++                              while (--t > 0);
++                      }
++
++                    match_done:
++                      t = ip[-2] & 3;
++
++                      if (t == 0)
++                              break;
++
++                    match_next:
++                      NEED_OP (t);
++                      NEED_IP (t + 1);
++                      do
++                              *op++ = *ip++;
++                      while (--t > 0);
++                      t = *ip++;
++              }
++      }
++      *out_len = op - out;
++      return LZO_E_EOF_NOT_FOUND;
++
++      eof_found:
++      *out_len = op - out;
++      return (ip == ip_end ? LZO_E_OK :
++              (ip <
++               ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
++
++      input_overrun:
++      *out_len = op - out;
++      return LZO_E_INPUT_OVERRUN;
++
++      output_overrun:
++      *out_len = op - out;
++      return LZO_E_OUTPUT_OVERRUN;
++
++      lookbehind_overrun:
++      *out_len = op - out;
++      return LZO_E_LOOKBEHIND_OVERRUN;
++}
++
++/* lzo1x_oo.ch */
++
++#define NO_LIT          LZO_UINT_MAX
++
++static void
++copy2 (lzo_byte * ip, const lzo_byte * m_pos, lzo_ptrdiff_t off)
++{
++      ip[0] = m_pos[0];
++      if (off == 1)
++              ip[1] = m_pos[0];
++      else
++              ip[1] = m_pos[1];
++}
++
++static void
++copy3 (lzo_byte * ip, const lzo_byte * m_pos, lzo_ptrdiff_t off)
++{
++      ip[0] = m_pos[0];
++      if (off == 1)
++      {
++              ip[2] = ip[1] = m_pos[0];
++      }
++      else if (off == 2)
++      {
++              ip[1] = m_pos[1];
++              ip[2] = m_pos[0];
++      }
++      else
++      {
++              ip[1] = m_pos[1];
++              ip[2] = m_pos[2];
++      }
++}
++
++static int
++lzo1x_optimize (lzo_byte * in, lzo_uint in_len,
++              lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem)
++{
++      register lzo_byte *op;
++      register lzo_byte *ip;
++      register lzo_uint t;
++      register lzo_byte *m_pos;
++      lzo_uint nl;
++      const lzo_byte *const ip_end = in + in_len;
++      const lzo_byte *const op_end = out + *out_len;
++      lzo_byte *litp = NULL;
++      lzo_uint lit = 0;
++      lzo_uint next_lit = NO_LIT;
++      long o_m1_a = 0, o_m1_b = 0, o_m2 = 0, o_m3_a = 0, o_m3_b = 0;
++
++      *out_len = 0;
++
++      op = out;
++      ip = in;
++
++      if (*ip > 17)
++      {
++              t = *ip++ - 17;
++              if (t < 4)
++                      goto match_next;
++              goto first_literal_run;
++      }
++
++      while (TEST_IP && TEST_OP)
++      {
++              t = *ip++;
++              if (t >= 16)
++                      goto match;
++              litp = ip - 1;
++              if (t == 0)
++              {
++                      t = 15;
++                      while (*ip == 0)
++                              t += 255, ip++;
++                      t += *ip++;
++              }
++              lit = t + 3;
++            copy_literal_run:
++              *op++ = *ip++;
++              *op++ = *ip++;
++              *op++ = *ip++;
++            first_literal_run:
++              do
++                      *op++ = *ip++;
++              while (--t > 0);
++
++              t = *ip++;
++
++              if (t >= 16)
++                      goto match;
++              m_pos = op - 1 - 0x800;
++              m_pos -= t >> 2;
++              m_pos -= *ip++ << 2;
++              *op++ = *m_pos++;
++              *op++ = *m_pos++;
++              *op++ = *m_pos++;
++              lit = 0;
++              goto match_done;
++
++              while (TEST_IP && TEST_OP)
++              {
++                      if (t < 16)
 +                      {
-+                              t &= 31;
-+                              if (t == 0)
-+                              {
-+                                      NEED_IP (1);
-+                                      while (*ip == 0)
-+                                      {
-+                                              t += 255;
-+                                              ip++;
-+                                              NEED_IP (1);
-+                                      }
-+                                      t += 31 + *ip++;
-+                              }
-+
 +                              m_pos = op - 1;
-+                              m_pos -= (ip[0] >> 2) + (ip[1] << 6);
++                              m_pos -= t >> 2;
++                              m_pos -= *ip++ << 2;
 +
-+                              ip += 2;
-+                      }
-+                      else if (t >= 16)
-+                      {
-+                              m_pos = op;
-+                              m_pos -= (t & 8) << 11;
++                              if (litp == NULL)
++                                      goto copy_m1;
 +
-+                              t &= 7;
-+                              if (t == 0)
++                              nl = ip[-2] & 3;
++                              if (nl == 0 && lit == 1 && ip[0] >= 16)
 +                              {
-+                                      NEED_IP (1);
-+                                      while (*ip == 0)
-+                                      {
-+                                              t += 255;
-+                                              ip++;
-+                                              NEED_IP (1);
-+                                      }
-+                                      t += 7 + *ip++;
++                                      next_lit = nl;
++                                      lit += 2;
++                                      *litp = LZO_BYTE ((*litp & ~3) | lit);
++                                      copy2 (ip - 2, m_pos, op - m_pos);
++                                      o_m1_a++;
 +                              }
++                              else if (nl == 0 && ip[0] < 16 && ip[0] != 0
++                                       && (lit + 2 + ip[0] < 16))
++                              {
++                                      t = *ip++;
++                                      *litp &= ~3;
++                                      copy2 (ip - 3 + 1, m_pos, op - m_pos);
++                                      litp += 2;
++                                      if (lit > 0)
++                                              memmove (litp + 1, litp, lit);
++                                      lit += 2 + t + 3;
++                                      *litp = LZO_BYTE (lit - 3);
 +
-+                              m_pos -= (ip[0] >> 2) + (ip[1] << 6);
-+
-+                              ip += 2;
-+                              if (m_pos == op)
-+                                      goto eof_found;
-+                              m_pos -= 0x4000;
++                                      o_m1_b++;
++                                      *op++ = *m_pos++;
++                                      *op++ = *m_pos++;
++                                      goto copy_literal_run;
++                              }
++                            copy_m1:
++                              *op++ = *m_pos++;
++                              *op++ = *m_pos++;
 +                      }
 +                      else
 +                      {
++                            match:
++                              if (t >= 64)
++                              {
++                                      m_pos = op - 1;
++                                      m_pos -= (t >> 2) & 7;
++                                      m_pos -= *ip++ << 3;
++                                      t = (t >> 5) - 1;
++                                      if (litp == NULL)
++                                              goto copy_m;
 +
-+                              m_pos = op - 1;
-+                              m_pos -= t >> 2;
-+                              m_pos -= *ip++ << 2;
-+                              TEST_LOOKBEHIND (m_pos, out);
-+                              NEED_OP (2);
-+                              *op++ = *m_pos++;
-+                              *op++ = *m_pos;
++                                      nl = ip[-2] & 3;
++                                      if (t == 1 && lit > 3 && nl == 0 &&
++                                          ip[0] < 16 && ip[0] != 0
++                                          && (lit + 3 + ip[0] < 16))
++                                      {
++                                              t = *ip++;
++                                              copy3 (ip - 1 - 2, m_pos,
++                                                     op - m_pos);
++                                              lit += 3 + t + 3;
++                                              *litp = LZO_BYTE (lit - 3);
++                                              o_m2++;
++                                              *op++ = *m_pos++;
++                                              *op++ = *m_pos++;
++                                              *op++ = *m_pos++;
++                                              goto copy_literal_run;
++                                      }
++                              }
++                              else
++                              {
++                                      if (t >= 32)
++                                      {
++                                              t &= 31;
++                                              if (t == 0)
++                                              {
++                                                      t = 31;
++                                                      while (*ip == 0)
++                                                              t += 255,
++                                                                      ip++;
++                                                      t += *ip++;
++                                              }
++                                              m_pos = op - 1;
++                                              m_pos -= *ip++ >> 2;
++                                              m_pos -= *ip++ << 6;
++                                      }
++                                      else
++                                      {
++                                              m_pos = op;
++                                              m_pos -= (t & 8) << 11;
++                                              t &= 7;
++                                              if (t == 0)
++                                              {
++                                                      t = 7;
++                                                      while (*ip == 0)
++                                                              t += 255,
++                                                                      ip++;
++                                                      t += *ip++;
++                                              }
++                                              m_pos -= *ip++ >> 2;
++                                              m_pos -= *ip++ << 6;
++                                              if (m_pos == op)
++                                                      goto eof_found;
++                                              m_pos -= 0x4000;
++                                      }
++                                      if (litp == NULL)
++                                              goto copy_m;
 +
-+                              goto match_done;
-+                      }
++                                      nl = ip[-2] & 3;
++                                      if (t == 1 && lit == 0 && nl == 0
++                                          && ip[0] >= 16)
++                                      {
++                                              next_lit = nl;
++                                              lit += 3;
++                                              *litp = LZO_BYTE ((*litp & ~3)
++                                                                | lit);
++                                              copy3 (ip - 3, m_pos,
++                                                     op - m_pos);
++                                              o_m3_a++;
++                                      }
++                                      else if (t == 1 && lit <= 3 && nl == 0
++                                               && ip[0] < 16 && ip[0] != 0
++                                               && (lit + 3 + ip[0] < 16))
++                                      {
++                                              t = *ip++;
++                                              *litp &= ~3;
++                                              copy3 (ip - 4 + 1, m_pos,
++                                                     op - m_pos);
++                                              litp += 2;
++                                              if (lit > 0)
++                                                      memmove (litp + 1,
++                                                               litp, lit);
++                                              lit += 3 + t + 3;
++                                              *litp = LZO_BYTE (lit - 3);
 +
-+                      TEST_LOOKBEHIND (m_pos, out);
-+                      NEED_OP (t + 3 - 1);
-+                      if (t >= 2 * 4 - (3 - 1)
-+                          && PTR_ALIGNED2_4 (op, m_pos))
-+                      {
-+                              COPY4 (op, m_pos);
-+                              op += 4;
-+                              m_pos += 4;
-+                              t -= 4 - (3 - 1);
-+                              do
-+                              {
-+                                      COPY4 (op, m_pos);
-+                                      op += 4;
-+                                      m_pos += 4;
-+                                      t -= 4;
-+                              }
-+                              while (t >= 4);
-+                              if (t > 0)
-+                                      do
++                                              o_m3_b++;
 +                                              *op++ = *m_pos++;
-+                                      while (--t > 0);
-+                      }
-+                      else
-+
-+                      {
-+                            copy_match:
++                                              *op++ = *m_pos++;
++                                              *op++ = *m_pos++;
++                                              goto copy_literal_run;
++                                      }
++                              }
++                            copy_m:
 +                              *op++ = *m_pos++;
 +                              *op++ = *m_pos++;
 +                              do
 +                      }
 +
 +                    match_done:
-+                      t = ip[-2] & 3;
-+
++                      if (next_lit == NO_LIT)
++                      {
++                              t = ip[-2] & 3;
++                              lit = t;
++                              litp = ip - 2;
++                      }
++                      else
++                              t = next_lit;
++                      next_lit = NO_LIT;
 +                      if (t == 0)
 +                              break;
-+
 +                    match_next:
-+                      NEED_OP (t);
-+                      NEED_IP (t + 1);
 +                      do
 +                              *op++ = *ip++;
 +                      while (--t > 0);
 +                      t = *ip++;
 +              }
 +      }
++
 +      *out_len = op - out;
 +      return LZO_E_EOF_NOT_FOUND;
 +
 +      return (ip == ip_end ? LZO_E_OK :
 +              (ip <
 +               ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
++}
 +
-+      input_overrun:
-+      *out_len = op - out;
-+      return LZO_E_INPUT_OVERRUN;
++/* interface to jffs2 follows */
 +
-+      output_overrun:
-+      *out_len = op - out;
-+      return LZO_E_OUTPUT_OVERRUN;
++#include "compr.h"
++#include <linux/jffs2.h>
 +
-+      lookbehind_overrun:
-+      *out_len = op - out;
-+      return LZO_E_LOOKBEHIND_OVERRUN;
++/*#define BLOCKSIZE           JFFS2_PAGE_SIZE
++#define OUTBLOCKSIZE  (BLOCKSIZE + BLOCKSIZE / 64 + 16 + 3)*/
++
++int jffs2_lzo_compress (unsigned char *input,
++                      unsigned char *output, uint32_t *sourcelen,
++                      uint32_t *dstlen, void *model);
++
++int jffs2_lzo_decompress (unsigned char *input,
++                        unsigned char *output, uint32_t sourcelen,
++                        uint32_t dstlen, void *model);
++
++static struct jffs2_compressor jffs2_lzo_comp = {
++      .priority = JFFS2_LZO_PRIORITY,
++      .name = "lzo",
++      .compr = JFFS2_COMPR_LZO,
++      .compress = &jffs2_lzo_compress,
++      .decompress = &jffs2_lzo_decompress,
++#ifdef JFFS2_LZO_DISABLED
++      .disabled = 1,
++#else
++      .disabled = 0,
++#endif
++};
++
++#ifdef JFFS2_LZO_1
++static int
++no_lzo1x_optimize (lzo_byte * src, lzo_uint src_len,
++                 lzo_byte * dst, lzo_uintp dst_len, lzo_voidp wrkmem)
++{
++      return 0;
++}
++#endif
++
++static lzo_compress_t lzo1x_compressor = lzo1x_999_compress;
++static lzo_optimize_t lzo1x_optimizer = lzo1x_optimize;
++#ifdef JFFS2_LZO_1
++static int lzo1x_compressor_type = 999;
++static int lzo1x_optimize_type = 1;
++#endif
++static unsigned long lzo1x_compressor_memsize = LZO1X_999_MEM_COMPRESS;
++
++static lzo_bytep wrkmem = NULL;               /* temporary buffer for compression, used by lzo       */
++static lzo_bytep cmprssmem = NULL;    /* temporary buffer for compression, used by interface */
++static int cmprssmem_size = 0;
++
++static int prepare_out_buf(uint32_t ssize, uint32_t dsize)
++{
++        uint32_t msize,req;
++
++        msize = (ssize>dsize)? ssize : dsize;
++        req   = (msize<<1) + 20;
++        if ((!cmprssmem)||(cmprssmem_size<req)) {
++                if (!cmprssmem) {
++                        vfree(cmprssmem);
++                        cmprssmem = NULL;
++                        cmprssmem_size = 0;
++                }
++                cmprssmem = vmalloc(req);
++                if (!cmprssmem) {
++                        return -1;
++                }
++                cmprssmem_size = req;
++        }
++        return 0;
++}
++
++int jffs2_lzo_compress (unsigned char *input,
++                      unsigned char *output, uint32_t *sourcelen,
++                      uint32_t *dstlen, void *model)
++{
++      lzo_uint csize = *dstlen; /*BLOCKSIZE;*/
++      lzo_uint isize = *sourcelen;
++      int retval;
++
++        if (prepare_out_buf(*sourcelen,*dstlen)) {
++                return -1;
++        }
++      if ((retval =
++           lzo1x_compressor (input, *sourcelen, cmprssmem, &csize,
++                             wrkmem)) != LZO_E_OK)
++      {
++              return retval;
++      }
++      else
++      {
++              retval = lzo1x_optimizer (cmprssmem, csize, input, &isize,
++                                        NULL);
++              if (csize <= *dstlen) {
++                      *dstlen = csize;
++                      memcpy (output, cmprssmem, csize);
++                      return retval;
++              } else {
++                      return -1;
++              }
++      }
++}
++
++int jffs2_lzo_decompress (unsigned char *input,
++                        unsigned char *output, uint32_t sourcelen,
++                        uint32_t dstlen, void *model)
++{
++      lzo_uint outlen = dstlen;
++      return lzo1x_decompress (input, sourcelen, output, &outlen, NULL);
++}
++
++int jffs2_lzo_init (void)
++{
++        wrkmem = (lzo_bytep) vmalloc(lzo1x_compressor_memsize);
++        if (!wrkmem) return -1;
++        jffs2_register_compressor(&jffs2_lzo_comp);
++        return 0;
++}
++
++void jffs2_lzo_exit (void)
++{
++      jffs2_unregister_compressor (&jffs2_lzo_comp);
++      if (cmprssmem) vfree(cmprssmem);
++        vfree(wrkmem);
++}
+--- linux-2.4.21/fs/jffs2/compr_rtime.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/compr_rtime.c
+@@ -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.
+- *
+- * 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.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $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
+  *
+@@ -49,12 +25,14 @@
+ #include <linux/types.h>
+ #include <linux/errno.h>
+ #include <linux/string.h> 
++#include <linux/jffs2.h> 
++#include "compr.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, void *model)
+ {
+-      int positions[256];
++      short positions[256];
+       int outpos = 0;
+       int pos=0;
+@@ -91,10 +69,10 @@
+ }                
+-void rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
+-                    __u32 srclen, __u32 destlen)
++int jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
++                    uint32_t srclen, uint32_t destlen, void *model)
+ {
+-      int positions[256];
++      short positions[256];
+       int outpos = 0;
+       int pos=0;
+       
+@@ -123,6 +101,28 @@
+                       }
+               }
+       }               
++        return 0;
+ }                
++static struct jffs2_compressor jffs2_rtime_comp = {
++    .priority = JFFS2_RTIME_PRIORITY,
++    .name = "rtime",
++    .compr = JFFS2_COMPR_RTIME,
++    .compress = &jffs2_rtime_compress,
++    .decompress = &jffs2_rtime_decompress,
++#ifdef JFFS2_RTIME_DISABLED
++    .disabled = 1,
++#else
++    .disabled = 0,
++#endif
++};
++
++int jffs2_rtime_init(void)
++{
++    return jffs2_register_compressor(&jffs2_rtime_comp);
++}
++void jffs2_rtime_exit(void)
++{
++    jffs2_unregister_compressor(&jffs2_rtime_comp);
++}
+--- linux-2.4.21/fs/jffs2/compr_rubin.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/compr_rubin.c
+@@ -1,49 +1,25 @@
+ /*
+  * 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.
+- *
+- * 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.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
+  
+ #include <linux/string.h>
+ #include <linux/types.h>
++#include <linux/jffs2.h>
+ #include "compr_rubin.h"
+ #include "histo_mips.h"
++#include "compr.h"
+-
+-
+-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, void *model)
+ {
+       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, void *model)
+ {
+       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,15 @@
+ }                
+-void rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, 
+-                 __u32 sourcelen, __u32 dstlen)
++int jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, 
++                 uint32_t sourcelen, uint32_t dstlen, void *model)
+ {
+       rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
++        return 0;
+ }
+-void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, 
+-                 __u32 sourcelen, __u32 dstlen)
++int jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, 
++                 uint32_t sourcelen, uint32_t dstlen, void *model)
+ {
+       int bits[8];
+       int c;
+@@ -346,4 +323,51 @@
+               bits[c] = data_in[c];
+       rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen);
++        return 0;
 +}
 +
-+/* lzo1x_oo.ch */
-+
-+#define NO_LIT          LZO_UINT_MAX
++static struct jffs2_compressor jffs2_rubinmips_comp = {
++    .priority = JFFS2_RUBINMIPS_PRIORITY,
++    .name = "rubinmips",
++    .compr = JFFS2_COMPR_DYNRUBIN,
++    .compress = NULL, /*&jffs2_rubinmips_compress,*/
++    .decompress = &jffs2_rubinmips_decompress,
++#ifdef JFFS2_RUBINMIPS_DISABLED
++    .disabled = 1,
++#else
++    .disabled = 0,
++#endif
++};
 +
-+static void
-+copy2 (lzo_byte * ip, const lzo_byte * m_pos, lzo_ptrdiff_t off)
++int jffs2_rubinmips_init(void)
 +{
-+      ip[0] = m_pos[0];
-+      if (off == 1)
-+              ip[1] = m_pos[0];
-+      else
-+              ip[1] = m_pos[1];
++    return jffs2_register_compressor(&jffs2_rubinmips_comp);
 +}
 +
-+static void
-+copy3 (lzo_byte * ip, const lzo_byte * m_pos, lzo_ptrdiff_t off)
++void jffs2_rubinmips_exit(void)
 +{
-+      ip[0] = m_pos[0];
-+      if (off == 1)
-+      {
-+              ip[2] = ip[1] = m_pos[0];
-+      }
-+      else if (off == 2)
-+      {
-+              ip[1] = m_pos[1];
-+              ip[2] = m_pos[0];
-+      }
-+      else
-+      {
-+              ip[1] = m_pos[1];
-+              ip[2] = m_pos[2];
-+      }
++    jffs2_unregister_compressor(&jffs2_rubinmips_comp);
 +}
 +
-+static int
-+lzo1x_optimize (lzo_byte * in, lzo_uint in_len,
-+              lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem)
-+{
-+      register lzo_byte *op;
-+      register lzo_byte *ip;
-+      register lzo_uint t;
-+      register lzo_byte *m_pos;
-+      lzo_uint nl;
-+      const lzo_byte *const ip_end = in + in_len;
-+      const lzo_byte *const op_end = out + *out_len;
-+      lzo_byte *litp = NULL;
-+      lzo_uint lit = 0;
-+      lzo_uint next_lit = NO_LIT;
-+      long o_m1_a = 0, o_m1_b = 0, o_m2 = 0, o_m3_a = 0, o_m3_b = 0;
-+
-+      *out_len = 0;
-+
-+      op = out;
-+      ip = in;
-+
-+      if (*ip > 17)
-+      {
-+              t = *ip++ - 17;
-+              if (t < 4)
-+                      goto match_next;
-+              goto first_literal_run;
-+      }
-+
-+      while (TEST_IP && TEST_OP)
-+      {
-+              t = *ip++;
-+              if (t >= 16)
-+                      goto match;
-+              litp = ip - 1;
-+              if (t == 0)
-+              {
-+                      t = 15;
-+                      while (*ip == 0)
-+                              t += 255, ip++;
-+                      t += *ip++;
-+              }
-+              lit = t + 3;
-+            copy_literal_run:
-+              *op++ = *ip++;
-+              *op++ = *ip++;
-+              *op++ = *ip++;
-+            first_literal_run:
-+              do
-+                      *op++ = *ip++;
-+              while (--t > 0);
-+
-+              t = *ip++;
-+
-+              if (t >= 16)
-+                      goto match;
-+              m_pos = op - 1 - 0x800;
-+              m_pos -= t >> 2;
-+              m_pos -= *ip++ << 2;
-+              *op++ = *m_pos++;
-+              *op++ = *m_pos++;
-+              *op++ = *m_pos++;
-+              lit = 0;
-+              goto match_done;
-+
-+              while (TEST_IP && TEST_OP)
-+              {
-+                      if (t < 16)
-+                      {
-+                              m_pos = op - 1;
-+                              m_pos -= t >> 2;
-+                              m_pos -= *ip++ << 2;
-+
-+                              if (litp == NULL)
-+                                      goto copy_m1;
++static struct jffs2_compressor jffs2_dynrubin_comp = {
++    .priority = JFFS2_DYNRUBIN_PRIORITY,
++    .name = "dynrubin",
++    .compr = JFFS2_COMPR_RUBINMIPS,
++    .compress = jffs2_dynrubin_compress,
++    .decompress = &jffs2_dynrubin_decompress,
++#ifdef JFFS2_DYNRUBIN_DISABLED
++    .disabled = 1,
++#else
++    .disabled = 0,
++#endif
++};
 +
-+                              nl = ip[-2] & 3;
-+                              if (nl == 0 && lit == 1 && ip[0] >= 16)
-+                              {
-+                                      next_lit = nl;
-+                                      lit += 2;
-+                                      *litp = LZO_BYTE ((*litp & ~3) | lit);
-+                                      copy2 (ip - 2, m_pos, op - m_pos);
-+                                      o_m1_a++;
-+                              }
-+                              else if (nl == 0 && ip[0] < 16 && ip[0] != 0
-+                                       && (lit + 2 + ip[0] < 16))
-+                              {
-+                                      t = *ip++;
-+                                      *litp &= ~3;
-+                                      copy2 (ip - 3 + 1, m_pos, op - m_pos);
-+                                      litp += 2;
-+                                      if (lit > 0)
-+                                              memmove (litp + 1, litp, lit);
-+                                      lit += 2 + t + 3;
-+                                      *litp = LZO_BYTE (lit - 3);
++int jffs2_dynrubin_init(void)
++{
++    return jffs2_register_compressor(&jffs2_dynrubin_comp);
++}
 +
-+                                      o_m1_b++;
-+                                      *op++ = *m_pos++;
-+                                      *op++ = *m_pos++;
-+                                      goto copy_literal_run;
-+                              }
-+                            copy_m1:
-+                              *op++ = *m_pos++;
-+                              *op++ = *m_pos++;
-+                      }
-+                      else
-+                      {
-+                            match:
-+                              if (t >= 64)
-+                              {
-+                                      m_pos = op - 1;
-+                                      m_pos -= (t >> 2) & 7;
-+                                      m_pos -= *ip++ << 3;
-+                                      t = (t >> 5) - 1;
-+                                      if (litp == NULL)
-+                                              goto copy_m;
++void jffs2_dynrubin_exit(void)
++{
++    jffs2_unregister_compressor(&jffs2_dynrubin_comp);
+ }
+--- linux-2.4.21/fs/jffs2/compr_rubin.h~mtd-cvs
++++ linux-2.4.21/fs/jffs2/compr_rubin.h
+@@ -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);
+--- linux-2.4.21/fs/jffs2/compr_zlib.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/compr_zlib.c
+@@ -1,51 +1,28 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001, 2002 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * 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.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * 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.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $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"
++#include "compr.h"
+       /* Plan: call deflate() with avail_in == *sourcelen, 
+               avail_out = *dstlen - 12 and flush == Z_FINISH. 
+@@ -58,120 +35,184 @@
+ 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;
+-int __init jffs2_zlib_init(void)
++#ifdef __KERNEL__ /* Linux-only */
++#include <linux/vmalloc.h>
++#include <linux/init.h>
 +
-+                                      nl = ip[-2] & 3;
-+                                      if (t == 1 && lit > 3 && nl == 0 &&
-+                                          ip[0] < 16 && ip[0] != 0
-+                                          && (lit + 3 + ip[0] < 16))
-+                                      {
-+                                              t = *ip++;
-+                                              copy3 (ip - 1 - 2, m_pos,
-+                                                     op - m_pos);
-+                                              lit += 3 + t + 3;
-+                                              *litp = LZO_BYTE (lit - 3);
-+                                              o_m2++;
-+                                              *op++ = *m_pos++;
-+                                              *op++ = *m_pos++;
-+                                              *op++ = *m_pos++;
-+                                              goto copy_literal_run;
-+                                      }
-+                              }
-+                              else
-+                              {
-+                                      if (t >= 32)
-+                                      {
-+                                              t &= 31;
-+                                              if (t == 0)
-+                                              {
-+                                                      t = 31;
-+                                                      while (*ip == 0)
-+                                                              t += 255,
-+                                                                      ip++;
-+                                                      t += *ip++;
-+                                              }
-+                                              m_pos = op - 1;
-+                                              m_pos -= *ip++ >> 2;
-+                                              m_pos -= *ip++ << 6;
-+                                      }
-+                                      else
-+                                      {
-+                                              m_pos = op;
-+                                              m_pos -= (t & 8) << 11;
-+                                              t &= 7;
-+                                              if (t == 0)
-+                                              {
-+                                                      t = 7;
-+                                                      while (*ip == 0)
-+                                                              t += 255,
-+                                                                      ip++;
-+                                                      t += *ip++;
-+                                              }
-+                                              m_pos -= *ip++ >> 2;
-+                                              m_pos -= *ip++ << 6;
-+                                              if (m_pos == op)
-+                                                      goto eof_found;
-+                                              m_pos -= 0x4000;
-+                                      }
-+                                      if (litp == NULL)
-+                                              goto copy_m;
++static int __init alloc_workspaces(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()));
+       return 0;
+ }
+-void jffs2_zlib_exit(void)
++static void free_workspaces(void)
+ {
+-      vfree(deflate_workspace);
+-      vfree(inflate_workspace);
++      vfree(def_strm.workspace);
++      vfree(inf_strm.workspace);
+ }
++#else
++#define alloc_workspaces() (0)
++#define free_workspaces() do { } while(0)
++#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, void *model)
+ {
+-      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);
 +
-+                                      nl = ip[-2] & 3;
-+                                      if (t == 1 && lit == 0 && nl == 0
-+                                          && ip[0] >= 16)
-+                                      {
-+                                              next_lit = nl;
-+                                              lit += 3;
-+                                              *litp = LZO_BYTE ((*litp & ~3)
-+                                                                | lit);
-+                                              copy3 (ip - 3, m_pos,
-+                                                     op - m_pos);
-+                                              o_m3_a++;
-+                                      }
-+                                      else if (t == 1 && lit <= 3 && nl == 0
-+                                               && ip[0] < 16 && ip[0] != 0
-+                                               && (lit + 3 + ip[0] < 16))
-+                                      {
-+                                              t = *ip++;
-+                                              *litp &= ~3;
-+                                              copy3 (ip - 4 + 1, m_pos,
-+                                                     op - m_pos);
-+                                              litp += 2;
-+                                              if (lit > 0)
-+                                                      memmove (litp + 1,
-+                                                               litp, lit);
-+                                              lit += 3 + t + 3;
-+                                              *litp = LZO_BYTE (lit - 3);
+       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)
++int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
++                    uint32_t srclen, uint32_t destlen, void *model)
+ {
+-      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;
 +
-+                                              o_m3_b++;
-+                                              *op++ = *m_pos++;
-+                                              *op++ = *m_pos++;
-+                                              *op++ = *m_pos++;
-+                                              goto copy_literal_run;
-+                                      }
-+                              }
-+                            copy_m:
-+                              *op++ = *m_pos++;
-+                              *op++ = *m_pos++;
-+                              do
-+                                      *op++ = *m_pos++;
-+                              while (--t > 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)) {
 +
-+                    match_done:
-+                      if (next_lit == NO_LIT)
-+                      {
-+                              t = ip[-2] & 3;
-+                              lit = t;
-+                              litp = ip - 2;
-+                      }
-+                      else
-+                              t = next_lit;
-+                      next_lit = NO_LIT;
-+                      if (t == 0)
-+                              break;
-+                    match_next:
-+                      do
-+                              *op++ = *ip++;
-+                      while (--t > 0);
-+                      t = *ip++;
-+              }
++              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"));
 +      }
 +
-+      *out_len = op - out;
-+      return LZO_E_EOF_NOT_FOUND;
 +
-+      eof_found:
-+      *out_len = op - out;
-+      return (ip == ip_end ? LZO_E_OK :
-+              (ip <
-+               ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
++      if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) {
+               printk(KERN_WARNING "inflateInit failed\n");
+               up(&inflate_sem);
+-              return;
++              return 1;
+       }
+-      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);
++        return 0;
 +}
 +
-+/* interface to jffs2 follows */
-+
-+#include "compr.h"
-+#include <linux/jffs2.h>
-+
-+/*#define BLOCKSIZE           JFFS2_PAGE_SIZE
-+#define OUTBLOCKSIZE  (BLOCKSIZE + BLOCKSIZE / 64 + 16 + 3)*/
-+
-+int jffs2_lzo_compress (unsigned char *input,
-+                      unsigned char *output, uint32_t *sourcelen,
-+                      uint32_t *dstlen, void *model);
-+
-+int jffs2_lzo_decompress (unsigned char *input,
-+                        unsigned char *output, uint32_t sourcelen,
-+                        uint32_t dstlen, void *model);
-+
-+static struct jffs2_compressor jffs2_lzo_comp = {
-+      .priority = JFFS2_LZO_PRIORITY,
-+      .name = "lzo",
-+      .compr = JFFS2_COMPR_LZO,
-+      .compress = &jffs2_lzo_compress,
-+      .decompress = &jffs2_lzo_decompress,
-+#ifdef JFFS2_LZO_DISABLED
-+      .disabled = 1,
++static struct jffs2_compressor jffs2_zlib_comp = {
++    .priority = JFFS2_ZLIB_PRIORITY,
++    .name = "zlib",
++    .compr = JFFS2_COMPR_ZLIB,
++    .compress = &jffs2_zlib_compress,
++    .decompress = &jffs2_zlib_decompress,
++#ifdef JFFS2_ZLIB_DISABLED
++    .disabled = 1,
 +#else
-+      .disabled = 0,
++    .disabled = 0,
 +#endif
 +};
 +
-+#ifdef JFFS2_LZO_1
-+static int
-+no_lzo1x_optimize (lzo_byte * src, lzo_uint src_len,
-+                 lzo_byte * dst, lzo_uintp dst_len, lzo_voidp wrkmem)
++int __init jffs2_zlib_init(void)
 +{
-+      return 0;
++    int ret;
++
++    ret = alloc_workspaces();
++    if (ret)
++        return ret;
++
++    ret = jffs2_register_compressor(&jffs2_zlib_comp);
++    if (ret)
++        free_workspaces();
++
++    return ret;
 +}
-+#endif
 +
-+static lzo_compress_t lzo1x_compressor = lzo1x_999_compress;
-+static lzo_optimize_t lzo1x_optimizer = lzo1x_optimize;
-+#ifdef JFFS2_LZO_1
-+static int lzo1x_compressor_type = 999;
-+static int lzo1x_optimize_type = 1;
++void jffs2_zlib_exit(void)
++{
++    jffs2_unregister_compressor(&jffs2_zlib_comp);
++    free_workspaces();
+ }
+--- linux-2.4.21/fs/jffs2/crc32.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/crc32.c
+@@ -37,11 +37,11 @@
+  *      polynomial $edb88320
+  */
+-/* $Id$ */
++/* $Id$ */
+ #include "crc32.h"
+-const __u32 crc32_table[256] = {
++const uint32_t crc32_table[256] = {
+       0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+       0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+       0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+--- linux-2.4.21/fs/jffs2/crc32.h~mtd-cvs
++++ linux-2.4.21/fs/jffs2/crc32.h
+@@ -1,16 +1,16 @@
+ #ifndef CRC32_H
+ #define CRC32_H
+-/* $Id$ */
++/* $Id$ */
+ #include <linux/types.h>
+-extern const __u32 crc32_table[256];
++extern const uint32_t crc32_table[256];
+ /* Return a 32-bit CRC of the contents of the buffer. */
+-static inline __u32 
+-crc32(__u32 val, const void *ss, int len)
++static inline uint32_t 
++crc32(uint32_t val, const void *ss, int len)
+ {
+       const unsigned char *s = ss;
+         while (--len >= 0)
+--- linux-2.4.21/fs/jffs2/dir.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/dir.c
+@@ -1,84 +1,73 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * 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.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * 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.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $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 "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 unsigned long lzo1x_compressor_memsize = LZO1X_999_MEM_COMPRESS;
+ 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 lzo_bytep wrkmem = NULL;               /* temporary buffer for compression, used by lzo       */
-+static lzo_bytep cmprssmem = NULL;    /* temporary buffer for compression, used by interface */
-+static int cmprssmem_size = 0;
++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,21 @@
+       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 +235,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 prepare_out_buf(uint32_t ssize, uint32_t dsize)
-+{
-+        uint32_t msize,req;
+ 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;
 +
-+        msize = (ssize>dsize)? ssize : dsize;
-+        req   = (msize<<1) + 20;
-+        if ((!cmprssmem)||(cmprssmem_size<req)) {
-+                if (!cmprssmem) {
-+                        vfree(cmprssmem);
-+                        cmprssmem = NULL;
-+                        cmprssmem_size = 0;
-+                }
-+                cmprssmem = vmalloc(req);
-+                if (!cmprssmem) {
-+                        return -1;
-+                }
-+                cmprssmem_size = req;
-+        }
-+        return 0;
-+}
++      ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, dentry->d_name.name, dentry->d_name.len);
 +
-+int jffs2_lzo_compress (unsigned char *input,
-+                      unsigned char *output, uint32_t *sourcelen,
-+                      uint32_t *dstlen, void *model)
-+{
-+      lzo_uint csize = *dstlen; /*BLOCKSIZE;*/
-+      lzo_uint isize = *sourcelen;
-+      int retval;
+       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,13 +295,12 @@
+       struct jffs2_full_dnode *fn;
+       struct jffs2_full_dirent *fd;
+       int namelen;
+-      __u32 alloclen, phys_ofs;
+-      __u32 writtenlen;
+-      int ret;
++      uint32_t alloclen, phys_ofs;
++      int ret, targetlen = strlen(target);
+       /* FIXME: If you care. We'd need to use frags for the target
+          if it grows much more than this */
+-      if (strlen(target) > 254)
++      if (targetlen > 254)
+               return -EINVAL;
+       ri = jffs2_alloc_raw_inode();
+@@ -537,7 +314,7 @@
+        * Just the node will do for now, though 
+        */
+       namelen = dentry->d_name.len;
+-      ret = jffs2_reserve_space(c, sizeof(*ri) + strlen(target), &phys_ofs, &alloclen, ALLOC_NORMAL);
++      ret = jffs2_reserve_space(c, sizeof(*ri) + targetlen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+       if (ret) {
+               jffs2_free_raw_inode(ri);
+@@ -556,15 +333,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 = targetlen;
++      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, targetlen));
++      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, targetlen, phys_ofs, ALLOC_NORMAL);
+       jffs2_free_raw_inode(ri);
+@@ -575,19 +353,26 @@
+               jffs2_clear_inode(inode);
+               return PTR_ERR(fn);
+       }
 +
-+        if (prepare_out_buf(*sourcelen,*dstlen)) {
-+                return -1;
-+        }
-+      if ((retval =
-+           lzo1x_compressor (input, *sourcelen, cmprssmem, &csize,
-+                             wrkmem)) != LZO_E_OK)
-+      {
-+              return retval;
-+      }
-+      else
-+      {
-+              retval = lzo1x_optimizer (cmprssmem, csize, input, &isize,
-+                                        NULL);
-+              if (csize <= *dstlen) {
-+                      *dstlen = csize;
-+                      memcpy (output, cmprssmem, csize);
-+                      return retval;
-+              } else {
-+                      return -1;
-+              }
++      /* We use f->dents field to store the target path. */
++      f->dents = kmalloc(targetlen + 1, GFP_KERNEL);
++      if (!f->dents) {
++              printk(KERN_WARNING "Can't allocate %d bytes of memory\n", targetlen + 1);
++              up(&f->sem);
++              jffs2_complete_reservation(c);
++              jffs2_clear_inode(inode);
++              return -ENOMEM;
 +      }
-+}
-+
-+int jffs2_lzo_decompress (unsigned char *input,
-+                        unsigned char *output, uint32_t sourcelen,
-+                        uint32_t dstlen, void *model)
-+{
-+      lzo_uint outlen = dstlen;
-+      return lzo1x_decompress (input, sourcelen, output, &outlen, NULL);
-+}
 +
-+int jffs2_lzo_init (void)
-+{
-+        wrkmem = (lzo_bytep) vmalloc(lzo1x_compressor_memsize);
-+        if (!wrkmem) return -1;
-+        jffs2_register_compressor(&jffs2_lzo_comp);
-+        return 0;
-+}
++      memcpy(f->dents, target, targetlen + 1);
++      D1(printk(KERN_DEBUG "jffs2_symlink: symlink's target '%s' cached\n", (char *)f->dents));
 +
-+void jffs2_lzo_exit (void)
-+{
-+      jffs2_unregister_compressor (&jffs2_lzo_comp);
-+      if (cmprssmem) vfree(cmprssmem);
-+        vfree(wrkmem);
-+}
---- linux-2.4.21/fs/jffs2/compr_rtime.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/compr_rtime.c
-@@ -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.
-- *
-- * 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.
-+ * For licensing information, see the file 'LICENCE' in this directory.
-  *
-- * $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
-  *
-@@ -49,12 +25,14 @@
- #include <linux/types.h>
- #include <linux/errno.h>
- #include <linux/string.h> 
-+#include <linux/jffs2.h> 
-+#include "compr.h"
+       /* No data here. Only a metadata node, which will be 
+          obsoleted by the first data write
+       */
+       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 +380,6 @@
+                       jffs2_clear_inode(inode);
+                       return ret;
+               }
+-      }
+       rd = jffs2_alloc_raw_dirent();
+       if (!rd) {
+@@ -608,41 +392,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->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 = dir_i->i_ino;
+-      rd->version = ++dir_f->highest_version;
+-      rd->ino = inode->i_ino;
+-      rd->mctime = CURRENT_TIME;
++      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);
+-
+-      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);
+       }
  
- /* _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, void *model)
- {
--      int positions[256];
-+      short positions[256];
-       int outpos = 0;
-       int pos=0;
+-      dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
++      dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
  
-@@ -91,10 +69,10 @@
- }                
+       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);
  
--void rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
--                    __u32 srclen, __u32 destlen)
-+int jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
-+                    uint32_t srclen, uint32_t destlen, void *model)
- {
--      int positions[256];
-+      short positions[256];
-       int outpos = 0;
-       int pos=0;
-       
-@@ -123,6 +101,28 @@
-                       }
-               }
-       }               
-+        return 0;
- }                
+       d_instantiate(dentry, inode);
+       return 0;
+@@ -659,8 +444,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;
  
-+static struct jffs2_compressor jffs2_rtime_comp = {
-+    .priority = JFFS2_RTIME_PRIORITY,
-+    .name = "rtime",
-+    .compr = JFFS2_COMPR_RTIME,
-+    .compress = &jffs2_rtime_compress,
-+    .decompress = &jffs2_rtime_decompress,
-+#ifdef JFFS2_RTIME_DISABLED
-+    .disabled = 1,
-+#else
-+    .disabled = 0,
-+#endif
-+};
-+
-+int jffs2_rtime_init(void)
-+{
-+    return jffs2_register_compressor(&jffs2_rtime_comp);
-+}
+       mode |= S_IFDIR;
+@@ -692,13 +476,15 @@
  
-+void jffs2_rtime_exit(void)
-+{
-+    jffs2_unregister_compressor(&jffs2_rtime_comp);
-+}
---- linux-2.4.21/fs/jffs2/compr_rubin.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/compr_rubin.c
-@@ -1,49 +1,25 @@
- /*
-  * 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.
-- *
-- * 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.
-+ * For licensing information, see the file 'LICENCE' in this directory.
-  *
-- * $Id$
-+ * $Id$
-  *
-  */
+       inode->i_op = &jffs2_dir_inode_operations;
+       inode->i_fop = &jffs2_dir_operations;
++      /* Directories get nlink 2 at start */
++      inode->i_nlink = 2;
  
-  
- #include <linux/string.h>
- #include <linux/types.h>
-+#include <linux/jffs2.h>
- #include "compr_rubin.h"
- #include "histo_mips.h"
-+#include "compr.h"
+       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 +501,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 +508,6 @@
+                       jffs2_clear_inode(inode);
+                       return ret;
+               }
+-      }
+       
+       rd = jffs2_alloc_raw_dirent();
+       if (!rd) {
+@@ -742,41 +520,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->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 = dir_i->i_ino;
+-      rd->version = ++dir_f->highest_version;
+-      rd->ino = inode->i_ino;
+-      rd->mctime = CURRENT_TIME;
++      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);
 -
--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;
+-      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);
+       }
  
-@@ -56,7 +32,7 @@
- }
+-      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);
  
--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)
+       /* 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 +566,19 @@
  {
+       struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
+       struct jffs2_full_dirent *fd;
++      int ret;
  
-       long i0, i1;
-@@ -91,7 +67,7 @@
+       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 +588,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 +603,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 +630,15 @@
  
--void end_rubin(struct rubin_state *rs)
-+static void end_rubin(struct rubin_state *rs)
- {                             
+       f = JFFS2_INODE_INFO(inode);
  
-       int i;
-@@ -104,7 +80,7 @@
- }
+-      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);
  
--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);              
+       jffs2_free_raw_inode(ri);
  
-@@ -151,7 +127,7 @@
-       rs->rec_q = rec_q;
- }
+@@ -869,13 +655,6 @@
+       f->metadata = fn;
+       up(&f->sem);
  
--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 @@
+-      /* 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 +662,6 @@
+                       jffs2_clear_inode(inode);
+                       return ret;
+               }
+-      }
  
+       rd = jffs2_alloc_raw_dirent();
+       if (!rd) {
+@@ -896,44 +674,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->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));
  
--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, void *model)
- {
-       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, void *model)
- {
-       int bits[8];
-       unsigned char histo[256];
-       int i;
-       int ret;
--      __u32 mysrclen, mydstlen;
-+      uint32_t mysrclen, mydstlen;
+-      rd->pino = dir_i->i_ino;
+-      rd->version = ++dir_f->highest_version;
+-      rd->ino = inode->i_ino;
+-      rd->mctime = CURRENT_TIME;
++      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;
  
-       mysrclen = *sourcelen;
-       mydstlen = *dstlen - 8;
-@@ -315,8 +291,8 @@
-       return 0;
- }
+       /* XXX: This is ugly. */
+       rd->type = (mode & S_IFMT) >> 12;
  
--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,15 @@
- }                
+-      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));
  
--void rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, 
--                 __u32 sourcelen, __u32 dstlen)
-+int jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, 
-+                 uint32_t sourcelen, uint32_t dstlen, void *model)
- {
-       rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
-+        return 0;
- }
+       jffs2_free_raw_dirent(rd);
  
--void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, 
--                 __u32 sourcelen, __u32 dstlen)
-+int jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, 
-+                 uint32_t sourcelen, uint32_t dstlen, void *model)
+       /* 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 +723,9 @@
+                         struct inode *new_dir_i, struct dentry *new_dentry)
  {
-       int bits[8];
-       int c;
-@@ -346,4 +323,51 @@
-               bits[c] = data_in[c];
+       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;
  
-       rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen);
-+        return 0;
-+}
-+
-+static struct jffs2_compressor jffs2_rubinmips_comp = {
-+    .priority = JFFS2_RUBINMIPS_PRIORITY,
-+    .name = "rubinmips",
-+    .compr = JFFS2_COMPR_DYNRUBIN,
-+    .compress = NULL, /*&jffs2_rubinmips_compress,*/
-+    .decompress = &jffs2_rubinmips_decompress,
-+#ifdef JFFS2_RUBINMIPS_DISABLED
-+    .disabled = 1,
-+#else
-+    .disabled = 0,
-+#endif
-+};
+       /* 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 +754,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;
 +
-+int jffs2_rubinmips_init(void)
-+{
-+    return jffs2_register_compressor(&jffs2_rubinmips_comp);
-+}
++      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);
 +
-+void jffs2_rubinmips_exit(void)
-+{
-+    jffs2_unregister_compressor(&jffs2_rubinmips_comp);
-+}
+       if (ret)
+               return ret;
+@@ -989,22 +778,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++;
 +
-+static struct jffs2_compressor jffs2_dynrubin_comp = {
-+    .priority = JFFS2_DYNRUBIN_PRIORITY,
-+    .name = "dynrubin",
-+    .compr = JFFS2_COMPR_RUBINMIPS,
-+    .compress = jffs2_dynrubin_compress,
-+    .decompress = &jffs2_dynrubin_decompress,
-+#ifdef JFFS2_DYNRUBIN_DISABLED
-+    .disabled = 1,
-+#else
-+    .disabled = 0,
-+#endif
-+};
+       /* 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);
 +
-+int jffs2_dynrubin_init(void)
-+{
-+    return jffs2_register_compressor(&jffs2_dynrubin_comp);
-+}
++      /* 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;
++      }
 +
-+void jffs2_dynrubin_exit(void)
-+{
-+    jffs2_unregister_compressor(&jffs2_dynrubin_comp);
++      if (S_ISDIR(old_dentry->d_inode->i_mode))
++              old_dir_i->i_nlink--;
++
++      return 0;
  }
---- linux-2.4.21/fs/jffs2/compr_rubin.h~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/compr_rubin.h
-@@ -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);
---- linux-2.4.21/fs/jffs2/compr_zlib.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/compr_zlib.c
-@@ -1,51 +1,28 @@
+--- linux-2.4.21/fs/jffs2/erase.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/erase.c
+@@ -1,68 +1,63 @@
  /*
   * JFFS2 -- Journalling Flash File System, Version 2.
   *
-- * Copyright (C) 2001, 2002 Red Hat, Inc.
+- * Copyright (C) 2001 Red Hat, Inc.
 - *
 - * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
 - *
 + * $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/mtd/mtd.h>
 -#include <linux/jffs2.h>
- #include <linux/zlib.h>
-+#include <linux/zutil.h>
-+#include <asm/semaphore.h>
+-#include <linux/interrupt.h>
++#include <linux/compiler.h>
++#include <linux/crc32.h>
++#include <linux/sched.h>
++#include <linux/pagemap.h>
  #include "nodelist.h"
-+#include "compr.h"
-       /* Plan: call deflate() with avail_in == *sourcelen, 
-               avail_out = *dstlen - 12 and flush == Z_FINISH. 
-@@ -58,120 +35,184 @@
+-#include "crc32.h"
  
- 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;
+ 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, uint32_t bad_offset);
++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);
  
--int __init jffs2_zlib_init(void)
-+#ifdef __KERNEL__ /* Linux-only */
-+#include <linux/vmalloc.h>
-+#include <linux/init.h>
-+
-+static int __init alloc_workspaces(void)
+ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
  {
--      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;
+-      struct erase_info *instr;
+       int ret;
++      uint32_t bad_offset;
++#ifdef __ECOS
++       ret = jffs2_flash_erase(c, jeb);
++       if (!ret) {
++               jffs2_erase_succeeded(c, jeb);
++               return;
++       }
++       bad_offset = jeb->offset;
++#else /* Linux */
++      struct erase_info *instr;
++      D1(printk(KERN_DEBUG "jffs2_erase_block(): erase block %#x (range %#x-%#x)\n", jeb->offset, jeb->offset, jeb->offset + c->sector_size));
+       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;
        }
-       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;
+@@ -73,23 +68,29 @@
+       instr->len = c->sector_size;
+       instr->callback = jffs2_erase_callback;
+       instr->priv = (unsigned long)(&instr[1]);
++      instr->fail_addr = 0xffffffff;
+       
+       ((struct erase_priv_struct *)instr->priv)->jeb = jeb;
+       ((struct erase_priv_struct *)instr->priv)->c = c;
+       ret = c->mtd->erase(c->mtd, instr);
+-      if (!ret) {
++      if (!ret)
+               return;
+-      }
++
++      bad_offset = instr->fail_addr;
++      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;
        }
-       D1(printk(KERN_DEBUG "Allocated %d bytes for inflate workspace\n", zlib_inflate_workspacesize()));
-       return 0;
- }
  
--void jffs2_zlib_exit(void)
-+static void free_workspaces(void)
- {
--      vfree(deflate_workspace);
--      vfree(inflate_workspace);
-+      vfree(def_strm.workspace);
-+      vfree(inf_strm.workspace);
+@@ -97,74 +98,119 @@
+               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, bad_offset);
  }
-+#else
-+#define alloc_workspaces() (0)
-+#define free_workspaces() do { } while(0)
-+#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, void *model)
+-void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
++void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
  {
--      z_stream strm;
-       int ret;
-       if (*dstlen <= STREAM_END_SPACE)
-               return -1;
+       struct jffs2_eraseblock *jeb;
  
-       down(&deflate_sem);
--      strm.workspace = deflate_workspace;
+-      spin_lock_bh(&c->erase_completion_lock);
+-      while (!list_empty(&c->erase_pending_list)) {
++      down(&c->erase_free_sem);
  
--      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;
-       }
+-              jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list);
++      spin_lock(&c->erase_completion_lock);
  
--      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;
+-              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;
++                      }
  
--      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);
++              } 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);
 +
-       if (ret != Z_STREAM_END) {
-               D1(printk(KERN_DEBUG "final deflate returned %d\n", ret));
--              return -1;
-+              ret = -1;
-+              goto out;
++              } 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);
+ }
  
--      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;
++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, uint32_t bad_offset)
++{
++      /* For NAND, if the failure did not occur at the device level for a
++         specific physical page, don't bother updating the bad block table. */
++      if (jffs2_cleanmarker_oob(c) && (bad_offset != 0xffffffff)) {
++              /* We had a device-level failure to erase.  Let's see if we've
++                 failed too many times. */
++              if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) {
++                      /* We'd like to give this block another try. */
++                      spin_lock(&c->erase_completion_lock);
++                      list_del(&jeb->list);
++                      list_add(&jeb->list, &c->erase_pending_list);
++                      c->erasing_size -= c->sector_size;
++                      c->dirty_size += c->sector_size;
++                      jeb->dirty_size = c->sector_size;
++                      spin_unlock(&c->erase_completion_lock);
++                      return;
++              }
 +      }
++
++      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 (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));
+       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, instr->fail_addr);
+       } 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 */
  
--      *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;
+ /* Hmmm. Maybe we should accept the extra space it takes and make
+    this a standard doubly-linked list? */
+@@ -187,7 +233,7 @@
+                       continue;
+               } 
+-              if (((*prev)->flash_offset & ~(c->sector_size -1)) == jeb->offset) {
++              if (SECTOR_ADDR((*prev)->flash_offset) == jeb->offset) {
+                       /* It's in the block we're erasing */
+                       struct jffs2_raw_node_ref *this;
+@@ -221,7 +267,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;
+@@ -231,11 +277,8 @@
+               printk("\n");
+       });
+-      if (ic->nodes == (void *)ic) {
+-              D1(printk(KERN_DEBUG "inocache for ino #%u is all gone now. Freeing\n", ic->ino));
++      if (ic->nodes == (void *)ic)
+               jffs2_del_ino_cache(c, ic);
+-              jffs2_free_inode_cache(ic);
+-      }
  }
  
--void zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
--                    __u32 srclen, __u32 destlen)
-+int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
-+                    uint32_t srclen, uint32_t destlen, void *model)
+ static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+@@ -256,118 +299,148 @@
+       jeb->last_node = NULL;
+ }
+-void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
+-{
+-      OFNI_BS_2SFFJ(c)->s_dirt = 1;
+-}
+-
+-void jffs2_mark_erased_blocks(struct jffs2_sb_info *c)
++static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
  {
--      z_stream strm;
+-      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;
-+      int wbits = MAX_WBITS;
-       down(&inflate_sem);
--      strm.workspace = inflate_workspace;
++      uint32_t bad_offset;
  
--      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"));
+-      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)) && (c->cleanmarker_size > 0)) {
+               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;
+               }
+-
 +      }
-+
-+
-+      if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) {
-               printk(KERN_WARNING "inflateInit failed\n");
-               up(&inflate_sem);
--              return;
-+              return 1;
-       }
--      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;
+               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;
  
--      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);
-+        return 0;
-+}
+                       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) {
++                      bad_offset = ofs;
 +
-+static struct jffs2_compressor jffs2_zlib_comp = {
-+    .priority = JFFS2_ZLIB_PRIORITY,
-+    .name = "zlib",
-+    .compr = JFFS2_COMPR_ZLIB,
-+    .compress = &jffs2_zlib_compress,
-+    .decompress = &jffs2_zlib_decompress,
-+#ifdef JFFS2_ZLIB_DISABLED
-+    .disabled = 1,
-+#else
-+    .disabled = 0,
-+#endif
-+};
++                      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)) {
+                                       /* It's OK. We know it's properly aligned */
+                                       unsigned long datum = *(unsigned long *)(&ebuf[i]);
+                                       if (datum + 1) {
+-                                              printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, ofs + i);
++                                      bad_offset += i;
++                                      printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, bad_offset);
+                                       bad: 
++                                      if ((!jffs2_cleanmarker_oob(c)) && (c->cleanmarker_size > 0))
+                                               jffs2_free_raw_node_ref(marker_ref);
+                                               kfree(ebuf);
+                                       bad2:
+-                                              spin_lock_bh(&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);
+-                                              wake_up(&c->erase_wait);
++                                      spin_lock(&c->erase_completion_lock);
++                                      /* Stick it on a list (any list) so
++                                         erase_failed can take it right off
++                                         again.  Silly, but shouldn't happen
++                                         often. */
++                                      list_add(&jeb->list, &c->erasing_list);
++                                      spin_unlock(&c->erase_completion_lock);
++                                      jffs2_erase_failed(c, jeb, bad_offset);
+                                               return;
+                                       }
+                               }
+                               ofs += readlen;
++                      cond_resched();
+                       }
+                       kfree(ebuf);
+               }
+                                       
++      bad_offset = jeb->offset;
 +
-+int __init jffs2_zlib_init(void)
-+{
-+    int ret;
+               /* 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)) {
 +
-+    ret = alloc_workspaces();
-+    if (ret)
-+        return ret;
++              if (jffs2_write_nand_cleanmarker(c, jeb))
++                      goto bad2;
++                      
++              jeb->first_node = jeb->last_node = NULL;
 +
-+    ret = jffs2_register_compressor(&jffs2_zlib_comp);
-+    if (ret)
-+        free_workspaces();
++              jeb->free_size = c->sector_size;
++              jeb->used_size = 0;
++              jeb->dirty_size = 0;
++              jeb->wasted_size = 0;
++      } else if (c->cleanmarker_size == 0) {
++              jeb->first_node = jeb->last_node = NULL;
 +
-+    return ret;
-+}
++              jeb->free_size = c->sector_size;
++              jeb->used_size = 0;
++              jeb->dirty_size = 0;
++              jeb->wasted_size = 0;
++      } else {
++              struct kvec vecs[1];
++              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)
++              };
 +
-+void jffs2_zlib_exit(void)
-+{
-+    jffs2_unregister_compressor(&jffs2_zlib_comp);
-+    free_workspaces();
- }
---- linux-2.4.21/fs/jffs2/crc32.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/crc32.c
-@@ -37,11 +37,11 @@
-  *      polynomial $edb88320
-  */
--/* $Id$ */
-+/* $Id$ */
- #include "crc32.h"
++              marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
++
++              vecs[0].iov_base = (unsigned char *) &marker;
++              vecs[0].iov_len = sizeof(marker);
++              ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen);
++              
+               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 %zd, got %zd\n",
+                              jeb->offset, sizeof(marker), retlen);
+                       goto bad2;
+               }
  
--const __u32 crc32_table[256] = {
-+const uint32_t crc32_table[256] = {
-       0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
-       0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
-       0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
---- linux-2.4.21/fs/jffs2/crc32.h~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/crc32.h
-@@ -1,16 +1,16 @@
- #ifndef CRC32_H
- #define CRC32_H
+               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;
  
--/* $Id$ */
-+/* $Id$ */
+               jeb->first_node = jeb->last_node = marker_ref;
  
- #include <linux/types.h>
+-              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;
++      }
  
--extern const __u32 crc32_table[256];
-+extern const uint32_t crc32_table[256];
+-              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;
  
- /* Return a 32-bit CRC of the contents of the buffer. */
+               ACCT_SANITY_CHECK(c,jeb);
+-              ACCT_PARANOIA_CHECK(jeb);
++      D1(ACCT_PARANOIA_CHECK(jeb));
  
--static inline __u32 
--crc32(__u32 val, const void *ss, int len)
-+static inline uint32_t 
-+crc32(uint32_t val, const void *ss, int len)
- {
-       const unsigned char *s = ss;
-         while (--len >= 0)
---- linux-2.4.21/fs/jffs2/dir.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/dir.c
-@@ -1,84 +1,73 @@
+               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);
+ }
++
+--- linux-2.4.21/fs/jffs2/file.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/file.c
+@@ -1,319 +1,106 @@
  /*
   * JFFS2 -- Journalling Flash File System, Version 2.
   *
   *
   */
  
++#include <linux/version.h>
  #include <linux/kernel.h>
+-#include <linux/mtd/compatmac.h> /* for min() */
  #include <linux/slab.h>
-+#include <linux/sched.h>
  #include <linux/fs.h>
--#include <linux/mtd/compatmac.h> /* For completion */
++#include <linux/time.h>
+ #include <linux/pagemap.h>
++#include <linux/highmem.h>
 +#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 "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);
+ 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));
  
--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 =
+-int jffs2_null_fsync(struct file *filp, struct dentry *dentry, int datasync)
++int jffs2_fsync(struct file *filp, struct dentry *dentry, int datasync)
  {
--      read:           generic_read_dir,
--      readdir:        jffs2_readdir,
+-      /* 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
-+      .read =         generic_read_dir,
-+      .readdir =      jffs2_readdir,
++      .llseek =       generic_file_llseek,
++      .open =         generic_file_open,
++      .read =         generic_file_read,
++      .write =        generic_file_write,
 +      .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,21 @@
-       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));
++      .mmap =         generic_file_readonly_mmap,
++      .fsync =        jffs2_fsync,
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,29)
++      .sendfile =     generic_file_sendfile
++#endif
+ };
  
-+      jffs2_free_raw_inode(ri);
-       d_instantiate(dentry, inode);
+ /* jffs2_file_inode_operations */
  
-       D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n",
-@@ -332,173 +235,48 @@
+ 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
+ };
  
--static int jffs2_do_unlink(struct inode *dir_i, struct dentry *dentry, int rename)
+-int jffs2_setattr (struct dentry *dentry, struct iattr *iattr)
 -{
--      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_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;
 -
--      c = JFFS2_SB_INFO(dir_i->i_sb);
+-      /* 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));
+-      }
 -
--      rd = jffs2_alloc_raw_dirent();
--      if (!rd)
+-      ri = jffs2_alloc_raw_inode();
+-      if (!ri) {
+-              if (S_ISLNK(inode->i_mode))
+-                      kfree(mdata);
 -              return -ENOMEM;
--
--      ret = jffs2_reserve_space(c, sizeof(*rd)+dentry->d_name.len, &phys_ofs, &alloclen, ALLOC_DELETION);
+-      }
+-              
+-      ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL);
 -      if (ret) {
--              jffs2_free_raw_dirent(rd);
+-              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);
 -
--      dir_f = JFFS2_INODE_INFO(dir_i);
--      down(&dir_f->sem);
+-      ri->ino = inode->i_ino;
+-      ri->version = ++f->highest_version;
 -
--      /* 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);
+-      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;
 -
--      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);
+-      if (ivalid & ATTR_MODE && ri->mode & S_ISGID &&
+-          !in_group_p(ri->gid) && !capable(CAP_FSETID))
+-              ri->mode &= ~S_ISGID;
 -
--      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);
+-      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;
 -
--      if (IS_ERR(fd)) {
--              up(&dir_f->sem);
--              return PTR_ERR(fd);
+-      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;
 -
--      /* 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);
+-      new_metadata = jffs2_write_dnode(inode, ri, mdata, mdatalen, phys_ofs, NULL);
+-      if (S_ISLNK(inode->i_mode))
+-              kfree(mdata);
 -
--              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--;
+-      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;
 -}
- 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 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;
  
--      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;
+-      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;
 -      }
--      
--      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;
+       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);
 -
--      /* XXX: This is ugly. */
--      rd->type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12;
--      if (!rd->type) rd->type = DT_REG;
+       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.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 = 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.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);
+       }
+       
 -
--      rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
--      rd->name_crc = crc32(0, dentry->d_name.name, dentry->d_name.len);
+       /* 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,13 @@
+       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;
++      unsigned aligned_start = start & ~3;
+       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 +223,53 @@
+       }
+       ri = jffs2_alloc_raw_inode();
+-      if (!ri)
+-              return -ENOMEM;
 -
--      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);
+-      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));
 -
--      if (IS_ERR(fd)) {
--              up(&dir_f->sem);
--              return PTR_ERR(fd);
--      }
+-              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;
+-              }
+-              down(&f->sem);
+-              datalen = writelen;
+-              cdatalen = min(alloclen - sizeof(*ri), writelen);
 -
--      /* File it. This will mark the old one obsolete. */
--      jffs2_add_fd_to_list(c, fd, &dir_f->dents);
--      up(&dir_f->sem);
+-              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;
++      if (!ri) {
++              D1(printk(KERN_DEBUG "jffs2_commit_write(): Allocation of raw inode failed\n"));
++              return -ENOMEM;
+               }
+-              /* 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);
 -
--      if (!rename) {
--              f = JFFS2_INODE_INFO(old_dentry->d_inode);
--              down(&f->sem);
--              old_dentry->d_inode->i_nlink = ++f->inocache->nlink;
+-              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);
++      /* 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());
+-              fn = jffs2_write_dnode(inode, ri, comprbuf, cdatalen, phys_ofs, NULL);
++      /* 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);
+-              jffs2_complete_reservation(c);
++      ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + aligned_start,
++                                    (pg->index << PAGE_CACHE_SHIFT) + aligned_start,
++                                    end - aligned_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;
++      
++      /* Adjust writtenlen for the padding we did, so we don't confuse our caller */
++      if (writtenlen < (start&3))
++              writtenlen = 0;
++      else
++              writtenlen -= (start&3);
++
++      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 +277,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));
 -      }
--      return 0;
+-      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;
  }
-+/***********************************************************************/
+--- /dev/null
++++ linux-2.4.21/fs/jffs2/fs.c
+@@ -0,0 +1,693 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2001-2003 Red Hat, Inc.
++ *
++ * Created by David Woodhouse <dwmw2@infradead.org>
++ *
++ * 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/vmalloc.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, 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;
++
++      D2(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, 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.
++         Flush the writebuffer, if neccecary, else we loose it */
++      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);
++      }       
++
++      if (!(*flags & MS_RDONLY))
++              jffs2_start_garbage_collect_thread(c);
++      
++      *flags |= MS_NOATIME;
++
++      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);
 +
- 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;
++      f = JFFS2_INODE_INFO(inode);
++      jffs2_init_inode_info(f);
 +
-+      ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, dentry->d_name.name, dentry->d_name.len);
++      memset(ri, 0, sizeof(*ri));
++      /* Set OS-specific defaults for new inodes */
++      ri->uid = cpu_to_je16(current->fsuid);
 +
-       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,13 +295,12 @@
-       struct jffs2_full_dnode *fn;
-       struct jffs2_full_dirent *fd;
-       int namelen;
--      __u32 alloclen, phys_ofs;
--      __u32 writtenlen;
--      int ret;
-+      uint32_t alloclen, phys_ofs;
-+      int ret, targetlen = strlen(target);
-       /* FIXME: If you care. We'd need to use frags for the target
-          if it grows much more than this */
--      if (strlen(target) > 254)
-+      if (targetlen > 254)
-               return -EINVAL;
-       ri = jffs2_alloc_raw_inode();
-@@ -537,7 +314,7 @@
-        * Just the node will do for now, though 
-        */
-       namelen = dentry->d_name.len;
--      ret = jffs2_reserve_space(c, sizeof(*ri) + strlen(target), &phys_ofs, &alloclen, ALLOC_NORMAL);
-+      ret = jffs2_reserve_space(c, sizeof(*ri) + targetlen, &phys_ofs, &alloclen, ALLOC_NORMAL);
-       if (ret) {
-               jffs2_free_raw_inode(ri);
-@@ -556,15 +333,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 = targetlen;
-+      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, targetlen));
-+      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, targetlen, phys_ofs, ALLOC_NORMAL);
-       jffs2_free_raw_inode(ri);
-@@ -575,19 +353,26 @@
-               jffs2_clear_inode(inode);
-               return PTR_ERR(fn);
-       }
++      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));
 +
-+      /* We use f->dents field to store the target path. */
-+      f->dents = kmalloc(targetlen + 1, GFP_KERNEL);
-+      if (!f->dents) {
-+              printk(KERN_WARNING "Can't allocate %d bytes of memory\n", targetlen + 1);
-+              up(&f->sem);
-+              jffs2_complete_reservation(c);
-+              jffs2_clear_inode(inode);
-+              return -ENOMEM;
++      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);
++
++#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
++      if (c->mtd->type == MTD_NANDFLASH) {
++              printk(KERN_ERR "jffs2: Cannot operate on NAND flash unless jffs2 NAND support is compiled in.\n");
++              return -EINVAL;
++      }
++      if (c->mtd->type == MTD_DATAFLASH) {
++              printk(KERN_ERR "jffs2: Cannot operate on DataFlash unless jffs2 DataFlash support is compiled in.\n");
++              return -EINVAL;
 +      }
++#endif
 +
-+      memcpy(f->dents, target, targetlen + 1);
-+      D1(printk(KERN_DEBUG "jffs2_symlink: symlink's target '%s' cached\n", (char *)f->dents));
++      c->flash_size = c->mtd->size;
 +
-       /* No data here. Only a metadata node, which will be 
-          obsoleted by the first data write
-       */
-       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 +380,6 @@
-                       jffs2_clear_inode(inode);
-                       return ret;
-               }
--      }
-       rd = jffs2_alloc_raw_dirent();
-       if (!rd) {
-@@ -608,41 +392,42 @@
-       dir_f = JFFS2_INODE_INFO(dir_i);
-       down(&dir_f->sem);
++      /* 
++       * Check, if we have to concatenate physical blocks to larger virtual blocks
++       * to reduce the memorysize for c->blocks. (kmalloc allows max. 128K allocation)
++       */
++      c->sector_size = c->mtd->erasesize; 
++      blocks = c->flash_size / c->sector_size;
++      if (!(c->mtd->flags & MTD_NO_VIRTBLOCKS)) {
++              while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024)) {
++                      blocks >>= 1;
++                      c->sector_size <<= 1;
++              }       
++      }
++
++      /*
++       * Size alignment check
++       */
++      if ((c->sector_size * blocks) != c->flash_size) {
++              c->flash_size = c->sector_size * blocks;                
++              printk(KERN_INFO "jffs2: Flash size not aligned to erasesize, reducing to %dKiB\n",
++                      c->flash_size / 1024);
++      }
++
++      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 */
++
++      /* NAND (or other bizarre) flash... do setup accordingly */
++      ret = jffs2_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);
++      if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
++              vfree(c->blocks);
++      else
++              kfree(c->blocks);
++ out_inohash:
++      kfree(c->inocache_list);
++ out_wbuf:
++      jffs2_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);
++}
++
++int jffs2_flash_setup(struct jffs2_sb_info *c) {
++      int ret = 0;
++      
++      if (jffs2_cleanmarker_oob(c)) {
++              /* NAND flash... do setup accordingly */
++              ret = jffs2_nand_flash_setup(c);
++              if (ret)
++                      return ret;
++      }
++
++      /* add setups for other bizarre flashes here... */
++      if (jffs2_nor_ecc(c)) {
++              ret = jffs2_nor_ecc_flash_setup(c);
++              if (ret)
++                      return ret;
++      }
++      
++      /* and Dataflash */
++      if (jffs2_dataflash(c)) {
++              ret = jffs2_dataflash_setup(c);
++              if (ret)
++                      return ret;
++      }
++      
++      return ret;
++}
++
++void jffs2_flash_cleanup(struct jffs2_sb_info *c) {
++
++      if (jffs2_cleanmarker_oob(c)) {
++              jffs2_nand_flash_cleanup(c);
++      }
++
++      /* add cleanups for other bizarre flashes here... */
++      if (jffs2_nor_ecc(c)) {
++              jffs2_nor_ecc_flash_cleanup(c);
++      }
++      
++      /* and DataFlash */
++      if (jffs2_dataflash(c)) {
++              jffs2_dataflash_cleanup(c);
++      }
++}
+--- linux-2.4.21/fs/jffs2/gc.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/gc.c
+@@ -1,76 +1,68 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * 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.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * 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.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
  
--      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->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));
+ #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 <linux/crc32.h>
++#include <linux/compiler.h>
++#include <linux/stat.h>
+ #include "nodelist.h"
+-#include "crc32.h"
++#include "compr.h"
  
--      rd->pino = dir_i->i_ino;
--      rd->version = ++dir_f->highest_version;
--      rd->ino = inode->i_ino;
--      rd->mctime = CURRENT_TIME;
-+      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);
--
--      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);
-       }
++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);
  
--      dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
-+      dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
+ /* 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;
  
-       jffs2_free_raw_dirent(rd);
+       /* 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 +72,16 @@
+               D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next (clean_list was empty)\n"));
  
-       /* Link the fd into the inode's list, obsoleting an old
-          one if necessary. */
-       jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+               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"));
 +
-       up(&dir_f->sem);
-+      jffs2_complete_reservation(c);
-       d_instantiate(dentry, inode);
-       return 0;
-@@ -659,8 +444,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;
++              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 */
++              D1(printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n"));
+               return NULL;
+       }
  
-       mode |= S_IFDIR;
-@@ -692,13 +476,15 @@
+@@ -94,6 +93,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;
++      }
++
++      D2(jffs2_dump_block_lists(c));
+       return ret;
+ }
  
-       inode->i_op = &jffs2_dir_inode_operations;
-       inode->i_fop = &jffs2_dir_operations;
-+      /* Directories get nlink 2 at start */
-+      inode->i_nlink = 2;
+@@ -103,21 +113,90 @@
+  */
+ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
+ {
+-      struct jffs2_eraseblock *jeb;
+       struct jffs2_inode_info *f;
++      struct jffs2_inode_cache *ic;
++      struct jffs2_eraseblock *jeb;
+       struct jffs2_raw_node_ref *raw;
+-      struct jffs2_node_frag *frag;
+-      struct jffs2_full_dnode *fn = NULL;
+-      struct jffs2_full_dirent *fd;
+-      __u32 start = 0, end = 0, nrfrags = 0;
+-      __u32 inum;
+-      struct inode *inode;
+-      int ret = 0;
++      int ret = 0, inum, nlink;
  
-       f = JFFS2_INODE_INFO(inode);
+       if (down_interruptible(&c->alloc_sem))
+               return -EINTR;
  
--      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);
+-      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);
++                      D2(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;
++      }
  
-       jffs2_free_raw_inode(ri);
+       /* First, work out which block we're garbage-collecting */
+       jeb = c->gcblock;
+@@ -126,13 +205,15 @@
+               jeb = jffs2_find_gc_block(c);
  
-@@ -715,13 +501,6 @@
-       f->metadata = fn;
-       up(&f->sem);
+       if (!jeb) {
+-              printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n");
+-              spin_unlock_bh(&c->erase_completion_lock);
++              D1 (printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n"));
++              spin_unlock(&c->erase_completion_lock);
+               up(&c->alloc_sem);
+               return -EIO;
+       }
  
--      /* 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 +508,6 @@
-                       jffs2_clear_inode(inode);
-                       return ret;
-               }
--      }
-       
-       rd = jffs2_alloc_raw_dirent();
-       if (!rd) {
-@@ -742,41 +520,43 @@
-       dir_f = JFFS2_INODE_INFO(dir_i);
-       down(&dir_f->sem);
+-      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));
  
--      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->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));
+       if (!jeb->used_size) {
+               up(&c->alloc_sem);
+@@ -141,61 +222,215 @@
  
--      rd->pino = dir_i->i_ino;
--      rd->version = ++dir_f->highest_version;
--      rd->ino = inode->i_ino;
--      rd->mctime = CURRENT_TIME;
-+      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);
--
--      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);
+       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;
        }
+                                                    
+-      inum = jffs2_raw_ref_to_inum(raw);
+-      D1(printk(KERN_DEBUG "Inode number is #%u\n", inum));
++      ic = jffs2_raw_ref_to_ic(raw);
  
--      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++;
+-      spin_unlock_bh(&c->erase_completion_lock);
++      /* 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);
  
-       jffs2_free_raw_dirent(rd);
+-      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, inum));
++      spin_unlock(&c->erase_completion_lock);
  
-       /* Link the fd into the inode's list, obsoleting an old
-          one if necessary. */
-       jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+-      inode = iget(OFNI_BS_2SFFJ(c), inum);
+-      if (is_bad_inode(inode)) {
+-              printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", inum);
+-              /* NB. This will happen again. We need to do something appropriate here. */
++      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));
 +
-       up(&dir_f->sem);
-+      jffs2_complete_reservation(c);
-       d_instantiate(dentry, inode);
-       return 0;
-@@ -786,15 +566,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;
++      /* 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.
++      */
++
++      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));
++              }
++              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.
++              */
++              printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n",
++                     ic->ino, ic->state);
+               up(&c->alloc_sem);
+-              iput(inode);
+-              return -EIO;
++              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;
        }
--      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 +588,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;
+-      f = JFFS2_INODE_INFO(inode);
++      /* 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;
++              }
++
++              /* 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)) {
++              ret = PTR_ERR(f);
++              goto release_sem;
++      }
++      if (!f) {
++              ret = 0;
++              goto release_sem;
++      }
++
++      ret = jffs2_garbage_collect_live(c, jeb, raw, f);
++
++      jffs2_gc_release_inode(c, f);
++
++ release_sem:
++      up(&c->alloc_sem);
++
++ 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;
++
+       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 (!old_valid_dev(rdev))
-+              return -EINVAL;
+-      if (raw->flash_offset & 1) {
++      spin_lock(&c->erase_completion_lock);
 +
-       ri = jffs2_alloc_raw_inode();
-       if (!ri)
-               return -ENOMEM;
-@@ -817,7 +603,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);
++      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;
        }
        
-@@ -844,15 +630,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 +655,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 +662,6 @@
-                       jffs2_clear_inode(inode);
-                       return ret;
+-      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;
+@@ -206,13 +441,22 @@
                }
--      }
-       rd = jffs2_alloc_raw_dirent();
-       if (!rd) {
-@@ -896,44 +674,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->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 = dir_i->i_ino;
--      rd->version = ++dir_f->highest_version;
--      rd->ino = inode->i_ino;
--      rd->mctime = CURRENT_TIME;
-+      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);
+       }
+       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;
+       }
+@@ -224,12 +468,13 @@
        }
  
--      dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
-+      dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
+       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;
+@@ -237,53 +482,207 @@
+       }
+  upnout:
+       up(&f->sem);
+-      up(&c->alloc_sem);
+-      iput(inode);
  
-       jffs2_free_raw_dirent(rd);
+- eraseit_lock:
+-      /* If we've finished this block, start it erasing */
+-      spin_lock_bh(&c->erase_completion_lock);
++      return ret;
++}
  
-       /* Link the fd into the inode's list, obsoleting an old
-          one if necessary. */
-       jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+- 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;
 +
-       up(&dir_f->sem);
-+      jffs2_complete_reservation(c);
-       d_instantiate(dentry, inode);
-@@ -944,7 +723,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 +754,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;
++      D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw)));
 +
-+      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);
++      rawlen = ref_totlen(c, c->gcblock, raw);
 +
-       if (ret)
-               return ret;
-@@ -989,22 +778,36 @@
-               }
++      /* 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;
        }
+-      spin_unlock_bh(&c->erase_completion_lock);
  
-+      /* 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++;
++      node = kmalloc(rawlen, GFP_KERNEL);
++      if (!node)
++               return -ENOMEM;
 +
-       /* 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);
++      ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node);
++      if (!ret && retlen != rawlen)
++              ret = -EIO;
++      if (ret)
++              goto out_node;
 +
-+      /* 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;
++      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;
 +      }
 +
-+      if (S_ISDIR(old_dentry->d_inode->i_mode))
-+              old_dir_i->i_nlink--;
++      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;
++              }
 +
-+      return 0;
++              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;
++                      }
++              }
++              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);
++              }
++
++              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-core, the 'nraw' pointer is then
++         going to be fetched from ic->nodes by our caller. */
++      spin_lock(&c->erase_completion_lock);
++        nraw->next_in_ino = ic->nodes;
++        ic->nodes = nraw;
++      spin_unlock(&c->erase_completion_lock);
++
++      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;
  }
  
---- linux-2.4.21/fs/jffs2/erase.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/erase.c
-@@ -1,68 +1,63 @@
- /*
-  * JFFS2 -- Journalling Flash File System, Version 2.
-  *
-- * Copyright (C) 2001 Red Hat, Inc.
-- *
-- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
-- *
-- * The original JFFS, from which the design for JFFS2 was derived,
-- * was designed and implemented by Axis Communications AB.
-- *
-- * 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.
-+ * Copyright (C) 2001-2003 Red Hat, Inc.
-  *
-- * The Original Code is JFFS2 - Journalling Flash File System, version 2
-+ * Created by David Woodhouse <dwmw2@infradead.org>
-  *
-- * 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.
-+ * For licensing information, see the file 'LICENCE' in this directory.
-  *
-- * $Id$
-+ * $Id$
-  *
-  */
-+
- #include <linux/kernel.h>
- #include <linux/slab.h>
- #include <linux/mtd/mtd.h>
--#include <linux/jffs2.h>
--#include <linux/interrupt.h>
-+#include <linux/compiler.h>
-+#include <linux/crc32.h>
-+#include <linux/sched.h>
-+#include <linux/pagemap.h>
- #include "nodelist.h"
--#include "crc32.h"
+ 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) {
+                       printk(KERN_WARNING "kmalloc of mdata failed in jffs2_garbage_collect_metadata()\n");
+                       return -ENOMEM;
+               }
+-              ret = jffs2_read_dnode(c, fn, mdata, 0, mdatalen);
++              ret = jffs2_read_dnode(c, f, fn, mdata, 0, mdatalen);
+               if (ret) {
+                       printk(KERN_WARNING "read of old metadata failed in jffs2_garbage_collect_metadata(): %d\n", ret);
+                       kfree(mdata);
+@@ -295,34 +694,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.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 = 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.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);
  
- 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, uint32_t bad_offset);
-+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);
+       if (IS_ERR(new_fn)) {
+               printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
+@@ -333,41 +732,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;
+ }
  
- void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+ 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 erase_info *instr;
+-      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;
-+      uint32_t bad_offset;
-+#ifdef __ECOS
-+       ret = jffs2_flash_erase(c, jeb);
-+       if (!ret) {
-+               jffs2_erase_succeeded(c, jeb);
-+               return;
-+       }
-+       bad_offset = jeb->offset;
-+#else /* Linux */
-+      struct erase_info *instr;
  
-+      D1(printk(KERN_DEBUG "jffs2_erase_block(): erase block %#x (range %#x-%#x)\n", jeb->offset, jeb->offset, jeb->offset + c->sector_size));
-       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;
-       }
+-      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));
  
-@@ -73,23 +68,29 @@
-       instr->len = c->sector_size;
-       instr->callback = jffs2_erase_callback;
-       instr->priv = (unsigned long)(&instr[1]);
-+      instr->fail_addr = 0xffffffff;
+-      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));
        
-       ((struct erase_priv_struct *)instr->priv)->jeb = jeb;
-       ((struct erase_priv_struct *)instr->priv)->c = c;
-       ret = c->mtd->erase(c->mtd, instr);
--      if (!ret) {
-+      if (!ret)
-               return;
--      }
-+
-+      bad_offset = instr->fail_addr;
-+      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;
+       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);
  
-@@ -97,74 +98,119 @@
-               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, bad_offset);
+       if (IS_ERR(new_fd)) {
+               printk(KERN_WARNING "jffs2_write_dirent in garbage_collect_dirent failed: %ld\n", PTR_ERR(new_fd));
+@@ -378,19 +776,97 @@
  }
  
--void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
-+void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
+ 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_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);
+-      struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+       struct jffs2_full_dirent **fdp = &f->dents;
+       int found = 0;
  
--              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)) {
+-      /* 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);
 +
-+              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);
++              rd = kmalloc(rawlen, GFP_KERNEL);
++              if (!rd)
++                      return -ENOMEM;
 +
-+                      if (!--count) {
-+                              D1(printk(KERN_DEBUG "Count reached. jffs2_erase_pending_blocks leaving\n"));
-+                              goto done;
++              /* 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 (SECTOR_ADDR(raw->flash_offset) == SECTOR_ADDR(fd->raw->flash_offset))
++                              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 %u) reading header from obsolete node at %08x\n",
++                                     retlen, rawlen, ref_offset(raw));
++                              continue;
 +                      }
-+              } 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();
-+              }
++                      if (je16_to_cpu(rd->nodetype) != JFFS2_NODETYPE_DIRENT)
++                              continue;
 +
-               /* 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);
++                      /* If the name CRC doesn't match, skip */
++                      if (je32_to_cpu(rd->name_crc) != name_crc)
++                              continue;
 +
-+      spin_unlock(&c->erase_completion_lock);
-+ done:
-       D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n"));
++                      /* 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;
 +
-+      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, uint32_t bad_offset)
-+{
-+      /* For NAND, if the failure did not occur at the device level for a
-+         specific physical page, don't bother updating the bad block table. */
-+      if (jffs2_cleanmarker_oob(c) && (bad_offset != 0xffffffff)) {
-+              /* We had a device-level failure to erase.  Let's see if we've
-+                 failed too many times. */
-+              if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) {
-+                      /* We'd like to give this block another try. */
-+                      spin_lock(&c->erase_completion_lock);
-+                      list_del(&jeb->list);
-+                      list_add(&jeb->list, &c->erase_pending_list);
-+                      c->erasing_size -= c->sector_size;
-+                      c->dirty_size += c->sector_size;
-+                      jeb->dirty_size = c->sector_size;
-+                      spin_unlock(&c->erase_completion_lock);
-+                      return;
++                      /* 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);
 +              }
-+      }
 +
-+      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);
-+}      
++              up(&c->erase_free_sem);
++              kfree(rd);
++      }
 +
-+#ifndef __ECOS
- static void jffs2_erase_callback(struct erase_info *instr)
++      /* No need for it any more. Just mark it obsolete and remove it from the list */
+       while (*fdp) {
+               if ((*fdp) == fd) {
+                       found = 1;
+@@ -400,7 +876,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);
+@@ -408,93 +884,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 erase_priv_struct *priv = (void *)instr->priv;
+-      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;
  
-       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, instr->fail_addr);
+-      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 {
--              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 */
+       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.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));
  
- /* Hmmm. Maybe we should accept the extra space it takes and make
-    this a standard doubly-linked list? */
-@@ -187,7 +233,7 @@
-                       continue;
-               } 
+-              ri.ino = inode->i_ino;
+-              ri.version = ++f->highest_version;
+-              ri.offset = start;
+-              ri.dsize = end - start;
+-              ri.csize = 0;
++              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));
  
--              if (((*prev)->flash_offset & ~(c->sector_size -1)) == jeb->offset) {
-+              if (SECTOR_ADDR((*prev)->flash_offset) == jeb->offset) {
-                       /* It's in the block we're erasing */
-                       struct jffs2_raw_node_ref *this;
+       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);
  
-@@ -221,7 +267,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;
-@@ -231,11 +277,8 @@
-               printk("\n");
+       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);
+@@ -510,12 +988,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));
        });
  
--      if (ic->nodes == (void *)ic) {
--              D1(printk(KERN_DEBUG "inocache for ino #%u is all gone now. Freeing\n", ic->ino));
-+      if (ic->nodes == (void *)ic)
-               jffs2_del_ino_cache(c, ic);
--              jffs2_free_inode_cache(ic);
--      }
- }
- static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
-@@ -256,118 +299,148 @@
-       jeb->last_node = NULL;
+-      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) {
+@@ -540,49 +1023,146 @@
  }
  
--void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
--{
--      OFNI_BS_2SFFJ(c)->s_dirt = 1;
--}
--
--void jffs2_mark_erased_blocks(struct jffs2_sb_info *c)
-+static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+ 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 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;
-+      uint32_t bad_offset;
+-      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;
  
--      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)) && (c->cleanmarker_size > 0)) {
-               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;
+       memset(&ri, 0, sizeof(ri));
  
-                       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;
+-      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));
  
--                              ret = c->mtd->read(c->mtd, ofs, readlen, &retlen, ebuf);
--                              if (ret < 0) {
-+                      bad_offset = ofs;
+       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;
 +
-+                      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)) {
-                                       /* It's OK. We know it's properly aligned */
-                                       unsigned long datum = *(unsigned long *)(&ebuf[i]);
-                                       if (datum + 1) {
--                                              printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, ofs + i);
-+                                      bad_offset += i;
-+                                      printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, bad_offset);
-                                       bad: 
-+                                      if ((!jffs2_cleanmarker_oob(c)) && (c->cleanmarker_size > 0))
-                                               jffs2_free_raw_node_ref(marker_ref);
-                                               kfree(ebuf);
-                                       bad2:
--                                              spin_lock_bh(&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);
--                                              wake_up(&c->erase_wait);
-+                                      spin_lock(&c->erase_completion_lock);
-+                                      /* Stick it on a list (any list) so
-+                                         erase_failed can take it right off
-+                                         again.  Silly, but shouldn't happen
-+                                         often. */
-+                                      list_add(&jeb->list, &c->erasing_list);
-+                                      spin_unlock(&c->erase_completion_lock);
-+                                      jffs2_erase_failed(c, jeb, bad_offset);
-                                               return;
-                                       }
-                               }
-                               ofs += readlen;
-+                      cond_resched();
-                       }
-                       kfree(ebuf);
++              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;
                }
-                                       
-+      bad_offset = jeb->offset;
++                              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;
++                              }
 +
-               /* 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)) {
++                              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;
++                      }
++              }
 +
-+              if (jffs2_write_nand_cleanmarker(c, jeb))
-+                      goto bad2;
-+                      
-+              jeb->first_node = jeb->last_node = NULL;
++              /* ... then up */
 +
-+              jeb->free_size = c->sector_size;
-+              jeb->used_size = 0;
-+              jeb->dirty_size = 0;
-+              jeb->wasted_size = 0;
-+      } else if (c->cleanmarker_size == 0) {
-+              jeb->first_node = jeb->last_node = NULL;
++              /* Find last frag which is actually part of the node we're to GC. */
++              frag = jffs2_lookup_node_frag(&f->fragtree, end-1);
 +
-+              jeb->free_size = c->sector_size;
-+              jeb->used_size = 0;
-+              jeb->dirty_size = 0;
-+              jeb->wasted_size = 0;
-+      } else {
-+              struct kvec vecs[1];
-+              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)
-+              };
++              while((frag = frag_next(frag)) && frag->ofs+frag->size <= max) {
 +
-+              marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
++                      /* 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;
++                      }
 +
-+              vecs[0].iov_base = (unsigned char *) &marker;
-+              vecs[0].iov_len = sizeof(marker);
-+              ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen);
-+              
++                      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 */
+@@ -592,63 +1172,58 @@
+        *    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;
+-              char comprtype = JFFS2_COMPR_NONE;
++              uint32_t datalen;
++              uint32_t cdatalen;
++              uint16_t comprtype = JFFS2_COMPR_NONE;
+               ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen);
                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 %zd, got %zd\n",
-                              jeb->offset, sizeof(marker), retlen);
-                       goto bad2;
+-                      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;
  
-               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;
+               writebuf = pg_ptr + (offset & (PAGE_CACHE_SIZE -1));
  
--              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;
-+      }
+-              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);
++              comprtype = jffs2_compress(c, f, writebuf, &comprbuf, &datalen, &cdatalen);
  
--              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;
+-              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;
+-              ri.compr = comprtype;
+-              ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
+-              ri.data_crc = crc32(0, writebuf, 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));
+       
+-              new_fn = jffs2_write_dnode(inode, &ri, writebuf, cdatalen, phys_ofs, NULL);
++              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 & 0xff;
++              ri.usercompr = (comprtype >> 8) & 0xff;
++              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);
++
++              jffs2_free_comprbuf(comprbuf, writebuf);
  
-               ACCT_SANITY_CHECK(c,jeb);
--              ACCT_PARANOIA_CHECK(jeb);
-+      D1(ACCT_PARANOIA_CHECK(jeb));
+               if (IS_ERR(new_fn)) {
+                       printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
+@@ -663,12 +1238,8 @@
+                       f->metadata = NULL;
+               }
+       }
+-      if (comprbuf) kfree(comprbuf);
  
-               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);
+-      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;
  }
-+
---- linux-2.4.21/fs/jffs2/file.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/file.c
-@@ -1,319 +1,106 @@
+--- linux-2.4.21/fs/jffs2/ioctl.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/ioctl.c
+@@ -1,37 +1,13 @@
  /*
   * JFFS2 -- Journalling Flash File System, Version 2.
   *
   *
   */
  
-+#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 "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)
+@@ -42,6 +18,6 @@
  {
--      /* 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;
+       /* Later, this will provide for lsattr.jffs2 and chattr.jffs2, which
+          will include compression support etc. */
+-      return -EINVAL;
++      return -ENOTTY;
  }
+       
+--- linux-2.4.21/fs/jffs2/malloc.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/malloc.c
+@@ -1,37 +1,13 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * 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.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * 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.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
  
- 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
- };
+@@ -47,6 +23,9 @@
+ #define JFFS2_SLAB_POISON 0
+ #endif
  
- 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
};
++// 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;
  
--int jffs2_setattr (struct dentry *dentry, struct iattr *iattr)
+-void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn)
 -{
--      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);
--      }
+-      struct jffs2_tmp_dnode_info *next;
 -
--      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);
+-      while (tn) {
+-              next = tn;
+-              tn = tn->next;
+-              jffs2_free_full_dnode(next->fn);
+-              jffs2_free_tmp_dnode_info(next);
 -      }
--      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;
--      }
+-void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
+-{
+-      struct jffs2_full_dirent *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"));
--              }
+-      while (fd) {
+-              next = fd->next;
+-              jffs2_free_full_dirent(fd);
+-              fd = next;
 -      }
--      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 @@
+ int __init jffs2_create_slab_caches(void)
  {
-       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.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));
+-      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;
  
--              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.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,13 @@
-       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;
-+      unsigned aligned_start = start & ~3;
-       int ret = 0;
--      ssize_t writtenlen = 0;
-+      uint32_t writtenlen = 0;
+-      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;
  
--      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));
+-      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;
  
-       if (!start && end == PAGE_CACHE_SIZE) {
-               /* We need to avoid deadlock with page_cache_read() in
-@@ -435,109 +223,53 @@
-       }
+-      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;
  
-       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;
+-      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;
  
--              D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, file_ofs));
+-      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);
 -
--              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;
--              }
--              down(&f->sem);
--              datalen = writelen;
--              cdatalen = min(alloclen - sizeof(*ri), writelen);
++      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);
 -
--              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;
-+      if (!ri) {
-+              D1(printk(KERN_DEBUG "jffs2_commit_write(): Allocation of raw inode failed\n"));
-+              return -ENOMEM;
-               }
--              /* 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);
-+      /* 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());
+ struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize)
+@@ -146,75 +114,92 @@
  
--              fn = jffs2_write_dnode(inode, ri, comprbuf, cdatalen, phys_ofs, NULL);
-+      /* 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);
+ 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;
+ }
  
--              jffs2_complete_reservation(c);
-+      ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + aligned_start,
-+                                    (pg->index << PAGE_CACHE_SHIFT) + aligned_start,
-+                                    end - aligned_start, &writtenlen);
+ 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);
+ }
  
--              if (comprtype != JFFS2_COMPR_NONE)
--                      kfree(comprbuf);
-+      kunmap(pg);
+ 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;
+ }
  
--              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;
-+      
-+      /* Adjust writtenlen for the padding we did, so we don't confuse our caller */
-+      if (writtenlen < (start&3))
-+              writtenlen = 0;
-+      else
-+              writtenlen -= (start&3);
-+
-+      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;
-       }
+ 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);
+ }
  
-       jffs2_free_raw_inode(ri);
+ 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;
+ }
  
--      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 +277,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);
+ 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);
+ }
  
--      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;
+ 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;
  }
---- /dev/null
-+++ linux-2.4.21/fs/jffs2/fs.c
-@@ -0,0 +1,693 @@
-+/*
-+ * JFFS2 -- Journalling Flash File System, Version 2.
-+ *
+ 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);
+ }
+--- linux-2.4.21/fs/jffs2/nodelist.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/nodelist.c
+@@ -1,44 +1,24 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001, 2002 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * 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.
 + * Copyright (C) 2001-2003 Red Hat, Inc.
-+ *
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
 + * Created by David Woodhouse <dwmw2@infradead.org>
-+ *
+  *
+- * 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.
 + * For licensing information, see the file 'LICENCE' in this directory.
-+ *
+  *
+- * $Id$
 + * $Id$
-+ *
-+ */
-+
-+#include <linux/version.h>
-+#include <linux/config.h>
-+#include <linux/kernel.h>
+  *
+  */
+ #include <linux/kernel.h>
+-#include <linux/jffs2.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/vmalloc.h>
-+#include <linux/vfs.h>
+ #include <linux/fs.h>
+ #include <linux/mtd/mtd.h>
++#include <linux/rbtree.h>
 +#include <linux/crc32.h>
-+#include "nodelist.h"
-+
-+
-+static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
++#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,93 +69,156 @@
+         *prev = tn;
+ }
++static void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn)
 +{
-+      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, 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);
++      struct jffs2_tmp_dnode_info *next;
 +
-+      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);
++      while (tn) {
++              next = tn;
++              tn = tn->next;
++              jffs2_free_full_dnode(next->fn);
++              jffs2_free_tmp_dnode_info(next);
 +      }
-+      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);
++static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
++{
++      struct jffs2_full_dirent *next;
 +
-+      return 0;
++      while (fd) {
++              next = fd->next;
++              jffs2_free_full_dirent(fd);
++              fd = next;
++      }
 +}
 +
-+int jffs2_setattr(struct dentry *dentry, struct iattr *iattr)
++/* Returns first valid node after 'ref'. May return 'ref' */
++static struct jffs2_raw_node_ref *jffs2_first_valid_node(struct jffs2_raw_node_ref *ref)
 +{
-+      return jffs2_do_setattr(dentry->d_inode, iattr);
++      while (ref && ref->next_in_ino) {
++              if (!ref_obsolete(ref))
++                      return ref;
++              D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref)));
++              ref = ref->next_in_ino;
++      }
++      return NULL;
 +}
 +
-+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;
+ /* 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,
++int jffs2_get_inode_nodes(struct jffs2_sb_info *c, 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_raw_node_ref *ref, *valid_ref;
+       struct jffs2_tmp_dnode_info *tn, *ret_tn = NULL;
+       struct jffs2_full_dirent *fd, *ret_fd = NULL;
+-
+       union jffs2_node_union node;
+       size_t retlen;
+       int err;
+       *mctime_ver = 0;
+-      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);
+-      }
+-      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) {
+-                      /* 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));
+-                      continue;
+-              }
+-              err = c->mtd->read(c->mtd, (ref->flash_offset & ~3), min(ref->totlen, sizeof(node)), &retlen, (void *)&node);
++      D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%u\n", f->inocache->ino));
 +
 +      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;
++      valid_ref = jffs2_first_valid_node(f->inocache->nodes);
 +
-+      buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT;
++      if (!valid_ref && (f->inocache->ino != 1))
++              printk(KERN_WARNING "Eep. No valid nodes for ino #%u\n", f->inocache->ino);
 +
-+      D2(jffs2_dump_block_lists(c));
++      while (valid_ref) {
++              /* We can hold a pointer to a non-obsolete node without the spinlock,
++                 but _obsolete_ nodes may disappear at any time, if the block
++                 they're in gets erased. So if we mark 'ref' obsolete while we're
++                 not holding the lock, it can go away immediately. For that reason,
++                 we find the next valid node first, before processing 'ref'.
++              */
++              ref = valid_ref;
++              valid_ref = jffs2_first_valid_node(ref->next_in_ino);
++              spin_unlock(&c->erase_completion_lock);
 +
-+      spin_unlock(&c->erase_completion_lock);
++              cond_resched();
 +
-+      return 0;
-+}
++              /* 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;
+                               goto free_out;
+                       }
+-                      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;
+@@ -188,6 +231,7 @@
+                       }
+                       fd->nhash = full_name_hash(fd->name, node.d.nsize);
+                       fd->next = NULL;
++                      fd->name[node.d.nsize] = '\0';
+                               /* Wheee. We now have a complete jffs2_full_dirent structure, with
+                                  the name in it and everything. Link it into the list 
+                               */
+@@ -196,21 +240,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 (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->flash_offset & 1) {
+-                              D1(printk(KERN_DEBUG "obsoleted\n"));
+-                              /* Obsoleted */
++                      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;
 +
-+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));
++                              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;
++                              }
 +
-+      jffs2_do_clear_inode(c, f);
-+}
++                              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
 +
-+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;
++                                      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;
+                       }
++                                      
++                              }
 +
-+      D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino));
++                              /* 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);
 +
-+      f = JFFS2_INODE_INFO(inode);
-+      c = JFFS2_SB_INFO(inode->i_sb);
++                              jeb->used_size += len;
++                              jeb->unchecked_size -= len;
++                              c->used_size += len;
++                              c->unchecked_size -= len;
 +
-+      jffs2_init_inode_info(f);
-+      
-+      ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node);
++                              /* 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 (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));
++                                 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);
++                      }
 +
-+      inode->i_nlink = f->inocache->nlink;
+                       tn = jffs2_alloc_tmp_dnode_info();
+                       if (!tn) {
+                               D1(printk(KERN_DEBUG "alloc tn failed\n"));
+@@ -225,36 +374,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;
 +
-+      inode->i_blksize = PAGE_SIZE;
-+      inode->i_blocks = (inode->i_size + 511) >> 9;
-+      
-+      switch (inode->i_mode & S_IFMT) {
-+              jint16_t rdev;
++                              printk(KERN_ERR "Eep. Unknown node type %04x at %08x was marked REF_UNCHECKED\n",
++                                     je16_to_cpu(node.u.nodetype), ref_offset(ref));
 +
-+      case S_IFLNK:
-+              inode->i_op = &jffs2_symlink_inode_operations;
-+              break;
-+              
-+      case S_IFDIR:
-+      {
-+              struct jffs2_full_dirent *fd;
++                              /* 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);
 +
-+              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++;
++                              jeb->used_size += len;
++                              jeb->unchecked_size -= len;
++                              c->used_size += len;
++                              c->unchecked_size -= len;
 +
-+              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;
++                              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;
+                       }
 +
-+      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, 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;
-+              }                       
+               }
++              spin_lock(&c->erase_completion_lock);
 +
-+      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;
+       }
++      spin_unlock(&c->erase_completion_lock);
+       *tnp = ret_tn;
+       *fdp = ret_fd;
+@@ -266,19 +455,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;
+@@ -290,6 +490,8 @@
+ {
+       struct jffs2_inode_cache **prev;
+       D2(printk(KERN_DEBUG "jffs2_add_ino_cache: Add %p (ino #%u)\n", new, new->ino));
++      BUG_ON(!new->ino);
 +
-+      default:
-+              printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu\n", inode->i_mode, (unsigned long)inode->i_ino);
-+      }
+       spin_lock(&c->inocache_lock);
+       
+       prev = &c->inocache_list[new->ino % INOCACHE_HASHSIZE];
+@@ -299,13 +501,14 @@
+       }
+       new->next = *prev;
+       *prev = new;
 +
-+      up(&f->sem);
+       spin_unlock(&c->inocache_lock);
+ }
+ void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old)
+ {
+       struct jffs2_inode_cache **prev;
+-      D2(printk(KERN_DEBUG "jffs2_del_ino_cache: Del %p (ino #%u)\n", old, old->ino));
++      D1(printk(KERN_DEBUG "jffs2_del_ino_cache: Del %p (ino #%u)\n", old, old->ino));
+       spin_lock(&c->inocache_lock);
+       
+       prev = &c->inocache_list[old->ino % INOCACHE_HASHSIZE];
+@@ -316,6 +519,15 @@
+       if ((*prev) == old) {
+               *prev = old->next;
+       }
 +
-+      D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n"));
-+}
++      /* Free it now unless it's in READING or CLEARING state, which
++         are the transitions upon read_inode() and clear_inode(). The
++         rest of the time we know nobody else is looking at it, and 
++         if it's held by read_inode() or clear_inode() they'll free it
++         for themselves. */
++      if (old->state != INO_STATE_READING && old->state != INO_STATE_CLEARING)
++              jffs2_free_inode_cache(old);
 +
-+void jffs2_dirty_inode(struct inode *inode)
+       spin_unlock(&c->inocache_lock);
+ }
+@@ -328,7 +540,6 @@
+               this = c->inocache_list[i];
+               while (this) {
+                       next = this->next;
+-                      D2(printk(KERN_DEBUG "jffs2_free_ino_caches: Freeing ino #%u at %p\n", this->ino, this));
+                       jffs2_free_inode_cache(this);
+                       this = next;
+               }
+@@ -352,3 +563,128 @@
+       }
+ }
+       
++struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset)
 +{
-+      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));
++      /* 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;
 +
-+      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;
++      D2(printk(KERN_DEBUG "jffs2_lookup_node_frag(%p, %d)\n", fragtree, offset));
 +
-+      jffs2_do_setattr(inode, &iattr);
-+}
++      next = fragtree->rb_node;
 +
-+int jffs2_remount_fs (struct super_block *sb, int *flags, char *data)
-+{
-+      struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
++      while(next) {
++              frag = rb_entry(next, struct jffs2_node_frag, rb);
 +
-+      if (c->flags & JFFS2_SB_FLAG_RO && !(sb->s_flags & MS_RDONLY))
-+              return -EROFS;
++              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;
++              }
++      }
 +
-+      /* 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.
-+         Flush the writebuffer, if neccecary, else we loose it */
-+      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);
-+      }       
++      /* Exact match not found. Go back up looking at each parent,
++         and return the closest smaller one */
 +
-+      if (!(*flags & MS_RDONLY))
-+              jffs2_start_garbage_collect_thread(c);
++      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"));
 +      
-+      *flags |= MS_NOATIME;
-+
-+      return 0;
++      return prev;
 +}
 +
-+void jffs2_write_super (struct super_block *sb)
++/* 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_sb_info *c = JFFS2_SB_INFO(sb);
-+      sb->s_dirt = 0;
++      struct jffs2_node_frag *frag;
++      struct jffs2_node_frag *parent;
 +
-+      if (sb->s_flags & MS_RDONLY)
++      if (!root->rb_node)
 +              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));
++      frag = (rb_entry(root->rb_node, struct jffs2_node_frag, rb));
 +
-+      c = JFFS2_SB_INFO(sb);
-+      
-+      inode = new_inode(sb);
-+      
-+      if (!inode)
-+              return ERR_PTR(-ENOMEM);
++      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;
++              }
 +
-+      f = JFFS2_INODE_INFO(inode);
-+      jffs2_init_inode_info(f);
++              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;
++              }
 +
-+      memset(ri, 0, sizeof(*ri));
-+      /* Set OS-specific defaults for new inodes */
-+      ri->uid = cpu_to_je16(current->fsuid);
++              jffs2_free_node_frag(frag);
++              frag = parent;
 +
-+      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);
++              cond_resched();
 +      }
-+      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)
++void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base)
 +{
-+      struct jffs2_sb_info *c;
-+      struct inode *root_i;
-+      int ret;
-+      size_t blocks;
-+
-+      c = JFFS2_SB_INFO(sb);
-+
-+#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
-+      if (c->mtd->type == MTD_NANDFLASH) {
-+              printk(KERN_ERR "jffs2: Cannot operate on NAND flash unless jffs2 NAND support is compiled in.\n");
-+              return -EINVAL;
-+      }
-+      if (c->mtd->type == MTD_DATAFLASH) {
-+              printk(KERN_ERR "jffs2: Cannot operate on DataFlash unless jffs2 DataFlash support is compiled in.\n");
-+              return -EINVAL;
-+      }
-+#endif
-+
-+      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)
-+       */
-+      c->sector_size = c->mtd->erasesize; 
-+      blocks = c->flash_size / c->sector_size;
-+      if (!(c->mtd->flags & MTD_NO_VIRTBLOCKS)) {
-+              while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024)) {
-+                      blocks >>= 1;
-+                      c->sector_size <<= 1;
-+              }       
-+      }
-+
-+      /*
-+       * Size alignment check
-+       */
-+      if ((c->sector_size * blocks) != c->flash_size) {
-+              c->flash_size = c->sector_size * blocks;                
-+              printk(KERN_INFO "jffs2: Flash size not aligned to erasesize, reducing to %dKiB\n",
-+                      c->flash_size / 1024);
-+      }
++      struct rb_node *parent = &base->rb;
++      struct rb_node **link = &parent;
 +
-+      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);
++      D2(printk(KERN_DEBUG "jffs2_fragtree_insert(%p; %d-%d, %p)\n", newfrag, 
++                newfrag->ofs, newfrag->ofs+newfrag->size, base));
 +
-+      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;
++      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();
++              }
 +      }
 +
-+      c->cleanmarker_size = sizeof(struct jffs2_unknown_node);
-+      /* Joern -- stick alignment for weird 8-byte-page flash here */
++      rb_link_node(&newfrag->rb, &base->rb, link);
++}
+--- linux-2.4.21/fs/jffs2/nodelist.h~mtd-cvs
++++ linux-2.4.21/fs/jffs2/nodelist.h
+@@ -1,48 +1,35 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * 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.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * 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.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
++#ifndef __JFFS2_NODELIST_H__
++#define __JFFS2_NODELIST_H__
 +
-+      /* NAND (or other bizarre) flash... do setup accordingly */
-+      ret = jffs2_flash_setup(c);
-+      if (ret)
-+              return ret;
+ #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
 +
-+      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 *));
+ #ifndef CONFIG_JFFS2_FS_DEBUG
+-#define CONFIG_JFFS2_FS_DEBUG 2
++#define CONFIG_JFFS2_FS_DEBUG 1
+ #endif
+ #if CONFIG_JFFS2_FS_DEBUG > 0
+@@ -57,6 +44,39 @@
+ #define D2(x)
+ #endif
++#define JFFS2_NATIVE_ENDIAN
 +
-+      if ((ret = jffs2_do_mount_fs(c)))
-+              goto out_inohash;
++/* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from
++   whatever OS we're actually running on here too. */
 +
-+      ret = -EINVAL;
++#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)})
 +
-+      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;
-+      }
++#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))})
 +
-+      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;
++#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))})
 +
-+#if LINUX_VERSION_CODE >= 0x20403
-+      sb->s_maxbytes = 0xFFFFFFFF;
++#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
-+      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);
-+      if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
-+              vfree(c->blocks);
-+      else
-+              kfree(c->blocks);
-+ out_inohash:
-+      kfree(c->inocache_list);
-+ out_wbuf:
-+      jffs2_flash_cleanup(c);
+ /*
+   This is all we need to keep in-core for each raw node during normal
+   operation. As and when we do read_inode on a particular inode, we can
+@@ -71,27 +91,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.
+-      */
+-};
+-
+-/* 
+-   Used for keeping track of deletion nodes &c, which can only be marked
+-   as obsolete when the node which they mark as deleted has actually been 
+-   removed from the flash.
+-*/
+-struct jffs2_raw_node_ref_list {
+-      struct jffs2_raw_node_ref *rew;
+-      struct jffs2_raw_node_ref_list *next;
+-};
++         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)
+ /* For each inode in the filesystem, we need to keep a record of
+    nlink, because it would be a PITA to scan the whole directory tree
+@@ -101,20 +115,30 @@
+    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;
+ };
+-struct jffs2_scan_info {
+-      struct jffs2_full_dirent *dents;
+-      struct jffs2_tmp_dnode_info *tmpnodes;
+-};
++/* 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 INO_STATE_CLEARING    6       /* In clear_inode() */
 +
-+      return ret;
-+}
++#define INOCACHE_HASHSIZE 128
 +
-+void jffs2_gc_release_inode(struct jffs2_sb_info *c,
-+                                 struct jffs2_inode_info *f)
-+{
-+      iput(OFNI_EDONI_2SFFJ(f));
-+}
+ /*
+   Larger representation of a raw node, kept in-core only when the 
+   struct inode for this particular ino is instantiated.
+@@ -123,12 +147,11 @@
+ 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; /* The offset to which the data of this node belongs */
++      uint32_t size;
++      uint32_t frags; /* Number of fragments which currently refer
+                       to this node. When this reaches zero, 
+-                      the node is obsolete.
+-                   */
++                      the node is obsolete.  */
+ };
+ /* 
+@@ -140,144 +163,265 @@
+ {
+       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];
+ };
 +
-+struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c,
-+                                                   int inum, int nlink)
+ /*
+   Fragments - used to build a map of which raw node to obtain 
+   data from for each part of the ino
+ */
+ 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; /* The offset to which this fragment belongs */
+ };
+ 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;
+       struct jffs2_raw_node_ref *gc_node;     /* Next node to be garbage collected */
+-
+-      /* For deletia. When a dirent node in this eraseblock is
+-         deleted by a node elsewhere, that other node can only 
+-         be marked as obsolete when this block is actually erased.
+-         So we keep a list of the nodes to mark as obsolete when
+-         the erase is completed.
+-      */
+-      // MAYBE        struct jffs2_raw_node_ref_list *deletia;
+ };
+ #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 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);
-+                      }
++      struct jffs2_raw_node_ref *ref;
++      int i=0;
 +
-+                      return NULL;
++      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);
 +              }
-+      } 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);
++      printk("\n");
 +}
 +
-+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;
+ #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))) { \
++                                if (!ref2->next_phys) \
++                                     printk("ref for node at %p (phys %08x) has next_phys->%p (----), last_node->%p (phys %08x)\n", \
++                                           ref2, ref_offset(ref2), ref2->next_phys, \
++                                           jeb->last_node, ref_offset(jeb->last_node)); \
++                                else \
++                                       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;
 +      
-+      *priv = (unsigned long)pg;
-+      return kmap(pg);
++      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);
 +}
 +
-+void jffs2_gc_release_page(struct jffs2_sb_info *c,
-+                         unsigned char *ptr,
-+                         unsigned long *priv)
++static inline uint32_t ref_totlen(struct jffs2_sb_info *c,
++                                struct jffs2_eraseblock *jeb,
++                                struct jffs2_raw_node_ref *ref)
 +{
-+      struct page *pg = (void *)*priv;
-+
-+      kunmap(pg);
-+      page_cache_release(pg);
-+}
++      uint32_t ret;
 +
-+int jffs2_flash_setup(struct jffs2_sb_info *c) {
-+      int ret = 0;
-+      
-+      if (jffs2_cleanmarker_oob(c)) {
-+              /* NAND flash... do setup accordingly */
-+              ret = jffs2_nand_flash_setup(c);
-+              if (ret)
-+                      return 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();
++      })
 +
-+      /* add setups for other bizarre flashes here... */
-+      if (jffs2_nor_ecc(c)) {
-+              ret = jffs2_nor_ecc_flash_setup(c);
-+              if (ret)
-+                      return ret;
-+      }
-+      
-+      /* and Dataflash */
-+      if (jffs2_dataflash(c)) {
-+              ret = jffs2_dataflash_setup(c);
-+              if (ret)
-+                      return ret;
++#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;
 +}
 +
-+void jffs2_flash_cleanup(struct jffs2_sb_info *c) {
 +
-+      if (jffs2_cleanmarker_oob(c)) {
-+              jffs2_nand_flash_cleanup(c);
-+      }
+ #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)
+-static inline int jffs2_raw_ref_to_inum(struct jffs2_raw_node_ref *raw)
++static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw)
+ {
+       while(raw->next_in_ino) {
+               raw = raw->next_in_ino;
+       }
+-      return ((struct jffs2_inode_cache *)raw)->ino;
++      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;
 +
-+      /* add cleanups for other bizarre flashes here... */
-+      if (jffs2_nor_ecc(c)) {
-+              jffs2_nor_ecc_flash_cleanup(c);
-+      }
-+      
-+      /* and DataFlash */
-+      if (jffs2_dataflash(c)) {
-+              jffs2_dataflash_cleanup(c);
-+      }
++      if (!node)
++              return NULL;
++      while(node->rb_left)
++              node = node->rb_left;
++      return rb_entry(node, struct jffs2_node_frag, rb);
 +}
---- linux-2.4.21/fs/jffs2/gc.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/gc.c
-@@ -1,76 +1,68 @@
++#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));
++D2(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,
++int jffs2_get_inode_nodes(struct jffs2_sb_info *c, 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 +445,31 @@
+ /* 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);
+-
+-/* compr.c */
+-unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out, 
+-                           __u32 *datalen, __u32 *cdatalen);
+-int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, 
+-                   unsigned char *data_out, __u32 cdatalen, __u32 datalen);
++int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++                   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);
+ /* 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);
+-/* compr_zlib.c */
+-int jffs2_zlib_init(void);
+-void jffs2_zlib_exit(void);
++#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
++/* 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);
++#endif
++
++#endif /* __JFFS2_NODELIST_H__ */
+--- linux-2.4.21/fs/jffs2/nodemgmt.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/nodemgmt.c
+@@ -1,45 +1,21 @@
  /*
   * JFFS2 -- Journalling Flash File System, Version 2.
   *
   */
  
  #include <linux/kernel.h>
- #include <linux/mtd/mtd.h>
  #include <linux/slab.h>
 -#include <linux/jffs2.h>
--#include <linux/sched.h>
+ #include <linux/mtd/mtd.h>
 -#include <linux/interrupt.h>
- #include <linux/pagemap.h>
-+#include <linux/crc32.h>
 +#include <linux/compiler.h>
-+#include <linux/stat.h>
++#include <linux/sched.h> /* For cond_resched() */
  #include "nodelist.h"
--#include "crc32.h"
-+#include "compr.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 +72,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 */
-+              D1(printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n"));
-               return NULL;
-       }
+ /**
+@@ -62,53 +38,95 @@
+  *    for the requested allocation.
+  */
  
-@@ -94,6 +93,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;
-+      }
-+
-+      D2(jffs2_dump_block_lists(c));
-       return ret;
- }
+-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);
  
-@@ -103,21 +113,90 @@
-  */
- int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
+-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)
  {
--      struct jffs2_eraseblock *jeb;
-       struct jffs2_inode_info *f;
-+      struct jffs2_inode_cache *ic;
-+      struct jffs2_eraseblock *jeb;
-       struct jffs2_raw_node_ref *raw;
--      struct jffs2_node_frag *frag;
--      struct jffs2_full_dnode *fn = NULL;
--      struct jffs2_full_dirent *fd;
--      __u32 start = 0, end = 0, nrfrags = 0;
--      __u32 inum;
--      struct inode *inode;
--      int ret = 0;
-+      int ret = 0, inum, nlink;
+       int ret = -EAGAIN;
+-      int blocksneeded = JFFS2_RESERVED_BLOCKS_WRITE;
++      int blocksneeded = c->resv_blocks_write;
+       /* align it */
+       minsize = PAD(minsize);
  
-       if (down_interruptible(&c->alloc_sem))
-               return -EINTR;
+-      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);
-+      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);
-+                      D2(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;
-+              }
++      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;
 +
-+              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;
++                      /* 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));
 +
-+              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();
++                              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);
 +
-+              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;
-@@ -126,13 +205,15 @@
-               jeb = jffs2_find_gc_block(c);
++                      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 (!jeb) {
--              printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n");
--              spin_unlock_bh(&c->erase_completion_lock);
-+              D1 (printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n"));
-+              spin_unlock(&c->erase_completion_lock);
-               up(&c->alloc_sem);
-               return -EIO;
-       }
+-                      if (current->need_resched)
+-                              schedule();
++                      cond_resched();
  
--      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 (signal_pending(current))
+                               return -EINTR;
  
-       if (!jeb->used_size) {
-               up(&c->alloc_sem);
-@@ -141,61 +222,215 @@
+                       down(&c->alloc_sem);
+-                      spin_lock_bh(&c->erase_completion_lock);
++                      spin_lock(&c->erase_completion_lock);
+               }
  
-       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();
+               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));
                }
        }
--      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);
+-      spin_unlock_bh(&c->erase_completion_lock);
++      spin_unlock(&c->erase_completion_lock);
+       if (ret)
                up(&c->alloc_sem);
-               goto eraseit_lock;
-       }
-                                                    
--      inum = jffs2_raw_ref_to_inum(raw);
--      D1(printk(KERN_DEBUG "Inode number is #%u\n", inum));
-+      ic = jffs2_raw_ref_to_ic(raw);
+       return ret;
+ }
  
--      spin_unlock_bh(&c->erase_completion_lock);
-+      /* 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);
+-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_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x, ino #%u\n", jeb->offset, raw->flash_offset&~3, inum));
+       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;
+ }
  
--      inode = iget(OFNI_BS_2SFFJ(c), inum);
--      if (is_bad_inode(inode)) {
--              printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", inum);
--              /* NB. This will happen again. We need to do something appropriate here. */
-+      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.
-+      */
-+
-+      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));
+ /* 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;
 +              }
-+              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.
-+              */
-+              printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n",
-+                     ic->ino, ic->state);
-               up(&c->alloc_sem);
--              iput(inode);
--              return -EIO;
-+              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;
++              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 @@
  
--      f = JFFS2_INODE_INFO(inode);
-+      /* 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 (list_empty(&c->free_list)) {
+-                      DECLARE_WAITQUEUE(wait, current);
++                      if (!c->nr_erasing_blocks && 
++                          !list_empty(&c->erasable_list)) {
++                              struct jffs2_eraseblock *ejeb;
 +
-+      if (ic->state == INO_STATE_GC) {
-+              spin_unlock(&c->inocache_lock);
++                              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));
++                      }
 +
-+              ret = jffs2_garbage_collect_pristine(c, ic, raw);
++                      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_lock(&c->inocache_lock);
-+              ic->state = INO_STATE_CHECKEDABSENT;
-+              wake_up(&c->inocache_wq);
++                      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);
 +
-+              if (ret != -EBADFD) {
-+                      spin_unlock(&c->inocache_lock);
-+                      goto release_sem;
-+              }
+                       /* 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)) {
 +
-+              /* Fall through if it wanted us to, with inocache_lock held */
++              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);
 +      }
 +
-+      /* 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. */
+       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,61 @@
+  *    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;
+-      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));
++      jeb = &c->blocks[new->flash_offset / c->sector_size];
++      len = ref_totlen(c, jeb, new);
 +
-+      inum = ic->ino;
-+      nlink = ic->nlink;
-+      spin_unlock(&c->inocache_lock);
++      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)) {
++      /* we could get some obsolete nodes after nextblock was refiled
++         in wbuf.c */
++      if ((c->nextblock || !ref_obsolete(new))
++          &&(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);
 +
-+      f = jffs2_gc_fetch_inode(c, inum, nlink);
-+      if (IS_ERR(f)) {
-+              ret = PTR_ERR(f);
-+              goto release_sem;
-+      }
-+      if (!f) {
-+              ret = 0;
-+              goto release_sem;
-+      }
+       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) {
 +
-+      ret = jffs2_garbage_collect_live(c, jeb, raw, f);
++      if (!jeb->free_size && !jeb->dirty_size && !ISDIRTY(jeb->wasted_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);
++              }
 +
-+      jffs2_gc_release_inode(c, f);
+               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));
 +
-+ release_sem:
-+      up(&c->alloc_sem);
++      spin_unlock(&c->erase_completion_lock);
+       return 0;
+ }
+@@ -280,20 +365,34 @@
+       up(&c->alloc_sem);
+ }
++static inline int on_list(struct list_head *obj, struct list_head *head)
++{
++      struct list_head *this;
 +
-+ eraseit_lock:
-+      /* If we've finished this block, start it erasing */
-+      spin_lock(&c->erase_completion_lock);
++      list_for_each(this, head) {
++              if (this == obj) {
++                      D1(printk("%p is on list at %p\n", obj, head));
++                      return 1;
 +
-+ 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;
++      return 0;
 +}
 +
-+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;
+ 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,91 +401,439 @@
+               BUG();
+       }
+       jeb = &c->blocks[blocknr];
+-      if (jeb->used_size < ref->totlen) {
 +
-       down(&f->sem);
++      if (jffs2_can_mark_obsolete(c) && !jffs2_is_readonly(c) &&
++          !(c->flags & (JFFS2_SB_FLAG_SCANNING | JFFS2_SB_FLAG_BUILDING))) {
++              /* Hm. This may confuse static lock analysis. If any of the above 
++                 three conditions is false, we're going to return from this 
++                 function without actually obliterating any nodes or freeing
++                 any jffs2_raw_node_refs. So we don't need to stop erases from
++                 happening, or protect against people holding an obsolete
++                 jffs2_raw_node_ref without the erase_completion_lock. */
++              down(&c->erase_free_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;
++      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);
        }
-+      spin_unlock(&c->erase_completion_lock);
+-      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;
++      // 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);
 +
-       /* 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;
-@@ -206,13 +441,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;
++              /* 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;
++                      }
 +              }
-               /* 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;
-       }
-@@ -224,12 +468,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;
-@@ -237,53 +482,207 @@
-       }
-  upnout:
-       up(&f->sem);
--      up(&c->alloc_sem);
--      iput(inode);
++      } 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);
  
-- eraseit_lock:
--      /* If we've finished this block, start it erasing */
--      spin_lock_bh(&c->erase_completion_lock);
-+      return ret;
-+}
+-      ACCT_PARANOIA_CHECK(jeb);
++      D1(ACCT_PARANOIA_CHECK(jeb));
  
-- 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 (c->flags & JFFS2_SB_FLAG_MOUNTING) {
+-              /* Mount in progress. Don't muck about with the block
++      if (c->flags & JFFS2_SB_FLAG_SCANNING) {
++              /* Flash scanning is in progress. Don't muck about with the block
+                  lists because they're not ready yet, and don't actually
+                  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);
++              /* We didn't lock the erase_free_sem */
+               return;
+       }
 +
-+      if (alloclen < rawlen) {
-+              /* Doesn't fit untouched. We'll go the old route and split it */
-+              return -EBADFD;
+       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);
  
-+      node = kmalloc(rawlen, GFP_KERNEL);
-+      if (!node)
-+               return -ENOMEM;
+-      if (c->mtd->type != MTD_NORFLASH && c->mtd->type != MTD_RAM)
+-              return;
+-      if (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
++      spin_unlock(&c->erase_completion_lock);
 +
-+      ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node);
-+      if (!ret && retlen != rawlen)
-+              ret = -EIO;
-+      if (ret)
-+              goto out_node;
++      if (!jffs2_can_mark_obsolete(c) || jffs2_is_readonly(c) ||
++              (c->flags & JFFS2_SB_FLAG_BUILDING)) {
++              /* We didn't lock the erase_free_sem */
+               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);
++      /* The erase_free_sem is locked, and has been since before we marked the node obsolete
++         and potentially put its eraseblock onto the erase_pending_list. Thus, we know that
++         the block hasn't _already_ been erased, and that 'ref' itself hasn't been freed yet
++         by jffs2_free_all_node_refs() in erase.c. Which is nice. */
 +
-+      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;
++      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);
+-              return;
++              printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
++              goto out_erase_sem;
+       }
+       if (retlen != sizeof(n)) {
+-              printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen);
+-              return;
++              printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
++              goto out_erase_sem;
+       }
+-      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);
+-              return;
++      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));
++              goto out_erase_sem;
+       }
+-      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));
+-              return;
++      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)));
++              goto out_erase_sem;
+       }
+-      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);
+-              return;
++              printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
++              goto out_erase_sem;
+       }
+       if (retlen != sizeof(n)) {
+-              printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen);
+-              return;
++              printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
++              goto out_erase_sem;
 +      }
 +
-+      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;
-+              }
++      /* Nodes which have been marked obsolete no longer need to be
++         associated with any inode. Remove them from the per-inode list.
++         
++         Note we can't do this for NAND at the moment because we need 
++         obsolete dirent nodes to stay on the lists, because of the
++         horridness in jffs2_garbage_collect_deletion_dirent(). Also
++         because we delete the inocache, and on NAND we need that to 
++         stay around until all the nodes are actually erased, in order
++         to stop us from giving the same inode number to another newly
++         created inode. */
++      if (ref->next_in_ino) {
++              struct jffs2_inode_cache *ic;
++              struct jffs2_raw_node_ref **p;
 +
-+              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;
-+                      }
-+              }
-+              break;
++              spin_lock(&c->erase_completion_lock);
 +
-+      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;
-+              }
++              ic = jffs2_raw_ref_to_ic(ref);
++              for (p = &ic->nodes; (*p) != ref; p = &((*p)->next_in_ino))
++                      ;
 +
-+              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;
-+      }
++              *p = ref->next_in_ino;
++              ref->next_in_ino = NULL;
 +
-+      nraw = jffs2_alloc_raw_node_ref();
-+      if (!nraw) {
-+              ret = -ENOMEM;
-+              goto out_node;
-+      }
++              if (ic->nodes == (void *)ic)
++                      jffs2_del_ino_cache(c, ic);
 +
-+      /* 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;
++              spin_unlock(&c->erase_completion_lock);
+       }
 +
-+      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;
++      /* Merge with the next node in the physical list, if there is one
++         and if it's also obsolete and if it doesn't belong to any inode */
++      if (ref->next_phys && ref_obsolete(ref->next_phys) &&
++          !ref->next_phys->next_in_ino) {
++              struct jffs2_raw_node_ref *n = ref->next_phys;
++              
++              spin_lock(&c->erase_completion_lock);
 +
-+                      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);
++              ref->__totlen += n->__totlen;
++              ref->next_phys = n->next_phys;
++                if (jeb->last_node == n) jeb->last_node = ref;
++              if (jeb->gc_node == n) {
++                      /* gc will be happy continuing gc on this node */
++                      jeb->gc_node=ref;
 +              }
-+              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);
++              spin_unlock(&c->erase_completion_lock);
 +
-+                      if (!ret) {
-+                              D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", phys_ofs));
++              jffs2_free_raw_node_ref(n);
++      }
++      
++      /* Also merge with the previous node in the list, if there is one
++         and that one is obsolete */
++      if (ref != jeb->first_node ) {
++              struct jffs2_raw_node_ref *p = jeb->first_node;
 +
-+                              ACCT_SANITY_CHECK(c,jeb);
-+                              D1(ACCT_PARANOIA_CHECK(jeb));
++              spin_lock(&c->erase_completion_lock);
 +
-+                              goto retry;
++              while (p->next_phys != ref)
++                      p = p->next_phys;
++              
++              if (ref_obsolete(p) && !ref->next_in_ino) {
++                      p->__totlen += ref->__totlen;
++                      if (jeb->last_node == ref) {
++                              jeb->last_node = p;
 +                      }
-+                      D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
-+                      jffs2_free_raw_node_ref(nraw);
++                      if (jeb->gc_node == ref) {
++                              /* gc will be happy continuing gc on this node */
++                              jeb->gc_node=p;
++                      }
++                      p->next_phys = ref->next_phys;
++                      jffs2_free_raw_node_ref(ref);
 +              }
-+
-+              jffs2_free_raw_node_ref(nraw);
-+              if (!ret)
-+                      ret = -EIO;
-+              goto out_node;
++              spin_unlock(&c->erase_completion_lock);
 +      }
-+      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-core, the 'nraw' pointer is then
-+         going to be fetched from ic->nodes by our caller. */
-+      spin_lock(&c->erase_completion_lock);
-+        nraw->next_in_ino = ic->nodes;
-+        ic->nodes = nraw;
-+      spin_unlock(&c->erase_completion_lock);
-+
-+      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) {
-                       printk(KERN_WARNING "kmalloc of mdata failed in jffs2_garbage_collect_metadata()\n");
-                       return -ENOMEM;
-               }
--              ret = jffs2_read_dnode(c, fn, mdata, 0, mdatalen);
-+              ret = jffs2_read_dnode(c, f, fn, mdata, 0, mdatalen);
-               if (ret) {
-                       printk(KERN_WARNING "read of old metadata failed in jffs2_garbage_collect_metadata(): %d\n", ret);
-                       kfree(mdata);
-@@ -295,34 +694,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.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 = 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.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));
-@@ -333,41 +732,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));
-@@ -378,19 +776,97 @@
- }
- 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;
++ out_erase_sem:
++      up(&c->erase_free_sem);
++}
 +
-+              /* 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);
++#if CONFIG_JFFS2_FS_DEBUG >= 2
++void jffs2_dump_block_lists(struct jffs2_sb_info *c)
++{
 +
-+              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;
++      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);
 +
-+                      /* Any dirent with the same name is going to have the same length... */
-+                      if (ref_totlen(c, NULL, raw) != rawlen)
-+                              continue;
++      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;
 +
-+                      /* Doesn't matter if there's one in the same erase block. We're going to 
-+                         delete it too at the same time. */
-+                      if (SECTOR_ADDR(raw->flash_offset) == SECTOR_ADDR(fd->raw->flash_offset))
-+                              continue;
++              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;
 +
-+                      D1(printk(KERN_DEBUG "Check potential deletion dirent at %08x\n", ref_offset(raw)));
++              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;
 +
-+                      /* 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 %u) reading header from obsolete node at %08x\n",
-+                                     retlen, rawlen, ref_offset(raw));
-+                              continue;
-+                      }
++              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;
 +
-+                      if (je16_to_cpu(rd->nodetype) != JFFS2_NODETYPE_DIRENT)
-+                              continue;
++              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;
 +
-+                      /* If the name CRC doesn't match, skip */
-+                      if (je32_to_cpu(rd->name_crc) != name_crc)
-+                              continue;
++              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;
 +
-+                      /* 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;
++              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;
 +
-+                      /* OK, check the actual name now */
-+                      if (memcmp(rd->name, fd->name, name_len))
-+                              continue;
++              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;
 +
-+                      /* 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);
++              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;
 +
-+                      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);
++              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;
 +
-+                      return jffs2_garbage_collect_dirent(c, jeb, f, fd);
++              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 */
 +
-+              up(&c->erase_free_sem);
-+              kfree(rd);
++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;
 +      }
 +
-+      /* No need for it any more. Just mark it obsolete and remove it from the list */
-       while (*fdp) {
-               if ((*fdp) == fd) {
-                       found = 1;
-@@ -400,7 +876,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);
-@@ -408,93 +884,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.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 = inode->i_ino;
--              ri.version = ++f->highest_version;
--              ri.offset = start;
--              ri.dsize = end - start;
--              ri.csize = 0;
-+              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);
-@@ -510,12 +988,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);
++      /* 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;
 +
-+      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) {
-@@ -540,49 +1023,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;
++      if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger && 
++                      (dirty > c->nospc_dirty_size)) 
++              ret = 1;
 +
-+              min = start & ~(PAGE_CACHE_SIZE-1);
-+              max = min + PAGE_CACHE_SIZE;
++      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"));
 +
-+              frag = jffs2_lookup_node_frag(&f->fragtree, start);
++      return ret;
+ }
+--- /dev/null
++++ linux-2.4.21/fs/jffs2/os-linux.h
+@@ -0,0 +1,227 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2002-2003 Red Hat, Inc.
++ *
++ * Created by David Woodhouse <dwmw2@infradead.org>
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * $Id$
++ *
++ */
 +
-+              /* BUG_ON(!frag) but that'll happen anyway... */
++#ifndef __JFFS2_OS_LINUX_H__
++#define __JFFS2_OS_LINUX_H__
++#include <linux/version.h>
 +
-+              BUG_ON(frag->ofs != start);
++/* JFFS2 uses Linux mode bits natively -- no need for conversion */
++#define os_to_jffs2_mode(x) (x)
++#define jffs2_to_os_mode(x) (x)
 +
-+              /* First grow down... */
-+              while((frag = frag_prev(frag)) && frag->ofs >= min) {
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,73)
++#define kstatfs statfs
++#endif
 +
-+                      /* 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 {
++struct kstatfs;
++struct kvec;
 +
-+                              /* 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. */
++#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
 +
-+                              struct jffs2_raw_node_ref *raw = frag->node->raw;
-+                              struct jffs2_eraseblock *jeb;
 +
-+                              jeb = &c->blocks[raw->flash_offset / c->sector_size];
++#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 (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;
-+                              }
++#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
 +
-+                              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;
-+                      }
-+              }
++/* 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
 +
-+              /* ... then up */
++#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
++}
 +
-+              /* 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) {
++#define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
++#define jffs2_is_writebuffered(c) (c->wbuf != NULL)
 +
-+                      /* 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;
-+                      }
++#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
++#define SECTOR_ADDR(x) ( ((unsigned long)(x) & ~(c->sector_size-1)) )
++#define jffs2_can_mark_obsolete(c) (1)
++#define jffs2_cleanmarker_oob(c) (0)
++#define jffs2_write_nand_cleanmarker(c,jeb) (-EIO)
 +
-+                      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 {
++#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_write_nand_badblock(c,jeb,bad_offset) (1)
++#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
++#define jffs2_nor_ecc(c) (0)
++#define jffs2_dataflash(c) (0)
++#define jffs2_nor_ecc_flash_setup(c) (0)
++#define jffs2_nor_ecc_flash_cleanup(c) do {} while (0)
 +
-+                              /* 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. */
++#else /* NAND and/or ECC'd NOR support present */
 +
-+                              struct jffs2_raw_node_ref *raw = frag->node->raw;
-+                              struct jffs2_eraseblock *jeb;
++#define SECTOR_ADDR(x) ( ((unsigned long)(x) / (unsigned long)(c->sector_size)) * c->sector_size )
++#define jffs2_can_mark_obsolete(c) ((c->mtd->type == MTD_NORFLASH && !(c->mtd->flags & MTD_ECC)) || c->mtd->type == MTD_RAM)
++#define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH)
 +
-+                              jeb = &c->blocks[raw->flash_offset / c->sector_size];
++#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)
 +
-+                              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;
-+                              }
++/* wbuf.c */
++int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *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, uint32_t bad_offset);
++void jffs2_wbuf_timeout(unsigned long data);
++void jffs2_wbuf_process(void *data);
++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_nand_flash_setup(struct jffs2_sb_info *c);
++void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c);
 +
-+                              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));
++#define jffs2_nor_ecc(c) (c->mtd->type == MTD_NORFLASH && (c->mtd->flags & MTD_ECC))
++int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c);
++void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c);
 +
-+              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 */
-@@ -592,63 +1172,58 @@
-        *    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;
--              char comprtype = JFFS2_COMPR_NONE;
-+              uint32_t datalen;
-+              uint32_t cdatalen;
-+              uint16_t 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);
-+              comprtype = jffs2_compress(c, f, writebuf, &comprbuf, &datalen, &cdatalen);
--              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;
--              ri.compr = comprtype;
--              ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
--              ri.data_crc = crc32(0, writebuf, 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));
-       
--              new_fn = jffs2_write_dnode(inode, &ri, writebuf, cdatalen, phys_ofs, NULL);
-+              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 & 0xff;
-+              ri.usercompr = (comprtype >> 8) & 0xff;
-+              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);
++#define jffs2_dataflash(c) (c->mtd->type == MTD_DATAFLASH)
++int jffs2_dataflash_setup(struct jffs2_sb_info *c);
++void jffs2_dataflash_cleanup(struct jffs2_sb_info *c);
 +
-+              jffs2_free_comprbuf(comprbuf, writebuf);
-               if (IS_ERR(new_fn)) {
-                       printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
-@@ -663,12 +1238,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;
- }
---- linux-2.4.21/fs/jffs2/ioctl.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/ioctl.c
-@@ -1,37 +1,13 @@
- /*
-  * JFFS2 -- Journalling Flash File System, Version 2.
-  *
-- * Copyright (C) 2001 Red Hat, Inc.
-- *
-- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
-- *
-- * The original JFFS, from which the design for JFFS2 was derived,
-- * was designed and implemented by Axis Communications AB.
-- *
-- * 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.
-+ * Copyright (C) 2001-2003 Red Hat, Inc.
-  *
-- * The Original Code is JFFS2 - Journalling Flash File System, version 2
-+ * Created by David Woodhouse <dwmw2@infradead.org>
-  *
-- * 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.
-+ * For licensing information, see the file 'LICENCE' in this directory.
-  *
-- * $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;
- }
-       
---- linux-2.4.21/fs/jffs2/malloc.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/malloc.c
-@@ -1,37 +1,13 @@
- /*
-  * JFFS2 -- Journalling Flash File System, Version 2.
-  *
-- * Copyright (C) 2001 Red Hat, Inc.
-- *
-- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
-- *
-- * The original JFFS, from which the design for JFFS2 was derived,
-- * was designed and implemented by Axis Communications AB.
-- *
-- * 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.
-+ * Copyright (C) 2001-2003 Red Hat, Inc.
-  *
-- * The Original Code is JFFS2 - Journalling Flash File System, version 2
-+ * Created by David Woodhouse <dwmw2@infradead.org>
-  *
-- * 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.
-+ * For licensing information, see the file 'LICENCE' in this directory.
-  *
-- * $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)
++#endif /* WRITEBUFFER */
 +
- /* 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);
- }
---- linux-2.4.21/fs/jffs2/nodelist.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/nodelist.c
-@@ -1,44 +1,24 @@
++/* 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);
++int jffs2_flash_setup(struct jffs2_sb_info *c);
++void jffs2_flash_cleanup(struct jffs2_sb_info *c);
++     
++
++/* writev.c */
++int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs, 
++                     unsigned long count, loff_t to, size_t *retlen);
++
++
++#endif /* __JFFS2_OS_LINUX_H__ */
++
++
+--- linux-2.4.21/fs/jffs2/pushpull.h~mtd-cvs
++++ linux-2.4.21/fs/jffs2/pushpull.h
+@@ -1,42 +1,21 @@
  /*
   * JFFS2 -- Journalling Flash File System, Version 2.
   *
-- * Copyright (C) 2001, 2002 Red Hat, Inc.
+- * Copyright (C) 2001 Red Hat, Inc.
 - *
 - * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
 - *
 - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
 - * See the Licence for the specific language governing rights and
 - * limitations under the Licence.
-+ * Copyright (C) 2001-2003 Red Hat, Inc.
++ * Copyright (C) 2001, 2002 Red Hat, Inc.
   *
 - * The Original Code is JFFS2 - Journalling Flash File System, version 2
 + * Created by David Woodhouse <dwmw2@infradead.org>
   *
   */
  
- #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,93 +69,156 @@
-         *prev = tn;
- }
+ #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;
+ };
  
-+static void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn)
+-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)
 +{
-+      struct jffs2_tmp_dnode_info *next;
++      pp->buf = buf;
++      pp->buflen = buflen;
++      pp->ofs = ofs;
++      pp->reserve = reserve;
++}
 +
-+      while (tn) {
-+              next = tn;
-+              tn = tn->next;
-+              jffs2_free_full_dnode(next->fn);
-+              jffs2_free_tmp_dnode_info(next);
++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 void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
++static inline int pushedbits(struct pushpull *pp)
 +{
-+      struct jffs2_full_dirent *next;
++      return pp->ofs;
++}
+ static inline int pullbit(struct pushpull *pp)
+ {
+--- /dev/null
++++ linux-2.4.21/fs/jffs2/rbtree.c
+@@ -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.
 +
-+      while (fd) {
-+              next = fd->next;
-+              jffs2_free_full_dirent(fd);
-+              fd = next;
++  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;
 +}
 +
-+/* Returns first valid node after 'ref'. May return 'ref' */
-+static struct jffs2_raw_node_ref *jffs2_first_valid_node(struct jffs2_raw_node_ref *ref)
++static void __rb_rotate_right(struct rb_node * node, struct rb_root * root)
 +{
-+      while (ref && ref->next_in_ino) {
-+              if (!ref_obsolete(ref))
-+                      return ref;
-+              D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref)));
-+              ref = ref->next_in_ino;
++      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;
 +      }
-+      return NULL;
++      else
++              root->rb_node = left;
++      node->rb_parent = left;
 +}
 +
- /* 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,
-+int jffs2_get_inode_nodes(struct jffs2_sb_info *c, 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_raw_node_ref *ref, *valid_ref;
-       struct jffs2_tmp_dnode_info *tn, *ret_tn = NULL;
-       struct jffs2_full_dirent *fd, *ret_fd = NULL;
--
-       union jffs2_node_union node;
-       size_t retlen;
-       int err;
-       *mctime_ver = 0;
--      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);
--      }
--      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) {
--                      /* 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));
--                      continue;
--              }
--              err = c->mtd->read(c->mtd, (ref->flash_offset & ~3), min(ref->totlen, sizeof(node)), &retlen, (void *)&node);
-+      D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%u\n", f->inocache->ino));
++void rb_insert_color(struct rb_node * node, struct rb_root * root)
++{
++      struct rb_node * parent, * gparent;
 +
-+      spin_lock(&c->erase_completion_lock);
++      while ((parent = node->rb_parent) && parent->rb_color == RB_RED)
++      {
++              gparent = parent->rb_parent;
 +
-+      valid_ref = jffs2_first_valid_node(f->inocache->nodes);
++              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 (!valid_ref && (f->inocache->ino != 1))
-+              printk(KERN_WARNING "Eep. No valid nodes for ino #%u\n", f->inocache->ino);
++                      if (parent->rb_right == node)
++                      {
++                              register struct rb_node * tmp;
++                              __rb_rotate_left(parent, root);
++                              tmp = parent;
++                              parent = node;
++                              node = tmp;
++                      }
 +
-+      while (valid_ref) {
-+              /* We can hold a pointer to a non-obsolete node without the spinlock,
-+                 but _obsolete_ nodes may disappear at any time, if the block
-+                 they're in gets erased. So if we mark 'ref' obsolete while we're
-+                 not holding the lock, it can go away immediately. For that reason,
-+                 we find the next valid node first, before processing 'ref'.
-+              */
-+              ref = valid_ref;
-+              valid_ref = jffs2_first_valid_node(ref->next_in_ino);
-+              spin_unlock(&c->erase_completion_lock);
++                      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;
++                              }
++                      }
 +
-+              cond_resched();
++                      if (parent->rb_left == node)
++                      {
++                              register struct rb_node * tmp;
++                              __rb_rotate_right(parent, root);
++                              tmp = parent;
++                              parent = node;
++                              node = tmp;
++                      }
 +
-+              /* 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();
++                      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 (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();
++                      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;
 +                      }
-+                      
-                       fd = jffs2_alloc_full_dirent(node.d.nsize+1);
-                       if (!fd) {
-                               err = -ENOMEM;
-                               goto free_out;
-                       }
--                      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;
-@@ -188,6 +231,7 @@
-                       }
-                       fd->nhash = full_name_hash(fd->name, node.d.nsize);
-                       fd->next = NULL;
-+                      fd->name[node.d.nsize] = '\0';
-                               /* Wheee. We now have a complete jffs2_full_dirent structure, with
-                                  the name in it and everything. Link it into the list 
-                               */
-@@ -196,21 +240,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 (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->flash_offset & 1) {
--                              D1(printk(KERN_DEBUG "obsoleted\n"));
--                              /* Obsoleted */
-+                      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();
++                      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;
 +                      }
-+
-+                      /* 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;
++                      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;
++}
 +
-+                              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
++void rb_erase(struct rb_node * node, struct rb_root * root)
++{
++      struct rb_node * child, * parent;
++      int color;
 +
-+                                      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;
-                       }
-+                                      
-+                              }
++      if (!node->rb_left)
++              child = node->rb_right;
++      else if (!node->rb_right)
++              child = node->rb_left;
++      else
++      {
++              struct rb_node * old = node, * left;
 +
-+                              /* 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);
++              node = node->rb_right;
++              while ((left = node->rb_left))
++                      node = left;
++              child = node->rb_right;
++              parent = node->rb_parent;
++              color = node->rb_color;
 +
-+                              jeb->used_size += len;
-+                              jeb->unchecked_size -= len;
-+                              c->used_size += len;
-+                              c->unchecked_size -= len;
++              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 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 (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 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);
-+                      }
++              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;
 +
-                       tn = jffs2_alloc_tmp_dnode_info();
-                       if (!tn) {
-                               D1(printk(KERN_DEBUG "alloc tn failed\n"));
-@@ -225,36 +374,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;
++              old->rb_left->rb_parent = node;
++              if (old->rb_right)
++                      old->rb_right->rb_parent = node;
++              goto color;
++      }
 +
-+                              printk(KERN_ERR "Eep. Unknown node type %04x at %08x was marked REF_UNCHECKED\n",
-+                                     je16_to_cpu(node.u.nodetype), ref_offset(ref));
++      parent = node->rb_parent;
++      color = node->rb_color;
 +
-+                              /* 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);
++      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;
 +
-+                              jeb->used_size += len;
-+                              jeb->unchecked_size -= len;
-+                              c->used_size += len;
-+                              c->unchecked_size -= len;
++ color:
++      if (color == RB_BLACK)
++              __rb_erase_color(child, parent, root);
++}
++#endif /* Before 2.4.11 */
 +
-+                              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;
-                       }
++      /* 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;
++      }
 +
-               }
-+              spin_lock(&c->erase_completion_lock);
++      /* 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;
 +
-       }
-+      spin_unlock(&c->erase_completion_lock);
-       *tnp = ret_tn;
-       *fdp = ret_fd;
-@@ -266,19 +455,30 @@
-       return err;
- }
-+void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state)
++      return node->rb_parent;
++}
++
++struct rb_node *rb_prev(struct rb_node *node)
 +{
-+      spin_lock(&c->inocache_lock);
-+      ic->state = state;
-+      wake_up(&c->inocache_wq);
-+      spin_unlock(&c->inocache_lock);
++      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;
 +}
 +
-+/* 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)
++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;
++}
+--- linux-2.4.21/fs/jffs2/read.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/read.c
+@@ -1,52 +1,32 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * 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.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * 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.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $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 "crc32.h"
++#include "compr.h"
+-int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len)
++int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
++                   struct jffs2_full_dnode *fd, unsigned char *buf,
++                   int ofs, int len)
  {
-       struct jffs2_inode_cache *ret;
+       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 +35,41 @@
+       if (!ri)
+               return -ENOMEM;
  
-       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;
+-      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 +86,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 +109,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;
        }
--      spin_unlock(&c->inocache_lock);
--
-       if (ret && ret->ino != ino)
-               ret = NULL;
-@@ -290,6 +490,8 @@
- {
-       struct jffs2_inode_cache **prev;
-       D2(printk(KERN_DEBUG "jffs2_add_ino_cache: Add %p (ino #%u)\n", new, new->ino));
-+      BUG_ON(!new->ino);
-+
-       spin_lock(&c->inocache_lock);
-       
-       prev = &c->inocache_list[new->ino % INOCACHE_HASHSIZE];
-@@ -299,13 +501,14 @@
+       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(c, f, ri->compr | (ri->usercompr << 8), 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;
+               }
        }
-       new->next = *prev;
-       *prev = new;
-+
-       spin_unlock(&c->inocache_lock);
- }
  
- void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old)
- {
-       struct jffs2_inode_cache **prev;
--      D2(printk(KERN_DEBUG "jffs2_del_ino_cache: Del %p (ino #%u)\n", old, old->ino));
-+      D1(printk(KERN_DEBUG "jffs2_del_ino_cache: Del %p (ino #%u)\n", old, old->ino));
-       spin_lock(&c->inocache_lock);
-       
-       prev = &c->inocache_list[old->ino % INOCACHE_HASHSIZE];
-@@ -316,6 +519,15 @@
-       if ((*prev) == old) {
-               *prev = old->next;
+-      if (len < ri->dsize) {
++      if (len < je32_to_cpu(ri->dsize)) {
+               memcpy(buf, decomprbuf+ofs, len);
        }
-+
-+      /* Free it now unless it's in READING or CLEARING state, which
-+         are the transitions upon read_inode() and clear_inode(). The
-+         rest of the time we know nobody else is looking at it, and 
-+         if it's held by read_inode() or clear_inode() they'll free it
-+         for themselves. */
-+      if (old->state != INO_STATE_READING && old->state != INO_STATE_CLEARING)
-+              jffs2_free_inode_cache(old);
-+
-       spin_unlock(&c->inocache_lock);
- }
+  out_decomprbuf:
+@@ -161,3 +151,66 @@
  
-@@ -328,7 +540,6 @@
-               this = c->inocache_list[i];
-               while (this) {
-                       next = this->next;
--                      D2(printk(KERN_DEBUG "jffs2_free_ino_caches: Freeing ino #%u at %p\n", this->ino, this));
-                       jffs2_free_inode_cache(this);
-                       this = next;
-               }
-@@ -352,3 +563,128 @@
-       }
+       return ret;
  }
-       
-+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)
++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;
-+      struct jffs2_node_frag *parent;
++      int ret;
 +
-+      if (!root->rb_node)
-+              return;
++      D1(printk(KERN_DEBUG "jffs2_read_inode_range: ino #%u, range 0x%08x-0x%08x\n",
++                f->inocache->ino, offset, offset+len));
 +
-+      frag = (rb_entry(root->rb_node, struct jffs2_node_frag, rb));
++      frag = jffs2_lookup_node_frag(&f->fragtree, offset);
 +
-+      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);
++      /* 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);
++                              D2(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;
-+              }
-+              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);
++              } 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;
-+              }
-+
-+              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);
++              } else {
++                      uint32_t readlen;
++                      uint32_t fragofs; /* offset within the frag to start reading */
 +                      
-+                      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;
++                      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, f, 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"));
 +              }
-+
-+              jffs2_free_node_frag(frag);
-+              frag = parent;
-+
-+              cond_resched();
 +      }
++      return 0;
 +}
 +
-+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);
-+}
---- linux-2.4.21/fs/jffs2/nodelist.h~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/nodelist.h
-@@ -1,48 +1,35 @@
+--- linux-2.4.21/fs/jffs2/readinode.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/readinode.c
+@@ -1,79 +1,124 @@
  /*
   * JFFS2 -- Journalling Flash File System, Version 2.
   *
   *
   */
  
-+#ifndef __JFFS2_NODELIST_H__
-+#define __JFFS2_NODELIST_H__
-+
- #include <linux/config.h>
+-/* 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/types.h>
-+#include <linux/jffs2.h>
- #include <linux/jffs2_fs_sb.h>
- #include <linux/jffs2_fs_i.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 "crc32.h"
  
-+#ifdef __ECOS
-+#include "os-ecos.h"
-+#else
-+#include <linux/mtd/compatmac.h> /* For min/max in older kernels */
-+#include "os-linux.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 >= 2
++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 (f->metadata) {
+-              printk(KERN_DEBUG "metadata at 0x%08x\n", f->metadata->raw->flash_offset &~3);
++      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);
+-int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
++      if (f->metadata) {
++              printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw));
++      }
++}
 +#endif
 +
- #ifndef CONFIG_JFFS2_FS_DEBUG
--#define CONFIG_JFFS2_FS_DEBUG 2
-+#define CONFIG_JFFS2_FS_DEBUG 1
- #endif
++#if CONFIG_JFFS2_FS_DEBUG >= 1
++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 +127,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 +167,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 && (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);
+               }
+-              newfrag->next = NULL;
+-              *prev = newfrag;
++                      rb_insert_color(&holefrag->rb, list);
++                      this = holefrag;
++              }
++              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;
+       }
  
- #if CONFIG_JFFS2_FS_DEBUG > 0
-@@ -57,6 +44,39 @@
- #define D2(x)
- #endif
+-      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));
  
-+#define JFFS2_NATIVE_ENDIAN
+-      /* 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) {
 +
-+/* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from
-+   whatever OS we're actually running on here too. */
++              /* 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 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)})
++              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;
 +
-+#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))})
++                      /* Adjust size of original 'this' */
+                       this->size = newfrag->ofs - this->ofs;
 +
-+#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))})
++                      /* 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;
 +
-+#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
++              /* 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;
 +
- /*
-   This is all we need to keep in-core for each raw node during normal
-   operation. As and when we do read_inode on a particular inode, we can
-@@ -71,27 +91,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.
--      */
--};
--
--/* 
--   Used for keeping track of deletion nodes &c, which can only be marked
--   as obsolete when the node which they mark as deleted has actually been 
--   removed from the flash.
--*/
--struct jffs2_raw_node_ref_list {
--      struct jffs2_raw_node_ref *rew;
--      struct jffs2_raw_node_ref_list *next;
--};
-+         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)
- /* For each inode in the filesystem, we need to keep a record of
-    nlink, because it would be a PITA to scan the whole directory tree
-@@ -101,20 +115,30 @@
-    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;
- };
++                      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;
  
--struct jffs2_scan_info {
--      struct jffs2_full_dirent *dents;
--      struct jffs2_tmp_dnode_info *tmpnodes;
--};
-+/* 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 INO_STATE_CLEARING    6       /* In clear_inode() */
+       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;
 +
-+#define INOCACHE_HASHSIZE 128
++      /* 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);
 +
- /*
-   Larger representation of a raw node, kept in-core only when the 
-   struct inode for this particular ino is instantiated.
-@@ -123,12 +147,11 @@
- 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; /* The offset to which the data of this node belongs */
-+      uint32_t size;
-+      uint32_t frags; /* Number of fragments which currently refer
-                       to this node. When this reaches zero, 
--                      the node is obsolete.
--                   */
-+                      the node is obsolete.  */
- };
+       return 0;
+ }
  
- /* 
-@@ -140,144 +163,265 @@
+-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_tmp_dnode_info *next;
-       struct jffs2_full_dnode *fn;
--      __u32 version;
-+      uint32_t version;
- };       
++      struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size);
++
+       D1(printk(KERN_DEBUG "Truncating fraglist to 0x%08x bytes\n", size));
  
- 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];
- };
+-      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;
++      /* 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;
+               }
+-              list = &(*list)->next;
++              frag = frag_next(frag);
++      }
++      while (frag && frag->ofs >= size) {
++              struct jffs2_node_frag *next = frag_next(frag);
 +
- /*
-   Fragments - used to build a map of which raw node to obtain 
-   data from for each part of the ino
- */
- 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; /* The offset to which this fragment belongs */
- };
++              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;
+       }
+ }
  
- 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 */
+ /* Scan the list of all nodes present for this ino, build map of versions, etc. */
  
--      __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;
+-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"));
  
-       struct jffs2_raw_node_ref *gc_node;     /* Next node to be garbage collected */
--
--      /* For deletia. When a dirent node in this eraseblock is
--         deleted by a node elsewhere, that other node can only 
--         be marked as obsolete when this block is actually erased.
--         So we keep a list of the nodes to mark as obsolete when
--         the erase is completed.
--      */
--      // MAYBE        struct jffs2_raw_node_ref_list *deletia;
- };
+-      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);
  
- #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)
+-      f = JFFS2_INODE_INFO(inode);
+-      c = JFFS2_SB_INFO(inode->i_sb);
++      D2(printk(KERN_DEBUG "jffs2_do_read_inode(): Got inocache at %p\n", f->inocache));
  
-+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))) { \
-+                                if (!ref2->next_phys) \
-+                                     printk("ref for node at %p (phys %08x) has next_phys->%p (----), last_node->%p (phys %08x)\n", \
-+                                           ref2, ref_offset(ref2), ref2->next_phys, \
-+                                           jeb->last_node, ref_offset(jeb->last_node)); \
-+                                else \
-+                                       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)
+-      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));
++      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;
  
-+/* 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];
+-      if (!f->inocache && inode->i_ino == 1) {
++              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;
 +
-+              /* Last node in block. Use free_space */
-+              BUG_ON(ref != jeb->last_node);
-+              ref_end = jeb->offset + c->sector_size - jeb->free_size;
++              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;
++
++              default:
++                      BUG();
++              }
 +      }
-+      return ref_end - ref_offset(ref);
++      spin_unlock(&c->inocache_lock);
++
++      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);
 +}
 +
-+static inline uint32_t ref_totlen(struct jffs2_sb_info *c,
-+                                struct jffs2_eraseblock *jeb,
-+                                struct jffs2_raw_node_ref *ref)
++int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
 +{
-+      uint32_t ret;
++      struct jffs2_raw_inode n;
++      struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL);
++      int 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 (!f)
++              return -ENOMEM;
 +
-+#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();
++      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);
 +      }
-+#endif
++      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;
 +
- #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 */
++      D1(printk(KERN_DEBUG "jffs2_do_read_inode_internal(): ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink));
  
--#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))
+       /* 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, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);
  
-+/* check if dirty space is more than 255 Byte */
-+#define ISDIRTY(size) ((size) >  sizeof (struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN) 
+       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;
  
- #define PAD(x) (((x)+3)&~3)
+@@ -304,219 +527,217 @@
  
--static inline int jffs2_raw_ref_to_inum(struct jffs2_raw_node_ref *raw)
-+static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw)
- {
-       while(raw->next_in_ino) {
-               raw = raw->next_in_ino;
-       }
+               fn = tn->fn;
  
--      return ((struct jffs2_inode_cache *)raw)->ino;
-+      return ((struct jffs2_inode_cache *)raw);
- }
+-              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;
++                      }
+               }
  
-+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);
+               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));
 +
- /* nodelist.c */
--D1(void jffs2_print_frag_list(struct jffs2_inode_info *f));
-+D2(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,
-+int jffs2_get_inode_nodes(struct jffs2_sb_info *c, 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);
+       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;
++                              if (f->inocache->state == INO_STATE_READING)
++                                      jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
++                              return -EIO;
+                       }
+-                      printk(KERN_WARNING "jffs2_read_inode(): But it has children so we fake some modes for it\n");
++                      printk(KERN_WARNING "jffs2_do_read_inode(): But it has children so we fake some modes for it\n");
+               }
+-              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;
++              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;
+               }
  
- /* 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);
+-              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;
++      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;
+               }
  
- /* 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);
-+
+-              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;
++      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;
+       }
  
- /* 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);
+-      /* 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;
++      switch(jemode_to_cpu(latest_node->mode) & S_IFMT) {
++      case S_IFDIR:
++              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. */
++                      latest_node->ctime = latest_node->mtime = cpu_to_je32(latest_mctime);
+       }                       
++              break;
+           
+-      inode->i_blksize = PAGE_SIZE;
+-      inode->i_blocks = (inode->i_size + 511) >> 9;
+       
+-      switch (inode->i_mode & S_IFMT) {
+-              unsigned short rdev;
++      case S_IFREG:
++              /* 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;
  
- /* 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);
+       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;
++              if (!je32_to_cpu(latest_node->isize))
++                      latest_node->isize = latest_node->dsize;
+               
+-      case S_IFDIR:
+-              if (mctime_ver > 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;
++              if (f->inocache->state != INO_STATE_CHECKING) {
++                      /* Symlink's inode data is the target path. Read it and
++                       * keep in RAM to facilitate quick follow symlink operation.
++                       * We use f->dents field to store the target path, which
++                       * is somewhat ugly. */
++                      f->dents = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL);
++                      if (!f->dents) {
++                              printk(KERN_WARNING "Can't allocate %d bytes of memory "
++                                              "for the symlink target path cache\n",
++                                              je32_to_cpu(latest_node->csize));
++                              up(&f->sem);
++                              jffs2_do_clear_inode(c, f);
++                              return -ENOMEM;
+               }
+-              inode->i_op = &jffs2_dir_inode_operations;
+-              inode->i_fop = &jffs2_dir_operations;
+-              break;
  
-@@ -301,54 +445,31 @@
- /* gc.c */
- int jffs2_garbage_collect_pass(struct jffs2_sb_info *c);
+-      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;
++                      ret = jffs2_flash_read(c, ref_offset(fn->raw) + sizeof(*latest_node),
++                                              je32_to_cpu(latest_node->csize), &retlen, (char *)f->dents);
++                      
++                      if (ret  || retlen != je32_to_cpu(latest_node->csize)) {
++                              if (retlen != je32_to_cpu(latest_node->csize))
++                                      ret = -EIO;
++                              kfree(f->dents);
++                              f->dents = NULL;
++                              up(&f->sem);
++                              jffs2_do_clear_inode(c, f);
++                              return -ret;
++                      }
++
++                      ((char *)f->dents)[je32_to_cpu(latest_node->csize)] = '\0';
++                      D1(printk(KERN_DEBUG "jffs2_do_read_inode(): symlink's target '%s' cached\n",
++                                              (char *)f->dents));
++              }
++              
++              /* fall through... */
  
--/* 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;
+       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;
+               }                       
 -
--/* 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);
+-      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;
 -
--/* ioctl.c */
--int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+-      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);
+-        /* I don't think we care about the potential race due to reading this
+-           without f->sem. It can never get undeleted. */
+-        int deleted = f->inocache && !f->inocache->nlink;
 -
- /* read.c */
--int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len);
+-      D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
 -
--/* compr.c */
--unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out, 
--                           __u32 *datalen, __u32 *cdatalen);
--int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, 
--                   unsigned char *data_out, __u32 cdatalen, __u32 datalen);
-+int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
-+                   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);
+-      /* If it's a deleted inode, grab the alloc_sem. This prevents
+-         jffs2_garbage_collect_pass() from deciding that it wants to
+-         garbage collect one of the nodes we're just about to mark 
+-         obsolete -- by the time we drop alloc_sem and return, all
+-         the nodes are marked obsolete, and jffs2_g_c_pass() won't
+-         call iget() for the inode in question.
+-      */
+-      if (deleted)
+-              down(&c->alloc_sem);
++      int deleted;
  
- /* scan.c */
- int jffs2_scan_medium(struct jffs2_sb_info *c);
-+void jffs2_rotate_lists(struct jffs2_sb_info *c);
+       down(&f->sem);
++      deleted = f->inocache && !f->inocache->nlink;
++
++      if (f->inocache && f->inocache->state != INO_STATE_CHECKING)
++              jffs2_set_inocache_state(c, f->inocache, INO_STATE_CLEARING);
  
- /* 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);
+-      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);
+       }
  
- /* 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);
+-      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));
+-
+-              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);
++      jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL);
  
--/* compr_zlib.c */
--int jffs2_zlib_init(void);
--void jffs2_zlib_exit(void);
-+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
-+/* 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);
-+#endif
+-                      jffs2_free_full_dnode(frag->node);
+-              }
+-              jffs2_free_node_frag(frag);
++      /* For symlink inodes we us f->dents to store the target path name */
++      if (S_ISLNK(OFNI_EDONI_2SFFJ(f)->i_mode)) {
++              if (f->dents) {
++                      kfree(f->dents);
++                      f->dents = NULL;
+       }
++      } else {
++              fds = f->dents;
 +
-+#endif /* __JFFS2_NODELIST_H__ */
---- linux-2.4.21/fs/jffs2/nodemgmt.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/nodemgmt.c
-@@ -1,45 +1,21 @@
+       while(fds) {
+               fd = fds;
+               fds = fd->next;
+               jffs2_free_full_dirent(fd);
+       }
++      }
+-      up(&f->sem);
+-
+-      if(deleted)
+-              up(&c->alloc_sem);
+-};
++      if (f->inocache && f->inocache->state != INO_STATE_CHECKING) {
++              jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
++              if (f->inocache->nodes == (void *)f->inocache)
++                      jffs2_del_ino_cache(c, f->inocache);
++      }
++      up(&f->sem);
++}
+--- linux-2.4.21/fs/jffs2/scan.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/scan.c
+@@ -1,47 +1,25 @@
  /*
   * JFFS2 -- Journalling Flash File System, Version 2.
   *
 + * $Id$
   *
   */
  #include <linux/kernel.h>
++#include <linux/sched.h>
  #include <linux/slab.h>
 -#include <linux/jffs2.h>
  #include <linux/mtd/mtd.h>
--#include <linux/interrupt.h>
+ #include <linux/pagemap.h>
++#include <linux/crc32.h>
 +#include <linux/compiler.h>
-+#include <linux/sched.h> /* For cond_resched() */
  #include "nodelist.h"
+-#include "crc32.h"
  
- /**
-@@ -62,53 +38,95 @@
-  *    for the requested allocation.
++#define DEFAULT_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,96 @@
+ } 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_WRITEBUFFER
++      if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize)
++              return c->wbuf_pagesize;
++#endif
++      return min;
  
--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);
++}
++
++static inline uint32_t EMPTY_SCAN_SIZE(uint32_t sector_size) {
++      if (sector_size < DEFAULT_EMPTY_SCAN_SIZE)
++              return sector_size;
++      else
++              return DEFAULT_EMPTY_SCAN_SIZE;
++}
  
--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 jffs2_scan_medium(struct jffs2_sb_info *c)
  {
-       int ret = -EAGAIN;
--      int blocksneeded = JFFS2_RESERVED_BLOCKS_WRITE;
-+      int blocksneeded = c->resv_blocks_write;
-       /* align it */
-       minsize = PAD(minsize);
+       int i, ret;
+-      __u32 empty_blocks = 0;
++      uint32_t empty_blocks = 0, bad_blocks = 0;
++      unsigned char *flashbuf = NULL;
++      uint32_t buf_size = 0;
++#ifndef __ECOS
++      size_t pointlen;
  
--      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);
+-      if (!c->blocks) {
+-              printk(KERN_WARNING "EEEK! c->blocks is NULL!\n");
+-              return -EINVAL;
++      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;
++
++              /* Respect kmalloc limitations */
++              if (buf_size > 128*1024)
++                      buf_size = 128*1024;
++
++              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];
  
-       D1(printk(KERN_DEBUG "jffs2_reserve_space(): alloc sem got\n"));
+-              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;
  
--      spin_lock_bh(&c->erase_completion_lock);
-+      spin_lock(&c->erase_completion_lock);
+               ACCT_PARANOIA_CHECK(jeb);
  
--      /* 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;
+               /* 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 +142,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;
 +
-+                      /* 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;
++              case BLK_STATE_CLEANMARKER:
+                       /* Only a CLEANMARKER node is valid */
+                       if (!jeb->dirty_size) {
+                               /* It's actually free */
+@@ -118,74 +159,224 @@
+                               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) && 
++                        /* We want to remember the block with most free space
++                           and stick it in the 'nextblock' position to start writing to it. */
++                        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);
++                                      }
 +                              }
-+                              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));
+                                 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_WRITEBUFFER
++      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 && ((c->nr_free_blocks+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;
++#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
++      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_WRITEBUFFER
++      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;
 +
-+                              spin_unlock(&c->erase_completion_lock);
-+                              up(&c->alloc_sem);
-+                              return -ENOSPC;
++      if (!buf_size) {
++              buf_len = c->sector_size;
++      } else {
++              buf_len = EMPTY_SCAN_SIZE(c->sector_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(c->sector_size) && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
++              ofs += 4;
++
++      if (ofs == EMPTY_SCAN_SIZE(c->sector_size)) {
++#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
++              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;
++                      default:        return ret;
 +                      }
-+                      
-+                      /* 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;
-+                              }
++              }
++#endif
+               D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset));
+-              return 1;       /* special return code */
++              if (c->cleanmarker_size == 0)
++                      return BLK_STATE_CLEANMARKER;   /* don't bother with re-erase */
++              else
++                      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;
  
-+                              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);
++scan_more:    
+       while(ofs < jeb->offset + c->sector_size) {
+-              ssize_t retlen;
+-              ACCT_PARANOIA_CHECK(jeb);
 +
-+                      up(&c->alloc_sem);
++              D1(ACCT_PARANOIA_CHECK(jeb));
 +
-+                      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);
++              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;
                }
-               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));
+               if (ofs == prevofs) {
+@@ -196,102 +387,183 @@
+               }
+               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;
                }
-       }
--      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));
+-              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;
++                      uint32_t empty_start;
++
++                      empty_start = ofs;
++                      ofs += 4;
++
++                      D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs));
++              more_empty:
++                      inbuf_ofs = ofs - buf_ofs;
++                      while (inbuf_ofs < buf_len) {
++                              if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) {
++                                      printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n",
++                                             empty_start, ofs);
++                                      DIRTY_SPACE(ofs-empty_start);
++                                      goto scan_more;
+               }
  
--      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));
+-              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;
+-                      continue;
++                              inbuf_ofs+=4;
++                              ofs += 4;
                }
-       }
--      spin_unlock_bh(&c->erase_completion_lock);
-+      spin_unlock(&c->erase_completion_lock);
-       return ret;
- }
++                      /* Ran off end. */
++                      D1(printk(KERN_DEBUG "Empty flash to end of buffer at 0x%08x\n", ofs));
  
- /* 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);
+-              if (ofs == jeb->offset && node.magic == KSAMTIB_CIGAM_2SFFJ) {
++                      /* If we're only checking the beginning of a block with a cleanmarker,
++                         bail now */
++                      if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) && 
++                          c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_phys) {
++                              D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size)));
++                              return BLK_STATE_CLEANMARKER;
 +                      }
-+              } 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);
++
++                      /* See how much more there is to read in this eraseblock... */
++                      buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
++                      if (!buf_len) {
++                              /* No more to read. Break out of main loop without marking 
++                                 this range of empty space as dirty (because it's not) */
++                              D1(printk(KERN_DEBUG "Empty flash at %08x runs to end of block. Treating as free_space\n",
++                                        empty_start));
++                              break;
++                      }
++                      D1(printk(KERN_DEBUG "Reading another 0x%x at 0x%08x\n", buf_len, ofs));
++                      err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
++                      if (err)
++                              return err;
++                      buf_ofs = ofs;
++                      goto more_empty;
 +              }
-               c->nextblock = jeb = NULL;
-       }
-       
-@@ -164,33 +209,44 @@
++
++              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 (list_empty(&c->free_list)) {
+-              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;
+               }
  
--                      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));
-+                      }
+-              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;
++              }
 +
-+                      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;
++              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;
                        
-                       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;
+               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 +572,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);
                        }
--                      /* 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)) {
+-                      ofs += PAD(sizeof(struct jffs2_unknown_node));
++                      break;
 +
-+              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;
++              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;
+                       }
                }
-@@ -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;
+       }
+-      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;
++      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));
+               
+-              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;
++      /* 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;
+               }
+-              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;
+-      }
++      if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size 
++              && (!jeb->first_node || !jeb->first_node->next_phys) )
++              return BLK_STATE_CLEANMARKER;
+-      D1(printk(KERN_DEBUG "Empty flash detected from 0x%08x to 0x%08x\n", *startofs, curofs));
+-      kfree(buf);
+-      *startofs = curofs;
+-      return 0;
++      /* 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;
  }
-@@ -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,61 @@
-  *    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)
+-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_eraseblock *jeb;
-+      uint32_t len;
+       struct jffs2_inode_cache *ic;
  
--      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));
-+      jeb = &c->blocks[new->flash_offset / c->sector_size];
-+      len = ref_totlen(c, jeb, new);
-+
-+      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)) {
-+      /* we could get some obsolete nodes after nextblock was refiled
-+         in wbuf.c */
-+      if ((c->nextblock || !ref_obsolete(new))
-+          &&(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;
+@@ -399,137 +653,77 @@
+       if (ic)
+               return ic;
  
--      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 (!jeb->free_size && !jeb->dirty_size && !ISDIRTY(jeb->wasted_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);
-+              }
++      if (ino > c->highest_ino)
++              c->highest_ino = ino;
 +
-               list_add_tail(&jeb->list, &c->clean_list);
-               c->nextblock = NULL;
+       ic = jffs2_alloc_inode_cache();
+       if (!ic) {
+               printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of inode cache failed\n");
+               return NULL;
        }
-       ACCT_SANITY_CHECK(c,jeb);
--      ACCT_PARANOIA_CHECK(jeb);
-+      D1(ACCT_PARANOIA_CHECK(jeb));
+       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));
 +
-+      spin_unlock(&c->erase_completion_lock);
-       return 0;
- }
-@@ -280,20 +365,34 @@
-       up(&c->alloc_sem);
+       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 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)
+-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_eraseblock *jeb;
-       int blocknr;
-       struct jffs2_unknown_node n;
+       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;
-+      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,91 +401,439 @@
-               BUG();
-       }
-       jeb = &c->blocks[blocknr];
--      if (jeb->used_size < ref->totlen) {
-+
-+      if (jffs2_can_mark_obsolete(c) && !jffs2_is_readonly(c) &&
-+          !(c->flags & (JFFS2_SB_FLAG_SCANNING | JFFS2_SB_FLAG_BUILDING))) {
-+              /* Hm. This may confuse static lock analysis. If any of the above 
-+                 three conditions is false, we're going to return from this 
-+                 function without actually obliterating any nodes or freeing
-+                 any jffs2_raw_node_refs. So we don't need to stop erases from
-+                 happening, or protect against people holding an obsolete
-+                 jffs2_raw_node_ref without the erase_completion_lock. */
-+              down(&c->erase_free_sem);
-+      }
-+
-+      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);
-       }
+-
+-      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;
+-      }
+-
+-      /* 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;
+-
+-      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;
+-      }
++      uint32_t ino = je32_to_cpu(ri->ino);
  
--      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;
-+      // 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;
-+                      }
-+              }
-+      } 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);
+-      if (ri.csize) {
+-              /* Check data CRC too */
+-              unsigned char *dbuf;
+-              __u32 crc;
++      D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", ofs));
  
--      ACCT_PARANOIA_CHECK(jeb);
-+      D1(ACCT_PARANOIA_CHECK(jeb));
+-              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;
+-              }
+-      }
++      /* 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 (c->flags & JFFS2_SB_FLAG_MOUNTING) {
--              /* Mount in progress. Don't muck about with the block
-+      if (c->flags & JFFS2_SB_FLAG_SCANNING) {
-+              /* Flash scanning is in progress. Don't muck about with the block
-                  lists because they're not ready yet, and don't actually
-                  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);
-+              /* We didn't lock the erase_free_sem */
-               return;
+-      /* 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);
 +
-       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)); 
++      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;
        }
--      spin_unlock_bh(&c->erase_completion_lock);
--      if (c->mtd->type != MTD_NORFLASH && c->mtd->type != MTD_RAM)
--              return;
--      if (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
-+      spin_unlock(&c->erase_completion_lock);
-+
-+      if (!jffs2_can_mark_obsolete(c) || jffs2_is_readonly(c) ||
-+              (c->flags & JFFS2_SB_FLAG_BUILDING)) {
-+              /* We didn't lock the erase_free_sem */
-               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);
-+      /* The erase_free_sem is locked, and has been since before we marked the node obsolete
-+         and potentially put its eraseblock onto the erase_pending_list. Thus, we know that
-+         the block hasn't _already_ been erased, and that 'ref' itself hasn't been freed yet
-+         by jffs2_free_all_node_refs() in erase.c. Which is nice. */
+-      /* Build the data structures and file them for later */
+-      raw->flash_offset = *ofs;
+-      raw->totlen = PAD(ri.totlen);
++      /* Wheee. It worked */
 +
-+      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);
--              return;
-+              printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
-+              goto out_erase_sem;
-       }
-       if (retlen != sizeof(n)) {
--              printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen);
--              return;
-+              printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
-+              goto out_erase_sem;
++      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 +732,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;
+-
+-              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) {
++                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)));
+-                      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));
++      pseudo_random += je32_to_cpu(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;
        }
--      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);
--              return;
-+      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));
-+              goto out_erase_sem;
+-      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 (!(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));
--              return;
-+      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)));
-+              goto out_erase_sem;
+-      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;
        }
--      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);
--              return;
-+              printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
-+              goto out_erase_sem;
+       raw = jffs2_alloc_raw_node_ref();
+@@ -674,15 +790,15 @@
+               printk(KERN_NOTICE "jffs2_scan_dirent_node(): allocation of node reference failed\n");
+               return -ENOMEM;
        }
-       if (retlen != sizeof(n)) {
--              printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen);
--              return;
-+              printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
-+              goto out_erase_sem;
-+      }
-+
-+      /* Nodes which have been marked obsolete no longer need to be
-+         associated with any inode. Remove them from the per-inode list.
-+         
-+         Note we can't do this for NAND at the moment because we need 
-+         obsolete dirent nodes to stay on the lists, because of the
-+         horridness in jffs2_garbage_collect_deletion_dirent(). Also
-+         because we delete the inocache, and on NAND we need that to 
-+         stay around until all the nodes are actually erased, in order
-+         to stop us from giving the same inode number to another newly
-+         created inode. */
-+      if (ref->next_in_ino) {
-+              struct jffs2_inode_cache *ic;
-+              struct jffs2_raw_node_ref **p;
-+
-+              spin_lock(&c->erase_completion_lock);
-+
-+              ic = jffs2_raw_ref_to_ic(ref);
-+              for (p = &ic->nodes; (*p) != ref; p = &((*p)->next_in_ino))
-+                      ;
-+
-+              *p = ref->next_in_ino;
-+              ref->next_in_ino = NULL;
-+
-+              if (ic->nodes == (void *)ic)
-+                      jffs2_del_ino_cache(c, ic);
-+
-+              spin_unlock(&c->erase_completion_lock);
+-      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;
        }
-+
-+
-+      /* Merge with the next node in the physical list, if there is one
-+         and if it's also obsolete and if it doesn't belong to any inode */
-+      if (ref->next_phys && ref_obsolete(ref->next_phys) &&
-+          !ref->next_phys->next_in_ino) {
-+              struct jffs2_raw_node_ref *n = ref->next_phys;
-+              
-+              spin_lock(&c->erase_completion_lock);
-+
-+              ref->__totlen += n->__totlen;
-+              ref->next_phys = n->next_phys;
-+                if (jeb->last_node == n) jeb->last_node = ref;
-+              if (jeb->gc_node == n) {
-+                      /* gc will be happy continuing gc on this node */
-+                      jeb->gc_node=ref;
-+              }
-+              spin_unlock(&c->erase_completion_lock);
-+
-+              jffs2_free_raw_node_ref(n);
-+      }
-+      
-+      /* Also merge with the previous node in the list, if there is one
-+         and that one is obsolete */
-+      if (ref != jeb->first_node ) {
-+              struct jffs2_raw_node_ref *p = jeb->first_node;
-+
-+              spin_lock(&c->erase_completion_lock);
-+
-+              while (p->next_phys != ref)
-+                      p = p->next_phys;
-+              
-+              if (ref_obsolete(p) && !ref->next_in_ino) {
-+                      p->__totlen += ref->__totlen;
-+                      if (jeb->last_node == ref) {
-+                              jeb->last_node = p;
-+                      }
-+                      if (jeb->gc_node == ref) {
-+                              /* gc will be happy continuing gc on this node */
-+                              jeb->gc_node=p;
-+                      }
-+                      p->next_phys = ref->next_phys;
-+                      jffs2_free_raw_node_ref(ref);
-+              }
-+              spin_unlock(&c->erase_completion_lock);
+       
+-      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 +808,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 +838,90 @@
+       struct list_head *n = head->next;
+       list_del(head);
+-      while(count--)
++      while(count--) {
+               n = n->next;
 +      }
-+ out_erase_sem:
-+      up(&c->erase_free_sem);
-+}
-+
-+#if CONFIG_JFFS2_FS_DEBUG >= 2
-+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);
+       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));
 +
-+      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;
++              rotate_list((&c->clean_list), rotateby);
 +
-+              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");
++              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 {
-+              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);
++              D1(printk(KERN_DEBUG "Not rotating empty clean_list\n"));
 +      }
-+      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;
++      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));
 +
-+              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;
++              rotate_list((&c->very_dirty_list), rotateby);
 +
-+              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");
++              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 {
-+              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);
-+              }
++              D1(printk(KERN_DEBUG "Not rotating empty very_dirty_list\n"));
 +      }
-+      if (list_empty(&c->erasable_pending_wbuf_list)) {
-+              printk(KERN_DEBUG "erasable_pending_wbuf_list: empty\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));
+-      if (c->nr_erasing_blocks)
+-              rotate_list((&c->erase_pending_list), pseudo_random % c->nr_erasing_blocks);
++              rotate_list((&c->dirty_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 dirty_list is at %08x\n",
++                        list_entry(c->dirty_list.next, struct jffs2_eraseblock, list)->offset));
 +      } 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);
-+              }
++              D1(printk(KERN_DEBUG "Not rotating empty dirty_list\n"));
 +      }
-+      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;
++      x = count_list(&c->erasable_list);
++      if (x) {
++              rotateby = pseudo_random % x;
++              D1(printk(KERN_DEBUG "Rotating erasable_list by %d\n", rotateby));
 +
-+              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;
++              rotate_list((&c->erasable_list), rotateby);
 +
-+              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);
-+              }
++              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"));
 +      }
-+}
-+#endif /* CONFIG_JFFS2_FS_DEBUG */
 +
-+int jffs2_thread_should_wake(struct jffs2_sb_info *c)
-+{
-+      int ret = 0;
-+      uint32_t dirty;
++      if (c->nr_erasing_blocks) {
++              rotateby = pseudo_random % c->nr_erasing_blocks;
++              D1(printk(KERN_DEBUG "Rotating erase_pending_list by %d\n", rotateby));
 +
-+      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;
-+      }
++              rotate_list((&c->erase_pending_list), rotateby);
 +
-+      /* 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;
++              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 + c->nr_erasing_blocks < c->resv_blocks_gctrigger && 
-+                      (dirty > c->nospc_dirty_size)) 
-+              ret = 1;
++      if (c->nr_free_blocks) {
++              rotateby = pseudo_random % c->nr_free_blocks;
++              D1(printk(KERN_DEBUG "Rotating free_list by %d\n", rotateby));
 +
-+      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"));
++              rotate_list((&c->free_list), rotateby);
 +
-+      return ret;
++              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"));
++      }
  }
 --- /dev/null
-+++ linux-2.4.21/fs/jffs2/os-linux.h
-@@ -0,0 +1,227 @@
++++ linux-2.4.21/fs/jffs2/super-v24.c
+@@ -0,0 +1,170 @@
 +/*
 + * JFFS2 -- Journalling Flash File System, Version 2.
 + *
-+ * Copyright (C) 2002-2003 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
 + *
 + * Created by David Woodhouse <dwmw2@infradead.org>
 + *
 + *
 + */
 +
-+#ifndef __JFFS2_OS_LINUX_H__
-+#define __JFFS2_OS_LINUX_H__
++#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 "compr.h"
++#include "nodelist.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
-+
-+struct kstatfs;
-+struct kvec;
-+
-+#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)
++#ifndef MTD_BLOCK_MAJOR
++#define MTD_BLOCK_MAJOR 31
 +#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 void jffs2_put_super (struct super_block *);
 +
-+static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
++static struct super_operations jffs2_super_operations =
 +{
-+#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)
-+#define jffs2_is_writebuffered(c) (c->wbuf != NULL)
++      .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,
++};
 +
-+#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
-+#define SECTOR_ADDR(x) ( ((unsigned long)(x) & ~(c->sector_size-1)) )
-+#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_write_nand_badblock(c,jeb,bad_offset) (1)
-+#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
-+#define jffs2_nor_ecc(c) (0)
-+#define jffs2_dataflash(c) (0)
-+#define jffs2_nor_ecc_flash_setup(c) (0)
-+#define jffs2_nor_ecc_flash_cleanup(c) do {} while (0)
++static struct super_block *jffs2_read_super(struct super_block *sb, void *data, int silent)
++{
++      struct jffs2_sb_info *c;
++      int ret;
 +
-+#else /* NAND and/or ECC'd NOR support present */
++      D1(printk(KERN_DEBUG "jffs2: read_super for device %s\n", kdevname(sb->s_dev)));
 +
-+#define SECTOR_ADDR(x) ( ((unsigned long)(x) / (unsigned long)(c->sector_size)) * c->sector_size )
-+#define jffs2_can_mark_obsolete(c) ((c->mtd->type == MTD_NORFLASH && !(c->mtd->flags & MTD_ECC)) || c->mtd->type == MTD_RAM)
-+#define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH)
++      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;
++      }
 +
-+#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)
++      c = JFFS2_SB_INFO(sb);
++      memset(c, 0, sizeof(*c));
 +
-+/* wbuf.c */
-+int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *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, uint32_t bad_offset);
-+void jffs2_wbuf_timeout(unsigned long data);
-+void jffs2_wbuf_process(void *data);
-+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_nand_flash_setup(struct jffs2_sb_info *c);
-+void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c);
++      sb->s_op = &jffs2_super_operations;
 +
-+#define jffs2_nor_ecc(c) (c->mtd->type == MTD_NORFLASH && (c->mtd->flags & MTD_ECC))
-+int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c);
-+void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c);
++      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;
++      }
 +
-+#define jffs2_dataflash(c) (c->mtd->type == MTD_DATAFLASH)
-+int jffs2_dataflash_setup(struct jffs2_sb_info *c);
-+void jffs2_dataflash_cleanup(struct jffs2_sb_info *c);
++      ret = jffs2_do_fill_super(sb, data, silent);
++      if (ret) {
++              put_mtd_device(c->mtd);
++              return NULL;
++      }
 +
-+#endif /* WRITEBUFFER */
++      return sb;
++}
 +
-+/* erase.c */
-+static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
++static void jffs2_put_super (struct super_block *sb)
 +{
-+      OFNI_BS_2SFFJ(c)->s_dirt = 1;
-+}
++      struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
 +
-+/* 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);
++      D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n"));
 +
-+/* 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);
++      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);
++      if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
++              vfree(c->blocks);
++      else
++              kfree(c->blocks);
++      jffs2_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"));
++}
 +
-+/* ioctl.c */
-+int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
++static DECLARE_FSTYPE_DEV(jffs2_fs_type, "jffs2", jffs2_read_super);
 +
-+/* symlink.c */
-+extern struct inode_operations jffs2_symlink_inode_operations;
++static int __init init_jffs2_fs(void)
++{
++      int ret;
 +
-+/* 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);
++      printk(KERN_INFO "JFFS2 version 2.2."
++#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
++             " (NAND)"
++#endif
++             " (C) 2001-2003 Red Hat, Inc.\n");
 +
-+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);
-+int jffs2_flash_setup(struct jffs2_sb_info *c);
-+void jffs2_flash_cleanup(struct jffs2_sb_info *c);
-+     
++#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;
++      }
 +
-+/* writev.c */
-+int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs, 
-+                     unsigned long count, loff_t to, size_t *retlen);
++      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_compressors_init();
++      if (ret) {
++              printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n");
++              goto out;
++      }
++      ret = jffs2_create_slab_caches();
++      if (ret) {
++              printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
++              goto out_compressors;
++      }
++      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_compressors:
++      jffs2_compressors_exit();
++ out:
++      return ret;
++}
 +
-+#endif /* __JFFS2_OS_LINUX_H__ */
++static void __exit exit_jffs2_fs(void)
++{
++      jffs2_destroy_slab_caches();
++      jffs2_compressors_exit();
++      unregister_filesystem(&jffs2_fs_type);
++}
 +
++module_init(init_jffs2_fs);
++module_exit(exit_jffs2_fs);
 +
---- linux-2.4.21/fs/jffs2/pushpull.h~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/pushpull.h
-@@ -1,42 +1,21 @@
++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.
+--- linux-2.4.21/fs/jffs2/super.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/super.c
+@@ -1,291 +1,270 @@
  /*
   * JFFS2 -- Journalling Flash File System, Version 2.
   *
 - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
 - * See the Licence for the specific language governing rights and
 - * limitations under the Licence.
-+ * Copyright (C) 2001, 2002 Red Hat, Inc.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
   *
 - * The Original Code is JFFS2 - Journalling Flash File System, version 2
 + * Created by David Woodhouse <dwmw2@infradead.org>
   *
   */
  
- #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)
- {
---- /dev/null
-+++ linux-2.4.21/fs/jffs2/rbtree.c
-@@ -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>
+ #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 "compr.h"
+ #include "nodelist.h"
+-#ifndef MTD_BLOCK_MAJOR
+-#define MTD_BLOCK_MAJOR 31
+-#endif
++static void jffs2_put_super(struct super_block *);
+-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 *);
++static kmem_cache_t *jffs2_inode_cachep;
 +
-+/* 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)
++static struct inode *jffs2_alloc_inode(struct super_block *sb)
 +{
-+      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;
++      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 __rb_rotate_right(struct rb_node * node, struct rb_root * root)
++static void jffs2_destroy_inode(struct inode *inode)
 +{
-+      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;
++      kmem_cache_free(jffs2_inode_cachep, JFFS2_INODE_INFO(inode));
 +}
 +
-+void rb_insert_color(struct rb_node * node, struct rb_root * root)
++static void jffs2_i_init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
 +{
-+      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;
-+                      }
++      struct jffs2_inode_info *ei = (struct jffs2_inode_info *) foo;
 +
-+                      parent->rb_color = RB_BLACK;
-+                      gparent->rb_color = RB_RED;
-+                      __rb_rotate_left(gparent, root);
-+              }
++      if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
++          SLAB_CTOR_CONSTRUCTOR) {
++              init_MUTEX_LOCKED(&ei->sem);
++              inode_init_once(&ei->vfs_inode);
 +      }
-+
-+      root->rb_node->rb_color = RB_BLACK;
 +}
 +
-+static void __rb_erase_color(struct rb_node * node, struct rb_node * parent,
-+                           struct rb_root * root)
++static int jffs2_sync_fs(struct super_block *sb, int wait)
 +{
-+      struct rb_node * other;
++      struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
 +
-+      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;
-+                      }
-+              }
++      down(&c->alloc_sem);
++      jffs2_flush_wbuf_pad(c);
++      up(&c->alloc_sem);      
++      return 0;
++}
+ 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,
++      .sync_fs =      jffs2_sync_fs,
+ };
+-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;
++      /* 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 {
++              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 (node)
-+              node->rb_color = RB_BLACK;
 +}
-+
-+void rb_erase(struct rb_node * node, struct rb_root * root)
+-      spin_lock_bh(&c->erase_completion_lock);
++static int jffs2_sb_set(struct super_block *sb, void *data)
 +{
-+      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;
++      struct jffs2_sb_info *p = data;
+-      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;
++      /* 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);
+-      buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT;
++      return 0;
++}
+-#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);
++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;
+-      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);
+-      } 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;
++      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->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;
++      sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, c);
+-              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;
++      if (IS_ERR(sb))
++              goto out_put;
+-              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 (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;
+       }
+-      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\n", jeb->offset);
+-              }
+-      }
+-      if (list_empty(&c->free_list)) {
+-              printk(KERN_DEBUG "free_list: empty\n");
+-      } else {
+-              struct list_head *this;
++      D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): New superblock for device %d (\"%s\")\n",
++                mtd->index, mtd->name));
+-              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;
++      sb->s_op = &jffs2_super_operations;
++      sb->s_flags = flags | MS_NOATIME;
+-              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;
++      ret = jffs2_do_fill_super(sb, data, (flags&MS_VERBOSE)?1:0);
+-              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 (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 (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 (!dev_name)
++              return ERR_PTR(-EINVAL);
 +
-+ color:
-+      if (color == RB_BLACK)
-+              __rb_erase_color(child, parent, root);
-+}
-+#endif /* Before 2.4.11 */
++      D1(printk(KERN_DEBUG "jffs2_get_sb(): dev_name \"%s\"\n", dev_name));
 +
-+      /* 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;
-+      }
++      /* 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. */
 +
-+      /* 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;
++      /* FIXME: How to do the root fs this way? */
 +
-+      return node->rb_parent;
-+}
++      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;
 +
-+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;
++                      /* 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;
+       }
++                      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;
+               
+-      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);
++                      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);
++                      }
++              }
 +      }
-+      while (node->rb_parent && node == node->rb_parent->rb_left)
-+              node = node->rb_parent;
+-      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;
++      /* Try the old way - the hack where we allowed users to mount 
++         /dev/mtdblock$(n) but didn't actually _use_ the blkdev */
+-      if (jffs2_build_filesystem(c)) {
+-              D1(printk(KERN_DEBUG "build_fs failed\n"));
+-              goto out_nodes;
+-      }
++      err = path_lookup(dev_name, LOOKUP_FOLLOW, &nd);
+-      sb->s_op = &jffs2_super_operations;
++      D1(printk(KERN_DEBUG "jffs2_get_sb(): path_lookup() returned %d, inode %p\n",
++                err, nd.dentry->d_inode));
+-      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;
++      if (err)
++              return ERR_PTR(err);
 +
-+      return node->rb_parent;
-+}
++      err = -EINVAL;
 +
-+void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root)
-+{
-+      struct rb_node *parent = victim->rb_parent;
++      if (!S_ISBLK(nd.dentry->d_inode->i_mode))
++              goto out;
 +
-+      /* 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 (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 (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;
-+}
---- linux-2.4.21/fs/jffs2/read.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/read.c
-@@ -1,52 +1,32 @@
- /*
-  * JFFS2 -- Journalling Flash File System, Version 2.
-  *
-- * Copyright (C) 2001 Red Hat, Inc.
-- *
-- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
-- *
-- * The original JFFS, from which the design for JFFS2 was derived,
-- * was designed and implemented by Axis Communications AB.
-- *
-- * 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.
-+ * Copyright (C) 2001-2003 Red Hat, Inc.
-  *
-- * The Original Code is JFFS2 - Journalling Flash File System, version 2
-+ * Created by David Woodhouse <dwmw2@infradead.org>
-  *
-- * 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.
-+ * For licensing information, see the file 'LICENCE' in this directory.
-  *
-- * $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 "crc32.h"
-+#include "compr.h"
+-#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);
  
--int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len)
-+int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
-+                   struct jffs2_full_dnode *fd, unsigned char *buf,
-+                   int ofs, int len)
+- 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_raw_inode *ri;
-       size_t readlen;
--      __u32 crc;
-+      uint32_t crc;
-       unsigned char *decomprbuf = NULL;
-       unsigned char *readbuf = NULL;
-       int ret = 0;
-@@ -55,35 +35,41 @@
-       if (!ri)
-               return -ENOMEM;
+       struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
  
--      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);
+@@ -293,83 +272,65 @@
  
--      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);
-       }
+       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);
++      if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
++              vfree(c->blocks);
++      else
+       kfree(c->blocks);
++      jffs2_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"));
+ }
  
--      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 +86,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 +109,35 @@
-               decomprbuf = readbuf;
-       }
+-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)
++static void jffs2_kill_sb(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);
+ }
  
--      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);
+-
+-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,
++};
  
--      if (!ret && readlen != ri->csize)
-+      if (!ret && readlen != je32_to_cpu(ri->csize))
-               ret = -EIO;
-       if (ret)
-               goto out_decomprbuf;
+ static int __init init_jffs2_fs(void)
+ {
+       int ret;
  
--      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(c, f, ri->compr | (ri->usercompr << 8), 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;
-               }
-       }
+-      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_JFFS2_FS_WRITEBUFFER
++             " (NAND)"
+ #endif
++             " (C) 2001-2003 Red Hat, Inc.\n");
  
--      if (len < ri->dsize) {
-+      if (len < je32_to_cpu(ri->dsize)) {
-               memcpy(buf, decomprbuf+ofs, len);
+-      ret = jffs2_zlib_init();
++      jffs2_inode_cachep = kmem_cache_create("jffs2_i",
++                                           sizeof(struct jffs2_inode_info),
++                                           0, 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_compressors_init();
+       if (ret) {
+-              printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n");
++              printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n");
+               goto out;
        }
-  out_decomprbuf:
-@@ -161,3 +151,66 @@
+       ret = jffs2_create_slab_caches();
+       if (ret) {
+               printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
+-              goto out_zlib;
++              goto out_compressors;
+       }
+       ret = register_filesystem(&jffs2_fs_type);
+       if (ret) {
+@@ -380,17 +341,19 @@
  
+  out_slab:
+       jffs2_destroy_slab_caches();
+- out_zlib:
+-      jffs2_zlib_exit();
++ out_compressors:
++      jffs2_compressors_exit();
+  out:
++      kmem_cache_destroy(jffs2_inode_cachep);
        return ret;
  }
+ static void __exit exit_jffs2_fs(void)
+ {
+-      jffs2_destroy_slab_caches();
+-      jffs2_zlib_exit();
+       unregister_filesystem(&jffs2_fs_type);
++      jffs2_destroy_slab_caches();
++      jffs2_compressors_exit();
++      kmem_cache_destroy(jffs2_inode_cachep);
+ }
+ module_init(init_jffs2_fs);
+--- /dev/null
++++ linux-2.4.21/fs/jffs2/symlink-v24.c
+@@ -0,0 +1,52 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2001, 2002 Red Hat, Inc.
++ *
++ * Created by David Woodhouse <dwmw2@infradead.org>
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * $Id$
++ *
++ */
 +
-+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));
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/fs.h>
++#include "nodelist.h"
 +
-+      frag = jffs2_lookup_node_frag(&f->fragtree, offset);
++int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen);
++int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
 +
-+      /* 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);
-+                              D2(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, f, 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"));
-+              }
++struct inode_operations jffs2_symlink_inode_operations =
++{     
++      .readlink =     jffs2_readlink,
++      .follow_link =  jffs2_follow_link,
++      .setattr =      jffs2_setattr
++};
++
++int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen)
++{
++      struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
++
++      if (!f->dents) {
++              printk(KERN_ERR "jffs2_readlink(): can't find symlink taerget\n");
++              return -EIO;
 +      }
-+      return 0;
++
++      return vfs_readlink(dentry, buffer, buflen, (char *)f->dents);
 +}
 +
---- linux-2.4.21/fs/jffs2/readinode.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/readinode.c
-@@ -1,79 +1,124 @@
- /*
-  * JFFS2 -- Journalling Flash File System, Version 2.
++int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
++{
++      struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
++
++      if (!f->dents) {
++              printk(KERN_ERR "jffs2_follow_link(): can't find symlink taerget\n");
++              return -EIO;
++      }
++      
++      return vfs_follow_link(nd, (char *)f->dents);
++}
+--- linux-2.4.21/fs/jffs2/symlink.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/symlink.c
+@@ -3,35 +3,11 @@
+  *
+  * Copyright (C) 2001, 2002 Red Hat, Inc.
   *
-- * Copyright (C) 2001 Red Hat, Inc.
-- *
 - * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
 - *
 - * The original JFFS, from which the design for JFFS2 was derived,
 - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
 - * See the Licence for the specific language governing rights and
 - * limitations under the Licence.
-+ * Copyright (C) 2001-2003 Red Hat, Inc.
-  *
+- *
 - * The Original Code is JFFS2 - Journalling Flash File System, version 2
 + * Created by David Woodhouse <dwmw2@infradead.org>
   *
   *
   */
  
--/* Given an inode, probably with existing list of fragments, add the new node
-- * to the fragment list.
-- */
+@@ -39,72 +15,49 @@
  #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 <linux/namei.h>
  #include "nodelist.h"
--#include "crc32.h"
  
-+static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag);
+-int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen);
+-int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
++static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
  
--D1(void jffs2_print_frag_list(struct jffs2_inode_info *f)
-+#if CONFIG_JFFS2_FS_DEBUG >= 2
-+static void jffs2_print_fragtree(struct rb_root *list, int permitbug)
+ struct inode_operations jffs2_symlink_inode_operations =
+ {     
+-      readlink:       jffs2_readlink,
+-      follow_link:    jffs2_follow_link,
+-      setattr:        jffs2_setattr
++      .readlink =     generic_readlink,
++      .follow_link =  jffs2_follow_link,
++      .setattr =      jffs2_setattr
+ };
+-static char *jffs2_getlink(struct dentry *dentry)
++static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
  {
--      struct jffs2_node_frag *this = f->fraglist;
-+      struct jffs2_node_frag *this = frag_first(list);
-+      uint32_t lastofs = 0;
-+      int buggy = 0;
+       struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
+-      char *buf;
+-      int ret;
  
-       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 (f->metadata) {
--              printk(KERN_DEBUG "metadata at 0x%08x\n", f->metadata->raw->flash_offset &~3);
-+      if (buggy && !permitbug) {
-+              printk(KERN_CRIT "Frag tree got a hole in it\n");
-+              BUG();
+-      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;
++      /*
++       * We don't acquire the f->sem mutex here since the only data we
++       * use is f->dents which in case of the symlink inode points to the
++       * symlink's target path.
++       *
++       * 1. If we are here the inode has already built and f->dents has
++       * to point to the target path.
++       * 2. Nobody uses f->dents (if the inode is symlink's inode). The
++       * exception is inode freeing function which frees f->dents. But
++       * it can't be called while we are here and before VFS has
++       * stopped using our f->dents string which we provide by means of
++       * nd_set_link() call.
++       */
+-      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);
++      if (!f->dents) {
++              printk(KERN_ERR "jffs2_follow_link(): can't find symlink taerget\n");
++              return -EIO;
        }
--})
-+}
+-      return buf;
+-
+-}
+-int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen)
+-{
+-      unsigned char *kbuf;
+-      int ret;
++      D1(printk(KERN_DEBUG "jffs2_follow_link(): target path is '%s'\n", (char *) f->dents));
  
-+void jffs2_print_frag_list(struct jffs2_inode_info *f)
-+{
-+      jffs2_print_fragtree(&f->fragtree, 0);
+-      kbuf = jffs2_getlink(dentry);
+-      if (IS_ERR(kbuf))
+-              return PTR_ERR(kbuf);
++      nd_set_link(nd, (char *)f->dents);
  
--int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
-+      if (f->metadata) {
-+              printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw));
+-      ret = vfs_readlink(dentry, buffer, buflen, kbuf);
+-      kfree(kbuf);
+-      return ret;
++      /*
++       * We unlock the f->sem mutex but VFS will use the f->dents string. This is safe
++       * since the only way that may cause f->dents to be changed is iput() operation.
++       * But VFS will not use f->dents after iput() has been called.
++       */
++      return 0;
+ }
+-int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
+-{
+-      unsigned char *buf;
+-      int ret;
+-
+-      buf = jffs2_getlink(dentry);
+-
+-      if (IS_ERR(buf))
+-              return PTR_ERR(buf);
+-
+-      ret = vfs_follow_link(nd, buf);
+-      kfree(buf);
+-      return ret;
+-}
+--- /dev/null
++++ linux-2.4.21/fs/jffs2/wbuf.c
+@@ -0,0 +1,1240 @@
++/*
++ * JFFS2 -- Journalling Flash File System, Version 2.
++ *
++ * Copyright (C) 2001-2003 Red Hat, Inc.
++ * Copyright (C) 2004 Thomas Gleixner <tglx@linutronix.de>
++ *
++ * Created by David Woodhouse <dwmw2@infradead.org>
++ * Modified debugged and enhanced by Thomas Gleixner <tglx@linutronix.de>
++ *
++ * For licensing information, see the file 'LICENCE' in this directory.
++ *
++ * $Id$
++ *
++ */
++
++#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    2
++
++/* 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);
++              }
++      }
++}
++
++#define REFILE_NOTEMPTY 0
++#define REFILE_ANYWAY   1
++
++static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int allow_empty)
++{
++      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_ON(allow_empty == REFILE_NOTEMPTY);
++              /* 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));
++}
++
++/* 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];
++
++      jffs2_block_refile(c, jeb, REFILE_NOTEMPTY);
++
++      /* 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... */
++              if (jffs2_cleanmarker_oob(c))
++                      ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo);
++              else
++                      ret = c->mtd->read(c->mtd, start, c->wbuf_ofs - start, &retlen, buf);
++              
++              if (ret == -EBADMSG && 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");
++              kfree(buf);
++              return;
++      }
++      if (end-start >= c->wbuf_pagesize) {
++              /* Need to do another write immediately, but it's possible
++                 that this is just because the wbuf itself is completely
++                 full, and there's nothing earlier read back from the 
++                 flash. Hence 'buf' isn't necessarily what we're writing 
++                 from. */
++              unsigned char *rewrite_buf = buf?:c->wbuf;
++              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
++              if (jffs2_cleanmarker_oob(c))
++                      ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen,
++                                              rewrite_buf, NULL, c->oobinfo);
++              else
++                      ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, rewrite_buf);
 +
-+#if CONFIG_JFFS2_FS_DEBUG >= 1
-+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 (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 (ref_flags(fn->raw) == REF_PRISTINE) {
++                      if (retlen) {
++                              struct jffs2_raw_node_ref *raw2;
 +
-+                      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;
-+                      }
++                              raw2 = jffs2_alloc_raw_node_ref();
++                              if (!raw2)
++                                      return;
 +
-+                      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;
++                              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;
 +              }
-+      }
-+      
-+      if (bitched) {
-+              struct jffs2_node_frag *thisfrag;
++              printk(KERN_NOTICE "Recovery of wbuf succeeded to %08x\n", ofs);
 +
-+              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);
++              c->wbuf_len = (end - start) - towrite;
++              c->wbuf_ofs = ofs + towrite;
++              memmove(c->wbuf, rewrite_buf + towrite, c->wbuf_len);
++              /* Don't muck about with c->wbuf_inodes. False positives are harmless. */
++              if (buf)
++                      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;
 +      }
-+      return bitched;
- }
-+#endif /* D1 */
- static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this)
- {
-@@ -82,42 +127,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 +167,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);
++      /* 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];
 +
-+              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);
++      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;
 +      }
 +
-+      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);
++      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;
 +      }
-+      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) {
++      /* 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);
 +
-+              /* 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 && (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);
-+              }
++      ACCT_SANITY_CHECK(c,jeb);
++        D1(ACCT_PARANOIA_CHECK(jeb));
 +
-+              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);
-               }
--              newfrag->next = NULL;
--              *prev = newfrag;
-+                      rb_insert_color(&holefrag->rb, list);
-+                      this = holefrag;
-+              }
-+              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) {
++      ACCT_SANITY_CHECK(c,new_jeb);
++        D1(ACCT_PARANOIA_CHECK(new_jeb));
 +
-+              /* 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);
++      spin_unlock(&c->erase_completion_lock);
 +
-+              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;
++      D1(printk(KERN_DEBUG "wbuf recovery completed OK\n"));
++}
 +
-+                      /* Adjust size of original 'this' */
-                       this->size = newfrag->ofs - this->ofs;
++/* 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
++*/
++#define NOPAD         0
++#define PAD_NOACCOUNT 1
++#define PAD_ACCOUNTING        2
 +
-+                      /* 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;
++static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
++{
++      int ret;
++      size_t retlen;
 +
-+              /* 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;
++      /* Nothing to do if not write-buffering the flash. In particular, we shouldn't
++         del_timer() the timer we never initialised. */
++      if (!jffs2_is_writebuffered(c))
++              return 0;
 +
-+                      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
++      if (!down_trylock(&c->alloc_sem)) {
++              up(&c->alloc_sem);
++              printk(KERN_CRIT "jffs2_flush_wbuf() called with alloc_sem not locked!\n");
++              BUG();
 +      }
-+      /* 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);
++      if (!c->wbuf_len)       /* already checked c->wbuf above */
++              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 && !jffs2_dataflash(c)) {
++              c->wbuf_len = PAD(c->wbuf_len);
++
++              /* Pad with JFFS2_DIRTY_BITMASK initially.  this helps out ECC'd NOR
++                 with 8 byte page size */
++              memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - 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
++      
++      if (jffs2_cleanmarker_oob(c))
++              ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo);
++      else
++              ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf);
++
++      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;
++              }
 +
-       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);
++              jffs2_wbuf_recover(c);
 +
-       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;
-+      /* 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;
-               }
--              list = &(*list)->next;
-+              frag = frag_next(frag);
++              return ret;
 +      }
-+      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);
++      spin_lock(&c->erase_completion_lock);
 +
-+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);
--      f = JFFS2_INODE_INFO(inode);
--      c = JFFS2_SB_INFO(inode->i_sb);
-+      D2(printk(KERN_DEBUG "jffs2_do_read_inode(): Got inocache at %p\n", f->inocache));
--      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));
-+      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;
--      if (!f->inocache && inode->i_ino == 1) {
-+              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;
++      /* Adjust free size of the block if we padded. */
++      if (pad && !jffs2_dataflash(c)) {
++              struct jffs2_eraseblock *jeb;
 +
-+              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;
++              jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
 +
-+              default:
++              D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of %sblock at %08x\n",
++                        (jeb==c->nextblock)?"next":"", jeb->offset));
++
++              /* 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 (jeb->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",
++                             jeb->offset, jeb->free_size);
 +                      BUG();
 +              }
++              jeb->free_size -= (c->wbuf_pagesize - c->wbuf_len);
++              c->free_size -= (c->wbuf_pagesize - c->wbuf_len);
++              jeb->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
++              c->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
 +      }
-+      spin_unlock(&c->inocache_lock);
 +
-+      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;
++      /* Stick any now-obsoleted blocks on the erase_pending_list */
++      jffs2_refile_wbuf_blocks(c);
++      jffs2_clear_wbuf_ino_list(c);
++      spin_unlock(&c->erase_completion_lock);
 +
-+      return jffs2_do_read_inode_internal(c, f, latest_node);
++      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;
 +}
 +
-+int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
++/* 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)
 +{
-+      struct jffs2_raw_inode n;
-+      struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL);
-+      int ret;
++      uint32_t old_wbuf_ofs;
++      uint32_t old_wbuf_len;
++      int ret = 0;
 +
-+      if (!f)
-+              return -ENOMEM;
++      D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() called for ino #%u...\n", ino));
 +
-+      memset(f, 0, sizeof(*f));
-+      init_MUTEX_LOCKED(&f->sem);
-+      f->inocache = ic;
++      if (!c->wbuf)
++              return 0;
 +
-+      ret = jffs2_do_read_inode_internal(c, f, &n);
-+      if (!ret) {
-+              up(&f->sem);
-+              jffs2_do_clear_inode(c, f);
++      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;
 +      }
-+      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;
++      old_wbuf_ofs = c->wbuf_ofs;
++      old_wbuf_len = c->wbuf_len;
 +
-+      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, &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,219 +527,217 @@
-               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 (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"));
++              down_write(&c->wbuf_sem);
++              ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
++              /* retry flushing wbuf in case jffs2_wbuf_recover
++                 left some data in the wbuf */
++              if (ret)
++                      ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
++              up_write(&c->wbuf_sem);
++      } else while (old_wbuf_len &&
++                    old_wbuf_ofs == c->wbuf_ofs) {
 +
-       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;
-+                              if (f->inocache->state == INO_STATE_READING)
-+                                      jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
-+                              return -EIO;
-                       }
--                      printk(KERN_WARNING "jffs2_read_inode(): But it has children so we fake some modes for it\n");
-+                      printk(KERN_WARNING "jffs2_do_read_inode(): But it has children so we fake some modes for it\n");
-               }
--              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;
-+              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;
-               }
--              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;
-+      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_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;
-+      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;
-       }
--      /* 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;
-+      switch(jemode_to_cpu(latest_node->mode) & S_IFMT) {
-+      case S_IFDIR:
-+              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. */
-+                      latest_node->ctime = latest_node->mtime = cpu_to_je32(latest_mctime);
-       }                       
-+              break;
-           
--      inode->i_blksize = PAGE_SIZE;
--      inode->i_blocks = (inode->i_size + 511) >> 9;
-       
--      switch (inode->i_mode & S_IFMT) {
--              unsigned short rdev;
-+      case S_IFREG:
-+              /* 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:
--              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;
-+              if (!je32_to_cpu(latest_node->isize))
-+                      latest_node->isize = latest_node->dsize;
-               
--      case S_IFDIR:
--              if (mctime_ver > 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;
-+              if (f->inocache->state != INO_STATE_CHECKING) {
-+                      /* Symlink's inode data is the target path. Read it and
-+                       * keep in RAM to facilitate quick follow symlink operation.
-+                       * We use f->dents field to store the target path, which
-+                       * is somewhat ugly. */
-+                      f->dents = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL);
-+                      if (!f->dents) {
-+                              printk(KERN_WARNING "Can't allocate %d bytes of memory "
-+                                              "for the symlink target path cache\n",
-+                                              je32_to_cpu(latest_node->csize));
-+                              up(&f->sem);
-+                              jffs2_do_clear_inode(c, f);
-+                              return -ENOMEM;
-               }
--              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;
-+                      ret = jffs2_flash_read(c, ref_offset(fn->raw) + sizeof(*latest_node),
-+                                              je32_to_cpu(latest_node->csize), &retlen, (char *)f->dents);
-+                      
-+                      if (ret  || retlen != je32_to_cpu(latest_node->csize)) {
-+                              if (retlen != je32_to_cpu(latest_node->csize))
-+                                      ret = -EIO;
-+                              kfree(f->dents);
-+                              f->dents = NULL;
-+                              up(&f->sem);
-+                              jffs2_do_clear_inode(c, f);
-+                              return -ret;
++              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);
++                      down_write(&c->wbuf_sem);
++                      ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
++                      /* retry flushing wbuf in case jffs2_wbuf_recover
++                         left some data in the wbuf */
++                      if (ret)
++                              ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
++                      up_write(&c->wbuf_sem);
++                      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)
++{
++      int ret;
++
++      if (!c->wbuf)
++              return 0;
++
++      down_write(&c->wbuf_sem);
++      ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
++      /* retry - maybe wbuf recover left some data in wbuf. */
++      if (ret)
++              ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
++      up_write(&c->wbuf_sem);
++
++      return ret;
++}
++
++#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
++#define PAGE_DIV(x) ( ((unsigned long)(x) / (unsigned long)(c->wbuf_pagesize)) * (unsigned long)(c->wbuf_pagesize) )
++#define PAGE_MOD(x) ( (unsigned long)(x) % (unsigned long)(c->wbuf_pagesize) )
++#else
++#define PAGE_DIV(x) ( (x) & (~(c->wbuf_pagesize - 1)) )
++#define PAGE_MOD(x) ( (x) & (c->wbuf_pagesize - 1) )
++#endif
++
++int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino)
++{
++      struct kvec 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 (!jffs2_is_writebuffered(c))
++              return jffs2_flash_direct_writev(c, invecs, count, to, retlen);
++      
++      down_write(&c->wbuf_sem);
++
++      /* 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);
++      }
++
++      /* Fixup the wbuf if we are moving to a new eraseblock.  The checks below
++         fail for ECC'd NOR because cleanmarker == 16, so a block starts at
++         xxx0010.  */
++      if (jffs2_nor_ecc(c)) {
++              if (((c->wbuf_ofs % c->sector_size) == 0) && !c->wbuf_len) {
++                      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 (SECTOR_ADDR(to) != SECTOR_ADDR(c->wbuf_ofs)) {
++              /* 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(c, PAD_NOACCOUNT);
++                      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;
++                              goto exit;
 +                      }
++              }
++              /* set pointer to new block */
++              c->wbuf_ofs = PAGE_DIV(to);
++              c->wbuf_len = PAGE_MOD(to);                     
++      } 
 +
-+                      ((char *)f->dents)[je32_to_cpu(latest_node->csize)] = '\0';
-+                      D1(printk(KERN_DEBUG "jffs2_do_read_inode(): symlink's target '%s' cached\n",
-+                                              (char *)f->dents));
++      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;
 +              }
 +              
-+              /* 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;
++              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, NOPAD);
++              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;
++                      goto exit;
 +              }
-+              /* 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;
++              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++;
 +              }
-+              /* 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);
++              invec++;
++      }
 +
-+      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);
--        /* I don't think we care about the potential race due to reading this
--           without f->sem. It can never get undeleted. */
--        int deleted = f->inocache && !f->inocache->nlink;
--
--      D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
--
--      /* If it's a deleted inode, grab the alloc_sem. This prevents
--         jffs2_garbage_collect_pass() from deciding that it wants to
--         garbage collect one of the nodes we're just about to mark 
--         obsolete -- by the time we drop alloc_sem and return, all
--         the nodes are marked obsolete, and jffs2_g_c_pass() won't
--         call iget() for the inode in question.
--      */
--      if (deleted)
--              down(&c->alloc_sem);
-+      int deleted;
-       down(&f->sem);
-+      deleted = f->inocache && !f->inocache->nlink;
++      /* OK, now we've flushed the wbuf and the start of the bits
++         we have been asked to write, now to write the rest.... */
 +
-+      if (f->inocache && f->inocache->state != INO_STATE_CHECKING)
-+              jffs2_set_inocache_state(c, f->inocache, INO_STATE_CLEARING);
--      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));
--
--              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);
-+      jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL);
--                      jffs2_free_full_dnode(frag->node);
--              }
--              jffs2_free_node_frag(frag);
-+      /* For symlink inodes we us f->dents to store the target path name */
-+      if (S_ISLNK(OFNI_EDONI_2SFFJ(f)->i_mode)) {
-+              if (f->dents) {
-+                      kfree(f->dents);
-+                      f->dents = NULL;
-       }
-+      } else {
-+              fds = f->dents;
++      /* 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;
++              }
++      }
 +
-       while(fds) {
-               fd = fds;
-               fds = fd->next;
-               jffs2_free_full_dirent(fd);
-       }
++      /* 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;
++
++              remainder = outvecs[splitvec].iov_len - split_ofs;
++              outvecs[splitvec].iov_len = split_ofs;
++
++              /* We did cross a page boundary, so we write some now */
++              if (jffs2_cleanmarker_oob(c))
++                      ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, c->oobinfo); 
++              else
++                      ret = jffs2_flash_direct_writev(c, outvecs, splitvec+1, outvec_to, &wbuf_retlen);
++              
++              if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) {
++                      /* At this point we have no problem,
++                         c->wbuf is empty. However refile nextblock to avoid
++                         writing again to same address.
++                      */
++                      struct jffs2_eraseblock *jeb;
++
++                      spin_lock(&c->erase_completion_lock);
++
++                      jeb = &c->blocks[outvec_to / c->sector_size];
++                      jffs2_block_refile(c, jeb, REFILE_ANYWAY);
++
++                      *retlen = 0;
++                      spin_unlock(&c->erase_completion_lock);
++                      goto exit;
++              }
++              
++              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;
 +      }
--      up(&f->sem);
--
--      if(deleted)
--              up(&c->alloc_sem);
--};
-+      if (f->inocache && f->inocache->state != INO_STATE_CHECKING) {
-+              jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
-+              if (f->inocache->nodes == (void *)f->inocache)
-+                      jffs2_del_ino_cache(c, f->inocache);
++
++      /* 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;
 +      }
-+      up(&f->sem);
++      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);
++
++      ret = 0;
++      
++exit:
++      up_write(&c->wbuf_sem);
++      return ret;
 +}
---- linux-2.4.21/fs/jffs2/scan.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/scan.c
-@@ -1,47 +1,25 @@
- /*
-  * JFFS2 -- Journalling Flash File System, Version 2.
-  *
-- * Copyright (C) 2001 Red Hat, Inc.
-- *
-- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
-- *
-- * The original JFFS, from which the design for JFFS2 was derived,
-- * was designed and implemented by Axis Communications AB.
-- *
-- * 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.
-+ * Copyright (C) 2001-2003 Red Hat, Inc.
-  *
-- * The Original Code is JFFS2 - Journalling Flash File System, version 2
-+ * Created by David Woodhouse <dwmw2@infradead.org>
-  *
-- * 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.
-+ * For licensing information, see the file 'LICENCE' in this directory.
-  *
-- * $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 <linux/crc32.h>
-+#include <linux/compiler.h>
- #include "nodelist.h"
--#include "crc32.h"
-+#define DEFAULT_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,96 @@
- } 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
++/*
++ *    This is the entry for flash write.
++ *    Check, if we work on NAND FLASH, if so build an kvec 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 kvec vecs[1];
++
++      if (!jffs2_is_writebuffered(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;
++
++      if (!jffs2_is_writebuffered(c))
++              return c->mtd->read(c->mtd, ofs, len, retlen, buf);
++
++      /* Read flash */
++      if (jffs2_cleanmarker_oob(c))
++              ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo);
++      else
++              ret = c->mtd->read(c->mtd, ofs, len, retlen, buf);
++
++      if ( (ret == -EBADMSG) && (*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;
++      }       
++
++      /* if no writebuffer available or write buffer empty, return */
++      if (!c->wbuf_pagesize || !c->wbuf_len)
++              return ret;;
 +
-+static inline int min_free(struct jffs2_sb_info *c)
-+{
-+      uint32_t min = 2 * sizeof(struct jffs2_raw_inode);
-+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
-+      if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize)
-+              return c->wbuf_pagesize;
-+#endif
-+      return min;
-+}
++      /* if we read in a different block, return */
++      if (SECTOR_ADDR(ofs) != SECTOR_ADDR(c->wbuf_ofs))
++              return ret;
 +
-+static inline uint32_t EMPTY_SCAN_SIZE(uint32_t sector_size) {
-+      if (sector_size < DEFAULT_EMPTY_SCAN_SIZE)
-+              return sector_size;
-+      else
-+              return DEFAULT_EMPTY_SCAN_SIZE;
++      /* Lock only if we have reason to believe wbuf contains relevant data,
++         so that checking an erased block during wbuf recovery space allocation
++         does not deadlock. */
++      down_read(&c->wbuf_sem);
++
++      if (ofs >= c->wbuf_ofs) {
++              owbf = (ofs - c->wbuf_ofs);     /* offset in write buffer */
++              if (owbf > c->wbuf_len)         /* is read beyond write buffer ? */
++                      goto exit;
++              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 ? */
++                      goto exit;
++              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);
++
++exit:
++      up_read(&c->wbuf_sem);
++      return ret;
 +}
- int jffs2_scan_medium(struct jffs2_sb_info *c)
- {
-       int i, ret;
--      __u32 empty_blocks = 0;
-+      uint32_t empty_blocks = 0, bad_blocks = 0;
-+      unsigned char *flashbuf = NULL;
-+      uint32_t buf_size = 0;
-+#ifndef __ECOS
-+      size_t pointlen;
--      if (!c->blocks) {
--              printk(KERN_WARNING "EEEK! c->blocks is NULL!\n");
--              return -EINVAL;
-+      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;
++
++/*
++ *    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;
++
++      /* allocate a buffer for all oob data in this sector */
++      oob_size = c->mtd->oobsize;
++      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 page */
++      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[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;
 +              }
-+              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;
 +
-+              /* Respect kmalloc limitations */
-+              if (buf_size > 128*1024)
-+                      buf_size = 128*1024;
++      /* we know, we are aligned :) */        
++      for (page = oob_size; page < len; page += sizeof(long)) {
++              unsigned long dat = *(unsigned long *)(&buf[page]);
++              if(dat != -1) {
++                      ret = 1; 
++                      goto out;
++              }
++      }
 +
-+              D1(printk(KERN_DEBUG "Allocating readbuf of %d bytes\n", buf_size));
-+              flashbuf = kmalloc(buf_size, GFP_KERNEL);
-+              if (!flashbuf)
-+                      return -ENOMEM;
-       }
++out:
++      kfree(buf);     
++      
++      return ret;
++}
 +
-       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);
++/*
++*     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[2 * NAND_MAX_OOBSIZE];
++      unsigned char *p;
++      int ret, i, cnt, retval = 0;
++      size_t retlen, offset;
++      int oob_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 +142,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;
++      offset = jeb->offset;
++      oob_size = c->mtd->oobsize;
 +
-+              case BLK_STATE_CLEANMARKER:
-                       /* Only a CLEANMARKER node is valid */
-                       if (!jeb->dirty_size) {
-                               /* It's actually free */
-@@ -118,74 +159,224 @@
-                               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;
++      /* Loop through the physical blocks */
++      for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) {
++              /* Check first if the block is bad. */
++              if (c->mtd->block_isbad (c->mtd, offset)) {
++                      D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n", jeb->offset));
++                      return 2;
++              }
++              /*
++                 *    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);
 +
-+              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;
++              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;
++              }
 +
-+              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) && 
-+                        /* We want to remember the block with most free space
-+                           and stick it in the 'nextblock' position to start writing to it. */
-+                        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;
++              /* 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;
 +
-+              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;
++                      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;
 +      }
-+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
-+      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. */
++      return retval;
++}
 +
-+              uint32_t skip = c->nextblock->free_size & (c->wbuf_pagesize-1);
++int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
++{
++      struct  jffs2_unknown_node n;
++      int     ret;
++      size_t  retlen;
 +
-+              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;
++      n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
++      n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
++      n.totlen = cpu_to_je32(8);
 +
-+              c->nextblock->free_size -= skip;
-+              c->free_size -= skip;
++      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;
 +      }
-+#endif
-       if (c->nr_erasing_blocks) {
--              if (!c->used_size && empty_blocks != c->nr_blocks) {
-+              if ( !c->used_size && ((c->nr_free_blocks+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;
++      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;
 +}
 +
-+static int jffs2_fill_scan_buf (struct jffs2_sb_info *c, unsigned char *buf,
-+                              uint32_t ofs, uint32_t len)
++/* 
++ * On NAND we try to mark this block bad. If the block was erased more
++ * than MAX_ERASE_FAILURES we mark it finaly bad.
++ * 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, uint32_t bad_offset)
 +{
-+      int ret;
-+      size_t retlen;
++      int     ret;
 +
-+      ret = jffs2_flash_read(c, ofs, len, &retlen, buf);
++      /* if the count is < max, we try to write the counter to the 2nd page oob area */
++      if( ++jeb->bad_count < MAX_ERASE_FAILURES)
++              return 0;
++
++      if (!c->mtd->block_markbad)
++              return 1; // What else can we do?
++
++      D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Marking bad block at %08x\n", bad_offset));
++      ret = c->mtd->block_markbad(c->mtd, bad_offset);
++      
 +      if (ret) {
-+              D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", len, ofs, 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 < 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;
-+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
-+      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_WRITEBUFFER
-+      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;
++      return 1;
++}
++
++#define NAND_JFFS2_OOB16_FSDALEN      8
++
++static struct nand_oobinfo jffs2_oobinfo_docecc = {
++      .useecc = MTD_NANDECC_PLACE,
++      .eccbytes = 6,
++      .eccpos = {0,1,2,3,4,5}
++};
++
++
++int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c)
++{
++      struct nand_oobinfo *oinfo = &c->mtd->oobinfo;
++
++      /* Do this only, if we have an oob buffer */
++      if (!c->mtd->oobsize)
++              return 0;
++      
++      /* Cleanmarker is out-of-band, so inline size zero */
++      c->cleanmarker_size = 0;
++
++      /* Should we use autoplacement ? */
++      if (oinfo && oinfo->useecc == MTD_NANDECC_AUTOPLACE) {
++              D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n"));
++              /* Get the position of the free bytes */
++              if (!oinfo->oobfree[0][1]) {
++                      printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep. Autoplacement selected and no empty space in oob\n");
++                      return -ENOSPC;
++              }
++              c->fsdata_pos = oinfo->oobfree[0][0];
++              c->fsdata_len = oinfo->oobfree[0][1];
++              if (c->fsdata_len > 8)
++                      c->fsdata_len = 8;
++      } else {
++              /* This is just a legacy fallback and should go away soon */
++              switch(c->mtd->ecctype) {
++              case MTD_ECC_RS_DiskOnChip:
++                      printk(KERN_WARNING "JFFS2 using DiskOnChip hardware ECC without autoplacement. Fix it!\n");
++                      c->oobinfo = &jffs2_oobinfo_docecc;
++                      c->fsdata_pos = 6;
++                      c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN;
++                      c->badblock_pos = 15;
++                      break;
++      
++              default:
++                      D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n"));
++                      return -EINVAL;
 +              }
 +      }
-+#endif
-+      buf_ofs = jeb->offset;
++      return 0;
++}
 +
-+      if (!buf_size) {
-+              buf_len = c->sector_size;
-+      } else {
-+              buf_len = EMPTY_SCAN_SIZE(c->sector_size);
-+              err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len);
-+              if (err)
-+                      return err;
-+      }
++int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
++{
++      int res;
++
++      /* Initialise write buffer */
++      init_rwsem(&c->wbuf_sem);
++      c->wbuf_pagesize = c->mtd->oobblock;
++      c->wbuf_ofs = 0xFFFFFFFF;
 +      
-+      /* We temporarily use 'ofs' as a pointer into the buffer/jeb */
-+      ofs = 0;
++      c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
++      if (!c->wbuf)
++              return -ENOMEM;
 +
-+      /* Scan only 4KiB of 0xFF before declaring it's empty */
-+      while(ofs < EMPTY_SCAN_SIZE(c->sector_size) && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
-+              ofs += 4;
++      res = jffs2_nand_set_oobinfo(c);
 +
-+      if (ofs == EMPTY_SCAN_SIZE(c->sector_size)) {
-+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
-+              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;
-+                      default:        return ret;
-+                      }
-+              }
-+#endif
-               D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset));
--              return 1;       /* special return code */
-+              if (c->cleanmarker_size == 0)
-+                      return BLK_STATE_CLEANMARKER;   /* don't bother with re-erase */
-+              else
-+                      return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */
++#ifdef BREAKME
++      if (!brokenbuf)
++              brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
++      if (!brokenbuf) {
++              kfree(c->wbuf);
++              return -ENOMEM;
 +      }
-+      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;
++      memset(brokenbuf, 0xdb, c->wbuf_pagesize);
++#endif
++      return res;
++}
 +
-       noise = 10;
-+scan_more:    
-       while(ofs < jeb->offset + c->sector_size) {
--              ssize_t retlen;
--              ACCT_PARANOIA_CHECK(jeb);
++void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
++{
++      kfree(c->wbuf);
++}
 +
-+              D1(ACCT_PARANOIA_CHECK(jeb));
++int jffs2_dataflash_setup(struct jffs2_sb_info *c) {
++      c->cleanmarker_size = 0;                /* No cleanmarkers needed */
++      
++      /* Initialize write buffer */
++      init_rwsem(&c->wbuf_sem);
++      c->wbuf_pagesize = c->sector_size;
++      c->wbuf_ofs = 0xFFFFFFFF;
 +
-+              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 +387,183 @@
-               }
-               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;
++      c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
++      if (!c->wbuf)
++              return -ENOMEM;
 +
-+              node = (struct jffs2_unknown_node *)&buf[ofs-buf_ofs];
++      printk(KERN_INFO "JFFS2 write-buffering enabled (%i)\n", c->wbuf_pagesize);
 +
-+              if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) {
-+                      uint32_t inbuf_ofs;
-+                      uint32_t empty_start;
++      return 0;
++}
 +
-+                      empty_start = ofs;
-+                      ofs += 4;
++void jffs2_dataflash_cleanup(struct jffs2_sb_info *c) {
++      kfree(c->wbuf);
++}
 +
-+                      D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs));
-+              more_empty:
-+                      inbuf_ofs = ofs - buf_ofs;
-+                      while (inbuf_ofs < buf_len) {
-+                              if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) {
-+                                      printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n",
-+                                             empty_start, ofs);
-+                                      DIRTY_SPACE(ofs-empty_start);
-+                                      goto scan_more;
-               }
--              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;
--                      continue;
-+                              inbuf_ofs+=4;
-+                              ofs += 4;
-               }
-+                      /* Ran off end. */
-+                      D1(printk(KERN_DEBUG "Empty flash to end of buffer at 0x%08x\n", ofs));
--              if (ofs == jeb->offset && node.magic == KSAMTIB_CIGAM_2SFFJ) {
-+                      /* If we're only checking the beginning of a block with a cleanmarker,
-+                         bail now */
-+                      if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) && 
-+                          c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_phys) {
-+                              D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size)));
-+                              return BLK_STATE_CLEANMARKER;
-+                      }
++int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c) {
++      /* Cleanmarker is actually larger on the flashes */
++      c->cleanmarker_size = 16;
 +
-+                      /* See how much more there is to read in this eraseblock... */
-+                      buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
-+                      if (!buf_len) {
-+                              /* No more to read. Break out of main loop without marking 
-+                                 this range of empty space as dirty (because it's not) */
-+                              D1(printk(KERN_DEBUG "Empty flash at %08x runs to end of block. Treating as free_space\n",
-+                                        empty_start));
-+                              break;
-+                      }
-+                      D1(printk(KERN_DEBUG "Reading another 0x%x at 0x%08x\n", buf_len, ofs));
-+                      err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
-+                      if (err)
-+                              return err;
-+                      buf_ofs = ofs;
-+                      goto more_empty;
-+              }
++      /* Initialize write buffer */
++      init_rwsem(&c->wbuf_sem);
++      c->wbuf_pagesize = c->mtd->eccsize;
++      c->wbuf_ofs = 0xFFFFFFFF;
 +
-+              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);
++      c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
++      if (!c->wbuf)
++              return -ENOMEM;
 +
-+              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;
-+              }
++      return 0;
++}
 +
-+              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;
++void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c) {
++      kfree(c->wbuf);
++}
+--- linux-2.4.21/fs/jffs2/write.c~mtd-cvs
++++ linux-2.4.21/fs/jffs2/write.c
+@@ -1,154 +1,70 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * 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.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * 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.
++ * For licensing information, see the file 'LICENCE' in this directory.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
  
-               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 +572,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;
+ #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 "crc32.h"
++#include "compr.h"
  
-               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;
+-/* 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));
  
-                       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;
+       ic = jffs2_alloc_inode_cache();
+       if (!ic) {
+-              return ERR_PTR(-ENOMEM);
+-      }
+-      memset(ic, 0, sizeof(*ic));
+-      
+-      inode = new_inode(sb);
+-      
+-      if (!inode) {
+-              jffs2_free_inode_cache(ic);
+-              return ERR_PTR(-ENOMEM);
++              return -ENOMEM;
+       }
  
-                       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;
+-      /* Alloc jffs2_inode_info when that's split in 2.5 */
++      memset(ic, 0, sizeof(*ic));
  
-                       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;
+-      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);
+-
+-      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;
+-
+-      insert_inode_hash(inode);
++      f->inocache->ino = ++c->highest_ino;
++      f->inocache->state = INO_STATE_PRESENT;
+-      return inode;
 -}
++      ri->ino = cpu_to_je32(f->inocache->ino);
  
--/* 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)
+-/* 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)
 -{
--      __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;
-+      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));
-               
--              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;
-+      /* 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;
-               }
--              for (i=0; i<(retlen / 4); i++) {
--                      if (buf[i] != 0xffffffff) {
--                              curofs += i*4;
+-      unsigned long i;
+-      size_t totlen = 0, thislen;
+-      int ret = 0;
++      D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino));
++      jffs2_add_ino_cache(c, f->inocache);
  
--                              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;
+-      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 ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size 
-+              && (!jeb->first_node || !jeb->first_node->next_phys) )
-+              return BLK_STATE_CLEANMARKER;
+-      if (retlen)
+-              *retlen = totlen;
+-      return ret;
+-}
++      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);
  
--      D1(printk(KERN_DEBUG "Empty flash detected from 0x%08x to 0x%08x\n", *startofs, curofs));
--      kfree(buf);
--      *startofs = curofs;
--      return 0;
-+      /* 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;
++      f->highest_version = 1;
++      ri->version = cpu_to_je32(f->highest_version);
+-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 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)
+-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)
  {
-       struct jffs2_inode_cache *ic;
-@@ -399,137 +653,77 @@
-       if (ic)
-               return ic;
+       unsigned char buf[16];
+-      ssize_t retlen;
++      size_t retlen;
+       int ret, i;
  
-+      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;
+-      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]);
        }
-       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;
  }
+-
+-      
++#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)
  
--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_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;
--      struct jffs2_tmp_dnode_info *tn, **tn_list;
-       struct jffs2_inode_cache *ic;
--      struct jffs2_raw_inode ri;
--      __u32 crc;
--      __u16 oldnodetype;
--      int ret;
+       struct jffs2_full_dnode *fn;
 -      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;
--      }
--
--      /* 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;
--
--      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;
--      }
-+      uint32_t ino = je32_to_cpu(ri->ino);
+-      struct iovec vecs[2];
++      size_t retlen;
++      struct kvec vecs[2];
+       int ret;
++      int retried = 0;
++      unsigned long cnt = 2;
  
--      if (ri.csize) {
--              /* Check data CRC too */
--              unsigned char *dbuf;
--              __u32 crc;
-+      D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", ofs));
+-      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;
  
--              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;
--              }
--      }
-+      /* 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. */
+-      writecheck(c->mtd, flash_ofs);
++      D1(writecheck(c, flash_ofs));
  
--      /* Wheee. It worked */
+-      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) {
-               printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of node reference failed\n");
-               return -ENOMEM;
+       if (!raw)
+@@ -206,19 +121,37 @@
+               jffs2_free_raw_node_ref(raw);
+               return ERR_PTR(-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);
+-      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;
 +
-+      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);
++      /* 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;
 +
-+              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;
-       }
++      if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(ri->version) < f->highest_version)) {
++              BUG_ON(!retried);
++              D1(printk(KERN_DEBUG "jffs2_write_dnode : dnode_version %d, "
++                              "highest version %d -> updating dnode\n", 
++                              je32_to_cpu(ri->version), f->highest_version));
++              ri->version = cpu_to_je32(++f->highest_version);
++              ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
 +      }
--      /* 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;
++      ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen,
++                               (alloc_mode==ALLOC_GC)?0:f->inocache->ino);
 +
-       ic->nodes = raw;
-       if (!jeb->first_node)
-               jeb->first_node = raw;
-@@ -538,134 +732,56 @@
-       jeb->last_node = raw;
+       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 +162,98 @@
+                          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);
++                      }
  
-       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;
--
--              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) {
-+                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)));
++                      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);
  
--                      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));
-+      pseudo_random += je32_to_cpu(ri->version);
+       /* Link into per-inode list */
++      spin_lock(&c->erase_completion_lock);
+       raw->next_in_ino = f->inocache->nodes;
+       f->inocache->nodes = raw;
++      spin_unlock(&c->erase_completion_lock);
  
--                      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;
+-      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;
  }
  
--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_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;
-       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;
-       }
+-      struct iovec vecs[2];
++      size_t retlen;
++      struct kvec vecs[2];
++      int retried = 0;
+       int ret;
  
--      pseudo_random += rd.version;
-+      pseudo_random += je32_to_cpu(rd->version);
+-      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));
  
--      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 +790,15 @@
-               printk(KERN_NOTICE "jffs2_scan_dirent_node(): allocation of node reference failed\n");
-               return -ENOMEM;
+-      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();
        }
--      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);
+@@ -291,44 +274,457 @@
                jffs2_free_raw_node_ref(raw);
-               return -ENOMEM;
+               return ERR_PTR(-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 +808,15 @@
-               jeb->last_node->next_phys = raw;
-       jeb->last_node = raw;
+-      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;
  
--      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 = 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(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 +838,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));
+       fd->nhash = full_name_hash(name, strlen(name));
+       fd->type = rd->type;
+       memcpy(fd->name, name, namelen);
+       fd->name[namelen]=0;
 +
-+              rotate_list((&c->clean_list), rotateby);
++ 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;
 +
-+              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"));
++      if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(rd->version) < f->highest_version)) {
++              BUG_ON(!retried);
++              D1(printk(KERN_DEBUG "jffs2_write_dirent : dirent_version %d, "
++                                   "highest version %d -> updating dirent\n",
++                                   je32_to_cpu(rd->version), f->highest_version));
++              rd->version = cpu_to_je32(++f->highest_version);
++              fd->version = je32_to_cpu(rd->version);
++              rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
 +      }
 +
-+      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);
++      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];
 +
-+              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));
--      if (c->nr_erasing_blocks)
--              rotate_list((&c->erase_pending_list), pseudo_random % c->nr_erasing_blocks);
-+              rotate_list((&c->dirty_list), rotateby);
++                      retried = 1;
  
--      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 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));
-+
-+              rotate_list((&c->erasable_list), rotateby);
++                      D1(printk(KERN_DEBUG "Retrying failed write.\n"));
 +
-+              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"));
-+      }
++                      ACCT_SANITY_CHECK(c,jeb);
++                      D1(ACCT_PARANOIA_CHECK(jeb));
 +
-+      if (c->nr_erasing_blocks) {
-+              rotateby = pseudo_random % c->nr_erasing_blocks;
-+              D1(printk(KERN_DEBUG "Rotating erase_pending_list by %d\n", rotateby));
++                      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);
++                      }
 +
-+              rotate_list((&c->erase_pending_list), rotateby);
++                      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);
++      spin_lock(&c->erase_completion_lock);
++      raw->next_in_ino = f->inocache->nodes;
+       f->inocache->nodes = raw;
++      spin_unlock(&c->erase_completion_lock);
 +
-+              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 (retried) {
++              ACCT_SANITY_CHECK(c,NULL);
 +      }
 +
-+      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"));
-+      }
+       return fd;
  }
---- /dev/null
-+++ linux-2.4.21/fs/jffs2/super-v24.c
-@@ -0,0 +1,170 @@
-+/*
-+ * JFFS2 -- Journalling Flash File System, Version 2.
-+ *
-+ * Copyright (C) 2001-2003 Red Hat, Inc.
-+ *
-+ * Created by David Woodhouse <dwmw2@infradead.org>
-+ *
-+ * 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 "compr.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)
++/* 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)
 +{
-+      struct jffs2_sb_info *c;
-+      int ret;
-+
-+      D1(printk(KERN_DEBUG "jffs2: read_super for device %s\n", kdevname(sb->s_dev)));
++      int ret = 0;
++      uint32_t writtenlen = 0;
 +
-+      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;
-+      }
++              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;
++              uint16_t comprtype = JFFS2_COMPR_NONE;
++              uint32_t phys_ofs, alloclen;
++              uint32_t datalen, cdatalen;
++              int retried = 0;
 +
-+      c = JFFS2_SB_INFO(sb);
-+      memset(c, 0, sizeof(*c));
++      retry:
++              D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset));
 +
-+      sb->s_op = &jffs2_super_operations;
++              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);
 +
-+      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;
-+      }
++              comprtype = jffs2_compress(c, f, buf, &comprbuf, &datalen, &cdatalen);
 +
-+      ret = jffs2_do_fill_super(sb, data, silent);
-+      if (ret) {
-+              put_mtd_device(c->mtd);
-+              return NULL;
-+      }
++              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));
 +
-+      return sb;
-+}
++              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 & 0xff;
++              ri->usercompr = (comprtype >> 8 ) & 0xff;
++              ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
++              ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
 +
-+static void jffs2_put_super (struct super_block *sb)
-+{
-+      struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
++              fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, ALLOC_NORETRY);
 +
-+      D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n"));
++              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);
 +
-+      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);
-+      if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
-+              vfree(c->blocks);
-+      else
-+              kfree(c->blocks);
-+      jffs2_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"));
++                      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;
 +}
 +
-+static DECLARE_FSTYPE_DEV(jffs2_fs_type, "jffs2", jffs2_read_super);
-+
-+static int __init init_jffs2_fs(void)
++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;
 +
-+      printk(KERN_INFO "JFFS2 version 2.2."
-+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
-+             " (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;
++      /* 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;
 +      }
 +
-+      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_compressors_init();
-+      if (ret) {
-+              printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n");
-+              goto out;
++      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);
 +      }
-+      ret = jffs2_create_slab_caches();
++      /* 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) {
-+              printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
-+              goto out_compressors;
++              /* Eep. */
++              D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n"));
++              return ret;
 +      }
-+      ret = register_filesystem(&jffs2_fs_type);
-+      if (ret) {
-+              printk(KERN_ERR "JFFS2 error: Failed to register filesystem\n");
-+              goto out_slab;
++
++      rd = jffs2_alloc_raw_dirent();
++      if (!rd) {
++              /* Argh. Now we treat it like a normal delete */
++              jffs2_complete_reservation(c);
++              return -ENOMEM;
 +      }
-+      return 0;
 +
-+ out_slab:
-+      jffs2_destroy_slab_caches();
-+ out_compressors:
-+      jffs2_compressors_exit();
-+ out:
-+      return ret;
-+}
++      down(&dir_f->sem);
 +
-+static void __exit exit_jffs2_fs(void)
-+{
-+      jffs2_destroy_slab_caches();
-+      jffs2_compressors_exit();
-+      unregister_filesystem(&jffs2_fs_type);
-+}
++      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));
 +
-+module_init(init_jffs2_fs);
-+module_exit(exit_jffs2_fs);
++      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));
 +
-+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.
---- linux-2.4.21/fs/jffs2/super.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/super.c
-@@ -1,291 +1,270 @@
- /*
-  * JFFS2 -- Journalling Flash File System, Version 2.
-  *
-- * Copyright (C) 2001 Red Hat, Inc.
-- *
-- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
-- *
-- * The original JFFS, from which the design for JFFS2 was derived,
-- * was designed and implemented by Axis Communications AB.
-- *
-- * 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.
-+ * Copyright (C) 2001-2003 Red Hat, Inc.
-  *
-- * The Original Code is JFFS2 - Journalling Flash File System, version 2
-+ * Created by David Woodhouse <dwmw2@infradead.org>
-  *
-- * 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.
-+ * For licensing information, see the file 'LICENCE' in this directory.
-  *
-- * $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 "compr.h"
- #include "nodelist.h"
--#ifndef MTD_BLOCK_MAJOR
--#define MTD_BLOCK_MAJOR 31
--#endif
-+static void jffs2_put_super(struct super_block *);
--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 *);
-+static kmem_cache_t *jffs2_inode_cachep;
++      fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL);
 +
-+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;
-+}
++      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);
++      }
 +
-+static void jffs2_destroy_inode(struct inode *inode)
-+{
-+      kmem_cache_free(jffs2_inode_cachep, JFFS2_INODE_INFO(inode));
-+}
++      /* Link the fd into the inode's list, obsoleting an old
++         one if necessary. */
++      jffs2_add_fd_to_list(c, fd, &dir_f->dents);
 +
-+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;
++      jffs2_complete_reservation(c);
++      up(&dir_f->sem);
 +
-+      if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
-+          SLAB_CTOR_CONSTRUCTOR) {
-+              init_MUTEX_LOCKED(&ei->sem);
-+              inode_init_once(&ei->vfs_inode);
-+      }
++      return 0;
 +}
 +
-+static int jffs2_sync_fs(struct super_block *sb, int wait)
-+{
-+      struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
 +
-+      down(&c->alloc_sem);
-+      jffs2_flush_wbuf_pad(c);
-+      up(&c->alloc_sem);      
-+      return 0;
-+}
- 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,
-+      .sync_fs =      jffs2_sync_fs,
- };
--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;
-+      /* 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 {
-+              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;
-+      }
-+}
--      spin_lock_bh(&c->erase_completion_lock);
-+static int jffs2_sb_set(struct super_block *sb, void *data)
-+{
-+      struct jffs2_sb_info *p = data;
--      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;
-+      /* 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);
--      buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT;
-+      return 0;
-+}
--#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);
-+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)
++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 super_block *sb;
-+      struct jffs2_sb_info *c;
++      struct jffs2_raw_dirent *rd;
++      struct jffs2_full_dirent *fd;
++      uint32_t alloclen, phys_ofs;
 +      int ret;
--      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);
--      } 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;
-+      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->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;
-+      sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, c);
--              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;
-+      if (IS_ERR(sb))
-+              goto out_put;
--              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 (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;
-       }
--      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\n", jeb->offset);
--              }
--      }
--      if (list_empty(&c->free_list)) {
--              printk(KERN_DEBUG "free_list: empty\n");
--      } else {
--              struct list_head *this;
-+      D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): New superblock for device %d (\"%s\")\n",
-+                mtd->index, mtd->name));
--              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;
-+      sb->s_op = &jffs2_super_operations;
-+      sb->s_flags = flags | MS_NOATIME;
--              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;
-+      ret = jffs2_do_fill_super(sb, data, (flags&MS_VERBOSE)?1:0);
--              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 (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);
++      if (1 /* alternative branch needs testing */ || 
++          !jffs2_can_mark_obsolete(c)) {
++              /* We can't mark stuff obsolete on the medium. We need to write a deletion dirent */
++
++              rd = jffs2_alloc_raw_dirent();
++              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);
++              }
 +
-+      D1(printk(KERN_DEBUG "jffs2_get_sb(): dev_name \"%s\"\n", dev_name));
++              /* File it. This will mark the old one obsolete. */
++              jffs2_add_fd_to_list(c, fd, &dir_f->dents);
++              up(&dir_f->sem);
++      } else {
++              struct jffs2_full_dirent **prev = &dir_f->dents;
++              uint32_t nhash = full_name_hash(name, namelen);
 +
-+      /* 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. */
++              down(&dir_f->sem);
 +
-+      /* FIXME: How to do the root fs this way? */
++              while ((*prev) && (*prev)->nhash <= nhash) {
++                      if ((*prev)->nhash == nhash && 
++                          !memcmp((*prev)->name, name, namelen) &&
++                          !(*prev)->name[namelen]) {
++                              struct jffs2_full_dirent *this = *prev;
 +
-+      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;
++                              D1(printk(KERN_DEBUG "Marking old dirent node (ino #%u) @%08x obsolete\n",
++                                        this->ino, ref_offset(this->raw)));
 +
-+                      /* 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;
-       }
-+                      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;
-               
--      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);
-+                      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);
++                              *prev = this->next;
++                              jffs2_mark_node_obsolete(c, (this->raw));
++                              jffs2_free_full_dirent(this);
++                              break;
 +                      }
++                      prev = &((*prev)->next);
 +              }
++              up(&dir_f->sem);
 +      }
--      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;
-+      /* Try the old way - the hack where we allowed users to mount 
-+         /dev/mtdblock$(n) but didn't actually _use_ the blkdev */
--      if (jffs2_build_filesystem(c)) {
--              D1(printk(KERN_DEBUG "build_fs failed\n"));
--              goto out_nodes;
--      }
-+      err = path_lookup(dev_name, LOOKUP_FOLLOW, &nd);
--      sb->s_op = &jffs2_super_operations;
-+      D1(printk(KERN_DEBUG "jffs2_get_sb(): path_lookup() returned %d, inode %p\n",
-+                err, nd.dentry->d_inode));
--      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;
-+      if (err)
-+              return ERR_PTR(err);
 +
-+      err = -EINVAL;
++      /* 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) { 
 +
-+      if (!S_ISBLK(nd.dentry->d_inode->i_mode))
-+              goto out;
++              down(&dead_f->sem);
 +
-+      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 (S_ISDIR(OFNI_EDONI_2SFFJ(dead_f)->i_mode)) {
++                      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);
 +      }
--#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,83 +272,65 @@
-       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);
-+      if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
-+              vfree(c->blocks);
-+      else
-       kfree(c->blocks);
-+      jffs2_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)
--{
--      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)
-+static void jffs2_kill_sb(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_JFFS2_FS_WRITEBUFFER
-+             " (NAND)"
- #endif
-+             " (C) 2001-2003 Red Hat, Inc.\n");
--      ret = jffs2_zlib_init();
-+      jffs2_inode_cachep = kmem_cache_create("jffs2_i",
-+                                           sizeof(struct jffs2_inode_info),
-+                                           0, SLAB_RECLAIM_ACCOUNT,
-+                                           jffs2_i_init_once, NULL);
-+      if (!jffs2_inode_cachep) {
-+              printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n");
++      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;
 +      }
-+      ret = jffs2_compressors_init();
-       if (ret) {
--              printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n");
-+              printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n");
-               goto out;
-       }
-       ret = jffs2_create_slab_caches();
-       if (ret) {
-               printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
--              goto out_zlib;
-+              goto out_compressors;
-       }
-       ret = register_filesystem(&jffs2_fs_type);
-       if (ret) {
-@@ -380,17 +341,19 @@
-  out_slab:
-       jffs2_destroy_slab_caches();
-- out_zlib:
--      jffs2_zlib_exit();
-+ out_compressors:
-+      jffs2_compressors_exit();
-  out:
-+      kmem_cache_destroy(jffs2_inode_cachep);
-       return ret;
- }
- static void __exit exit_jffs2_fs(void)
- {
--      jffs2_destroy_slab_caches();
--      jffs2_zlib_exit();
-       unregister_filesystem(&jffs2_fs_type);
-+      jffs2_destroy_slab_caches();
-+      jffs2_compressors_exit();
-+      kmem_cache_destroy(jffs2_inode_cachep);
- }
- module_init(init_jffs2_fs);
++      
++      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;
++}
 --- /dev/null
-+++ linux-2.4.21/fs/jffs2/symlink-v24.c
-@@ -0,0 +1,52 @@
++++ linux-2.4.21/fs/jffs2/writev.c
+@@ -0,0 +1,50 @@
 +/*
 + * JFFS2 -- Journalling Flash File System, Version 2.
 + *
 + *
 + */
 +
-+
 +#include <linux/kernel.h>
-+#include <linux/slab.h>
-+#include <linux/fs.h>
++#include <linux/mtd/mtd.h>
 +#include "nodelist.h"
 +
-+int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen);
-+int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
-+
-+struct inode_operations jffs2_symlink_inode_operations =
-+{     
-+      .readlink =     jffs2_readlink,
-+      .follow_link =  jffs2_follow_link,
-+      .setattr =      jffs2_setattr
-+};
-+
-+int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen)
++/* 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 kvec *vecs,
++                                unsigned long count, loff_t to, size_t *retlen)
 +{
-+      struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
++      unsigned long i;
++      size_t totlen = 0, thislen;
++      int ret = 0;
 +
-+      if (!f->dents) {
-+              printk(KERN_ERR "jffs2_readlink(): can't find symlink taerget\n");
-+              return -EIO;
++      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;
 +      }
-+
-+      return vfs_readlink(dentry, buffer, buflen, (char *)f->dents);
++      if (retlen)
++              *retlen = totlen;
++      return ret;
 +}
 +
-+int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
++int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct kvec *vecs,
++                            unsigned long count, loff_t to, size_t *retlen)
 +{
-+      struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
-+
-+      if (!f->dents) {
-+              printk(KERN_ERR "jffs2_follow_link(): can't find symlink taerget\n");
-+              return -EIO;
-+      }
-+      
-+      return vfs_follow_link(nd, (char *)f->dents);
++      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);
 +}
---- linux-2.4.21/fs/jffs2/symlink.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/symlink.c
-@@ -3,35 +3,11 @@
-  *
-  * Copyright (C) 2001, 2002 Red Hat, Inc.
-  *
-- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
-- *
-- * The original JFFS, from which the design for JFFS2 was derived,
-- * was designed and implemented by Axis Communications AB.
-- *
-- * 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
-+ * Created by David Woodhouse <dwmw2@infradead.org>
-  *
-- * 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.
-+ * For licensing information, see the file 'LICENCE' in this directory.
-  *
-- * $Id$
-+ * $Id$
-  *
++
+--- linux-2.4.21/include/asm-arm/arch-pxa/hardware.h~ramses
++++ linux-2.4.21/include/asm-arm/arch-pxa/hardware.h
+@@ -127,16 +127,20 @@
+  * Implementation specifics
   */
  
-@@ -39,72 +15,49 @@
- #include <linux/kernel.h>
- #include <linux/slab.h>
- #include <linux/fs.h>
--#include <linux/jffs2.h>
-+#include <linux/namei.h>
- #include "nodelist.h"
+-//#ifdef CONFIG_ARCH_LUBBOCK
++#ifdef CONFIG_ARCH_LUBBOCK
+ #include "lubbock.h"
+-//#endif
++#endif
  
--int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen);
--int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
-+static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
+-//#ifdef CONFIG_ARCH_PXA_IDP
++#ifdef CONFIG_ARCH_PXA_IDP
+ #include "idp.h"
+-//#endif
++#endif
  
- struct inode_operations jffs2_symlink_inode_operations =
- {     
--      readlink:       jffs2_readlink,
--      follow_link:    jffs2_follow_link,
--      setattr:        jffs2_setattr
-+      .readlink =     generic_readlink,
-+      .follow_link =  jffs2_follow_link,
-+      .setattr =      jffs2_setattr
- };
+-//#ifdef CONFIG_ARCH_PXA_CERF
++#ifdef CONFIG_ARCH_PXA_CERF
+ #include "cerf.h"
+-//#endif
++#endif
++
++#ifdef CONFIG_ARCH_RAMSES
++#include "ramses.h"
++#endif
  
--static char *jffs2_getlink(struct dentry *dentry)
-+static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
- {
-       struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
--      char *buf;
--      int ret;
+ #endif  /* _ASM_ARCH_HARDWARE_H */
+--- linux-2.4.21/include/asm-arm/arch-pxa/irqs.h~ramses
++++ linux-2.4.21/include/asm-arm/arch-pxa/irqs.h
+@@ -105,14 +105,13 @@
+ #define S0_BVD1_STSCHG        SA1111_IRQ(53)
+ #define S1_BVD1_STSCHG        SA1111_IRQ(54)
  
--      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;
-+      /*
-+       * We don't acquire the f->sem mutex here since the only data we
-+       * use is f->dents which in case of the symlink inode points to the
-+       * symlink's target path.
-+       *
-+       * 1. If we are here the inode has already built and f->dents has
-+       * to point to the target path.
-+       * 2. Nobody uses f->dents (if the inode is symlink's inode). The
-+       * exception is inode freeing function which frees f->dents. But
-+       * it can't be called while we are here and before VFS has
-+       * stopped using our f->dents string which we provide by means of
-+       * nd_set_link() call.
-+       */
+-#define SA1111_IRQ_MAX        SA1111_IRQ(54)
  
--      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);
-+      if (!f->dents) {
-+              printk(KERN_ERR "jffs2_follow_link(): can't find symlink taerget\n");
-+              return -EIO;
-       }
--      return buf;
--
--}
--int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen)
--{
--      unsigned char *kbuf;
--      int ret;
-+      D1(printk(KERN_DEBUG "jffs2_follow_link(): target path is '%s'\n", (char *) f->dents));
+ #undef NR_IRQS
+ #define NR_IRQS               (SA1111_IRQ_MAX + 1)
  
--      kbuf = jffs2_getlink(dentry);
--      if (IS_ERR(kbuf))
--              return PTR_ERR(kbuf);
-+      nd_set_link(nd, (char *)f->dents);
+ #endif        // defined(CONFIG_SA1111)
  
--      ret = vfs_readlink(dentry, buffer, buflen, kbuf);
--      kfree(kbuf);
--      return ret;
-+      /*
-+       * We unlock the f->sem mutex but VFS will use the f->dents string. This is safe
-+       * since the only way that may cause f->dents to be changed is iput() operation.
-+       * But VFS will not use f->dents after iput() has been called.
-+       */
-+      return 0;
- }
+-#if defined(CONFIG_ARCH_LUBBOCK) || defined(CONFIG_ARCH_PXA_IDP) 
++#if defined(CONFIG_ARCH_LUBBOCK) || defined(CONFIG_ARCH_PXA_IDP)
+ #if CONFIG_SA1111
+ #define LUBBOCK_IRQ(x)        (SA1111_IRQ_MAX + 1 + (x))
+ #else
+@@ -132,6 +131,3 @@
+ #define NR_IRQS               (LUBBOCK_LAST_IRQ + 1)
  
--int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
--{
--      unsigned char *buf;
--      int ret;
+ #endif        // CONFIG_ARCH_LUBBOCK
 -
--      buf = jffs2_getlink(dentry);
 -
--      if (IS_ERR(buf))
--              return PTR_ERR(buf);
 -
--      ret = vfs_follow_link(nd, buf);
--      kfree(buf);
--      return ret;
--}
+--- linux-2.4.21/include/asm-arm/arch-pxa/pxa-regs.h~ramses
++++ linux-2.4.21/include/asm-arm/arch-pxa/pxa-regs.h
+@@ -1051,6 +1051,7 @@
+ #define PGSR1         __REG(0x40F00024)  /* Power Manager GPIO Sleep State Register for GP[63-32] */
+ #define PGSR2         __REG(0x40F00028)  /* Power Manager GPIO Sleep State Register for GP[84-64] */
+ #define RCSR          __REG(0x40F00030)  /* Reset Controller Status Register */
++#define PMFW          __REG(0x40F00034)  /* Power Manager Fast-Sleep Wakeup Configuration Register */
+ #define PSSR_RDH      (1 << 5)        /* Read Disable Hold */
+ #define PSSR_PH               (1 << 4)        /* Peripheral Control Hold */
 --- /dev/null
-+++ linux-2.4.21/fs/jffs2/wbuf.c
-@@ -0,0 +1,1240 @@
++++ linux-2.4.21/include/asm-arm/arch-pxa/ramses.h
+@@ -0,0 +1,364 @@
 +/*
-+ * JFFS2 -- Journalling Flash File System, Version 2.
-+ *
-+ * Copyright (C) 2001-2003 Red Hat, Inc.
-+ * Copyright (C) 2004 Thomas Gleixner <tglx@linutronix.de>
++ *  linux/include/asm-arm/arch-pxa/ramses.h
 + *
-+ * Created by David Woodhouse <dwmw2@infradead.org>
-+ * Modified debugged and enhanced by Thomas Gleixner <tglx@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.
 + *
-+ * For licensing information, see the file 'LICENCE' in this directory.
++ * Copyright (c) 2002,2003 M&N Logistik-Lösungen Online GmbH
 + *
-+ * $Id$
++ * 2001-09-13: Cliff Brake <cbrake@accelent.com>
++ *             Initial code
 + *
++ * 2002-10-08: adaption from PXA IDP to Ramses
++ * 
 + */
 +
-+#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
++/*
++ * Note: this file must be safe to include in assembly files
++ */
 +
-+#ifdef BREAKME
-+static unsigned char *brokenbuf;
++#define RAMSES_FLASH_PHYS     (PXA_CS0_PHYS)
++#define RAMSES_ALT_FLASH_PHYS (PXA_CS1_PHYS)
++#define RAMSES_MEDIAQ_PHYS    (PXA_CS3_PHYS)
++#define RAMSES_CONTROL_PHYS   (PXA_CS4_PHYS)
++#define RAMSES_IDE_PHYS               (PXA_CS5_PHYS + 0x03000000)
++#define RAMSES_ETH_PHYS               (PXA_CS5_PHYS + 0x03400000)
++#define RAMSES_COREVOLT_PHYS  (PXA_CS5_PHYS + 0x03800000)
++#define RAMSES_CPLD_PHYS      (PXA_CS5_PHYS + 0x03C00000)
++
++/*
++ * virtual memory map
++ */
++
++#define RAMSES_IDE_BASE               (0xf0000000)
++#define RAMSES_IDE_SIZE               (1*1024*1024)
++#define RAMSES_ETH_BASE               (RAMSES_IDE_BASE + RAMSES_IDE_SIZE)
++#define RAMSES_ETH_SIZE               (1*1024*1024)
++#define RAMSES_COREVOLT_BASE  (RAMSES_ETH_BASE + RAMSES_ETH_SIZE)
++#define RAMSES_COREVOLT_SIZE  (1*1024*1024)
++#define RAMSES_CPLD_BASE      (RAMSES_COREVOLT_BASE + RAMSES_COREVOLT_SIZE)
++#define RAMSES_CPLD_SIZE      (1*1024*1024)
++#define RAMSES_CONTROL_BASE   (RAMSES_CPLD_BASE + RAMSES_CPLD_SIZE)
++#define RAMSES_CONTROL_SIZE   (1*1024*1024)
++
++#if (RAMSES_CONTROL_BASE + RAMSES_CONTROL_SIZE) > 0xfc000000
++#error Your custom IO space is getting a bit large !!
 +#endif
 +
-+/* max. erase failures before we mark a block bad */
-+#define MAX_ERASE_FAILURES    2
++#define CPLD_P2V(x)           ((x) - RAMSES_CPLD_PHYS + RAMSES_CPLD_BASE)
++#define CPLD_V2P(x)           ((x) - RAMSES_CPLD_BASE + RAMSES_CPLD_PHYS)
++#define CTRL_P2V(x)           ((x) - RAMSES_CONTROL_PHYS + RAMSES_CONTROL_BASE)
++#define CTRL_V2P(x)           ((x) - RAMSES_CONTROL_BASE + RAMSES_CONTROL_PHYS)
++#define CORE_P2V(x)           ((x) - RAMSES_COREVOLT_PHYS + RAMSES_COREVOLT_BASE)
++#define CORE_V2P(x)           ((x) - RAMSES_COREVOLT_BASE + RAMSES_COREVOLT_PHYS)
 +
-+/* two seconds timeout for timed wbuf-flushing */
-+#define WBUF_FLUSH_TIMEOUT    2 * HZ
++//smc91111 driver compatibility issue
++#define ETH_BASE              RAMSES_ETH_BASE
 +
-+struct jffs2_inodirty {
-+      uint32_t ino;
-+      struct jffs2_inodirty *next;
-+};
++#ifndef __ASSEMBLY__
++#  define __CPLD_REG(x)               (*((volatile unsigned long *)CPLD_P2V(x)))
++#  define __CTRL_REG(x)         (*((volatile unsigned long *)CTRL_P2V(x)))
++#  define __CORE_REG(x)         (*((volatile unsigned long *)CORE_P2V(x)))
++#else
++#  define __CPLD_REG(x)               CPLD_P2V(x)
++#  define __CTRL_REG(x)               CTRL_P2V(x)
++#  define __CORE_REG(x)               CORE_P2V(x)
++#endif
 +
-+static struct jffs2_inodirty inodirty_nomem;
++/* CPLD addresses */
 +
-+static int jffs2_wbuf_pending_for_ino(struct jffs2_sb_info *c, uint32_t ino)
-+{
-+      struct jffs2_inodirty *this = c->wbuf_inodes;
++#define RAMSES_CPLD_PERIPH_PWR_               (RAMSES_CPLD_PHYS + 0x04)
++#define RAMSES_CPLD_PERIPH_PWR                __CPLD_REG(RAMSES_CPLD_PERIPH_PWR_)
++#define PER_RESET                     (1 << 4)
++#define PER_PWR_EN                    (1 << 3)
++#define USB_HOST_PWR_EN                       (1 << 2)
++#define CORE_VAR_EN                   (1 << 0)
 +
-+      /* If a malloc failed, consider _everything_ dirty */
-+      if (this == &inodirty_nomem)
-+              return 1;
++#define RAMSES_CPLD_LED_CONTROL_      (RAMSES_CPLD_PHYS + 0x08)
++#define RAMSES_CPLD_LED_CONTROL               __CPLD_REG(RAMSES_CPLD_LED_CONTROL_)
++#define CPLD_LED2                     (1 << 6)
++#define CPLD_LED1                     (1 << 5)
++#define GSM_ACTIVE                    (1 << 4)
 +
-+      /* If ino == 0, _any_ non-GC writes mean 'yes' */
-+      if (this && !ino)
-+              return 1;
++#define RAMSES_CPLD_KB_COL_HIGH_      (RAMSES_CPLD_PHYS + 0x0C)
++#define RAMSES_CPLD_KB_COL_HIGH               __CPLD_REG(RAMSES_CPLD_KB_COL_HIGH_)
++// kbch(7)..kbch(13) on bit 0..6
 +
-+      /* 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;
-+}
++#define RAMSES_CPLD_KB_COL_LOW_               (RAMSES_CPLD_PHYS + 0x10)
++#define RAMSES_CPLD_KB_COL_LOW                __CPLD_REG(RAMSES_CPLD_KB_COL_LOW_)
++// kbcl(0)..kbch(6) on bit 0..6
 +
-+static void jffs2_clear_wbuf_ino_list(struct jffs2_sb_info *c)
-+{
-+      struct jffs2_inodirty *this;
++#define RAMSES_CPLD_PCCARD_EN_                (RAMSES_CPLD_PHYS + 0x14)
++#define RAMSES_CPLD_PCCARD_EN         __CPLD_REG(RAMSES_CPLD_PCCARD_EN_)
++#define PCC1_RESET                    (1 << 7)
++#define PCC0_RESET                    (1 << 6)
++#define PCC1_ENABLE                   (1 << 1)
++#define PCC0_ENABLE                   (1 << 0)
 +
-+      this = c->wbuf_inodes;
++#define RAMSES_CPLD_PCCARD_PWR_               (RAMSES_CPLD_PHYS + 0x28)
++#define RAMSES_CPLD_PCCARD_PWR                __CPLD_REG(RAMSES_CPLD_PCCARD_PWR_)
++#define PCC1_PWR3                     (1 << 7)
++#define PCC1_PWR2                     (1 << 6)
++#define PCC1_PWR1                     (1 << 5)
++#define PCC1_PWR0                     (1 << 4)
++#define PCC0_PWR3                     (1 << 3)
++#define PCC0_PWR2                     (1 << 2)
++#define PCC0_PWR1                     (1 << 1)
++#define PCC0_PWR0                     (1 << 0)
 +
-+      if (this != &inodirty_nomem) {
-+              while (this) {
-+                      struct jffs2_inodirty *next = this->next;
-+                      kfree(this);
-+                      this = next;
-+              }
-+      }
-+      c->wbuf_inodes = NULL;
-+}
++#define RAMSES_CPLD_MISC_CTRL_                (RAMSES_CPLD_PHYS + 0x2C)
++#define RAMSES_CPLD_MISC_CTRL         __CPLD_REG(RAMSES_CPLD_MISC_CTRL_)
++#define RAMSES_IRDA_MD1                       (1 << 5)
++#define RAMSES_IRDA_MD0                       (1 << 4)
++#define RAMSES_FIR                    (1 << 3)
 +
-+static void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino)
-+{
-+      struct jffs2_inodirty *new;
++#define RAMSES_CPLD_LCD_              (RAMSES_CPLD_PHYS + 0x30)
++#define RAMSES_CPLD_LCD                       __CPLD_REG(RAMSES_CPLD_LCD_)
++#define RAMSES_LCD_PINC                       (1 << 7)
++#define RAMSES_LCD_PUP                        (1 << 6)
++#define RAMSES_LCD_PCS                        (1 << 5)
++#define RAMSES_LCD_DISPOFF            (1 << 2)
++#define RAMSES_LCD_VCC                        (1 << 0)
 +
-+      /* Mark the superblock dirty so that kupdated will flush... */
-+      OFNI_BS_2SFFJ(c)->s_dirt = 1;
++#define RAMSES_CPLD_FLASH_WE_         (RAMSES_CPLD_PHYS + 0x34)
++#define RAMSES_CPLD_FLASH_WE          __CPLD_REG(RAMSES_CPLD_FLASH_WE_)
++#define RAMSES_FLASH_WE                       (1 << 0)
 +
-+      if (jffs2_wbuf_pending_for_ino(c, ino))
-+              return;
++/* Read-Only registers */
 +
-+      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;
-+}
++#define RAMSES_CPLD_KB_ROW_           (RAMSES_CPLD_PHYS + 0x50)
++#define RAMSES_CPLD_KB_ROW            __CPLD_REG(RAMSES_CPLD_KB_ROW_)
++// kbr(0)..kbr(6) on bits 0..6
 +
-+static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c)
-+{
-+      struct list_head *this, *next;
-+      static int n;
++#define RAMSES_CPLD_PCCARD0_STATUS_   (RAMSES_CPLD_PHYS + 0x54)
++#define RAMSES_CPLD_PCCARD0_STATUS    __CPLD_REG(RAMSES_CPLD_PCCARD0_STATUS_)
++#define RAMSES_CPLD_PCCARD1_STATUS_   (RAMSES_CPLD_PHYS + 0x58)
++#define RAMSES_CPLD_PCCARD1_STATUS    __CPLD_REG(RAMSES_CPLD_PCCARD1_STATUS_) 
++#define _PCC_WRPROT                   (1 << 7)
++#define _PCC_S16                      (1 << 7)
++#define _PCC_RESET                    (1 << 6)
++#define _PCC_IRQ                      (1 << 5)
++#define _PCC_INPACK                   (1 << 4)
++#define PCC_BVD2                      (1 << 3)
++#define PCC_BVD1                      (1 << 2)
++#define PCC_VS2                               (1 << 1)
++#define PCC_VS1                               (1 << 0)
 +
-+      if (list_empty(&c->erasable_pending_wbuf_list))
-+              return;
++#define RAMSES_CPLD_MISC_STATUS_      (RAMSES_CPLD_PHYS + 0x5C)
++#define RAMSES_CPLD_MISC_STATUS               __CPLD_REG(RAMSES_CPLD_MISC_STATUS_)
++#define RAMSES_MMC_WRPROT             (1 << 7)
++#define RAMSES_USB_OVERCURR           (1 << 4)
++#define RAMSES_CHG_STS                        (1 << 2)
++#define RAMSES_WALL_IN                        (1 << 1)
++#define RAMSES_USB_D_CON              (1 << 0)
 +
-+      list_for_each_safe(this, next, &c->erasable_pending_wbuf_list) {
-+              struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
++#define RAMSES_CPLD_YEAR_             (RAMSES_CPLD_PHYS + 0x60)
++#define RAMSES_CPLD_YEAR              __CPLD_REG(RAMSES_CPLD_YEAR_)
 +
-+              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);
-+              }
-+      }
-+}
++#define RAMSES_CPLD_MONTH_            (RAMSES_CPLD_PHYS + 0x64)
++#define RAMSES_CPLD_MONTH             __CPLD_REG(RAMSES_CPLD_MONTH_)
 +
-+#define REFILE_NOTEMPTY 0
-+#define REFILE_ANYWAY   1
++#define RAMSES_CPLD_DAY_              (RAMSES_CPLD_PHYS + 0x68)
++#define RAMSES_CPLD_DAY                       __CPLD_REG(RAMSES_CPLD_DAY_)
 +
-+static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int allow_empty)
-+{
-+      D1(printk("About to refile bad block at %08x\n", jeb->offset));
++#define RAMSES_CPLD_REV_              (RAMSES_CPLD_PHYS + 0x6C)
++#define RAMSES_CPLD_REV                       __CPLD_REG(RAMSES_CPLD_REV_)
 +
-+      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_ON(allow_empty == REFILE_NOTEMPTY);
-+              /* 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));
++#define RAMSES_CPLD_VSTAT_            (RAMSES_CPLD_PHYS + 0x7C)
++#define RAMSES_CPLD_VSTAT             __CPLD_REG(RAMSES_CPLD_VSTAT_)
++#define RAMSES_BWE                    (1 << 1)
++
++
++/* Flags for ramses_flags */
++
++#define RAMSES_FLAGS_LCD_FBTURN               (1<<0)
++/* MUST stay bit 0 */
++#define RAMSES_FLAGS_SCANNER_BEAM     (1<<1)
++#define RAMSES_FLAGS_KEY_SCAN         (1<<2)
++#define RAMSES_FLAGS_KEY_SUSPEND      (1<<3)
++#define RAMSES_FLAGS_KEY_OFF          (1<<4)
++
++
++/* Offset in SMC EEPROM for LCD type */
++#define RAMSES_LCD_TYPE_OFFSET                0x23
++
++
++/* The control register on the I/O board */
++
++#define RAMSES_CONTROL_                 (RAMSES_CONTROL_PHYS + 0)
++#define RAMSES_CONTROL                  __CTRL_REG(RAMSES_CONTROL_)
++// 5c00 = 0101 1100 0000 0000
++#define RAMSES_CONTROL_SCANNER_TRIG_  (1 << 15)
++#define RAMSES_CONTROL_SCANNER_WAKE_  (1 << 14)
++#define RAMSES_CONTROL_SCANNER_PWR    (1 << 13)
++#define RAMSES_CONTROL_LED_BLUE_      (1 << 12)
++
++#define RAMSES_CONTROL_LED_ORANGE_    (1 << 11)
++#define RAMSES_CONTROL_GSM_RESET      (1 << 10)
++#define RAMSES_CONTROL_GSM_BOOT               (1 << 9)
++#define RAMSES_CONTROL_GSM_PWR                (1 << 8)
++
++#define RAMSES_CONTROL_POWEROFF               (1 << 7)
++#define       RAMSES_CONTROL_USB_INTERN       (1 << 6)
++#define RAMSES_CONTROL_MMC_PWR                (1 << 5)
++#define RAMSES_CONTROL_UART_PWR               (1 << 4)
++
++#define RAMSES_CONTROL_LCD_BLIGHT     (1 << 3)
++#define RAMSES_CONTROL_USB            (1 << 2)
++
++#define RAMSES_POWER_OFF()        { ramses_control_shadow |=  RAMSES_CONTROL_POWEROFF;      RAMSES_CONTROL = ramses_control_shadow; }
++
++// Active low
++#define RAMSES_SCANNER_TRIG_ON()  { ramses_control_shadow &= ~RAMSES_CONTROL_SCANNER_TRIG_; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_SCANNER_TRIG_OFF() { ramses_control_shadow |=  RAMSES_CONTROL_SCANNER_TRIG_; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_SCANNER_WAKE_ON()  { ramses_control_shadow &= ~RAMSES_CONTROL_SCANNER_WAKE_; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_SCANNER_WAKE_OFF() { ramses_control_shadow |=  RAMSES_CONTROL_SCANNER_WAKE_; RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_LED_BLUE_ON()      { ramses_control_shadow &= ~RAMSES_CONTROL_LED_BLUE_;     RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_LED_BLUE_OFF()     { ramses_control_shadow |=  RAMSES_CONTROL_LED_BLUE_;     RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_LED_ORANGE_ON()    { ramses_control_shadow &= ~RAMSES_CONTROL_LED_ORANGE_;   RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_LED_ORANGE_OFF()   { ramses_control_shadow |=  RAMSES_CONTROL_LED_ORANGE_;   RAMSES_CONTROL = ramses_control_shadow; }
++
++// Active high
++#define RAMSES_SCANNER_ON()       { ramses_control_shadow |=  RAMSES_CONTROL_SCANNER_PWR;   RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_SCANNER_OFF()      { ramses_control_shadow &= ~RAMSES_CONTROL_SCANNER_PWR;   RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_GSM_RESET_ON()     { ramses_control_shadow |=  RAMSES_CONTROL_GSM_RESET;     RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_GSM_RESET_OFF()    { ramses_control_shadow &= ~RAMSES_CONTROL_GSM_RESET;     RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_GSM_BOOT_ON()      { ramses_control_shadow |=  RAMSES_CONTROL_GSM_BOOT;      RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_GSM_BOOT_OFF()     { ramses_control_shadow &= ~RAMSES_CONTROL_GSM_BOOT;      RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_GSM_ON()           { ramses_control_shadow |=  RAMSES_CONTROL_GSM_PWR;       RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_GSM_OFF()          { ramses_control_shadow &= ~RAMSES_CONTROL_GSM_PWR;       RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_USB_INTERN()       { ramses_control_shadow |=  RAMSES_CONTROL_USB_INTERN;    RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_USB_EXTERN()       { ramses_control_shadow &= ~RAMSES_CONTROL_USB_INTERN;    RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_UART_ON()          { ramses_control_shadow |=  RAMSES_CONTROL_UART_PWR;      RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_UART_OFF()         { ramses_control_shadow &= ~RAMSES_CONTROL_UART_PWR;      RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_MMC_ON()           { ramses_control_shadow |=  RAMSES_CONTROL_MMC_PWR;       RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_MMC_OFF()          { ramses_control_shadow &= ~RAMSES_CONTROL_MMC_PWR;       RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_LCD_BLIGHT_ON()    { ramses_control_shadow |=  RAMSES_CONTROL_LCD_BLIGHT;    RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_LCD_BLIGHT_OFF()   { ramses_control_shadow &= ~RAMSES_CONTROL_LCD_BLIGHT;    RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_USB_BUS_ON()       { ramses_control_shadow |=  RAMSES_CONTROL_USB;           RAMSES_CONTROL = ramses_control_shadow; }
++#define RAMSES_USB_BUS_OFF()      { ramses_control_shadow &= ~RAMSES_CONTROL_USB;           RAMSES_CONTROL = ramses_control_shadow; }
++
++// Corevolt settings
++#define RAMSES_COREVOLT_          (RAMSES_COREVOLT_PHYS)
++#define RAMSES_COREVOLT           __CORE_REG(RAMSES_COREVOLT_)
++
++// Battery protocol
++#define HDQ_TMP               0x02
++#define HDQ_LMD               0x05
++#define HDQ_VSB               0x0b
++#define HDQ_CACT      0x0d
++#define HDQ_SAEH      0x0f
++#define HDQ_SAEL      0x10
++#define HDQ_RCAC      0x11
++#define HDQ_DCR               0x18
++
++
++#ifndef __ASSEMBLY__
++
++/* Ramses specific functions */
++void ramses_lcd_power_on(void);
++void ramses_lcd_power_off(void);
++void ramses_lcd_backlight_on(void);
++void ramses_lcd_backlight_off(void);
++void ramses_lcd_set_intensity(int i);
++#ifdef OLDCODE
++void ramses_lcd_set_pwm1(int p);
++#endif
++void ramses_lcd_set_brightness(int b);
++void ramses_lcd_set_contrast(int c);
++int ramses_lcd_get_intensity(void);
++int ramses_lcd_get_brightness(void);
++int ramses_lcd_get_contrast(void);
++int ramses_hdq_get_reg(unsigned char reg);
++void ramses_shut_off(void);
++void ramses_set_corevolt(int volt);
++
++
++/* shadow registers for write only registers */
++extern u16 ramses_control_shadow;
++extern int ramses_corevolt_shadow;
++extern u16 ramses_lcd_type;
++extern int ramses_lcd_pwm1_shadow;
++
++
++/* flag register for various settings */
++extern unsigned int ramses_flags;
++
++/* 
++ * macros to write to write only register
++ *
++ * none of these macros are protected from 
++ * multiple drivers using them in interrupt context.
++ */
++
++#define WRITE_RAMSES_CONTROL(value, mask) \
++{\
++      ramses_control_shadow = ((value & mask) | (ramses_control_shadow & ~mask));\
++      RAMSES_CONTROL = ramses_control_shadow;\
++}
++#endif
++
++/*
++ *  USB Host
++ *
++ *  The SL811HS is selected with nCS3 and some address bits:
++ *
++ *                               12    8    4
++ *  nA14, nCS[3], address mask 1011 1111 1111 0000 = xBf00
++ */
++#define SL811HS_PHYS (PXA_CS3_PHYS+0xBFF0)
++#define SL811HS_DATA (PXA_CS3_PHYS+0xBFF4)
 +
-+      /* 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));
-+}
 +
-+/* 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];
 +
-+      jffs2_block_refile(c, jeb, REFILE_NOTEMPTY);
++#define PCC_DETECT(x) (GPLR(7 + (x)) & GPIO_bit(7 + (x)))
 +
-+      /* 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);
++/* A listing of interrupts used by external hardware devices */
 +
-+      D1(printk(KERN_DEBUG "wbuf recover %08x-%08x\n", start, end));
++#define TOUCH_PANEL_IRQ                       IRQ_GPIO(21)
++#define TOUCH_PANEL_IRQ_EDGE          GPIO_FALLING_EDGE
 +
-+      buf = NULL;
-+      if (start < c->wbuf_ofs) {
-+              /* First affected node was already partially written.
-+               * Attempt to reread the old data into our buffer. */
++#define ETHERNET_IRQ                  IRQ_GPIO(4)
++#define ETHERNET_IRQ_EDGE             GPIO_RISING_EDGE
 +
-+              buf = kmalloc(end - start, GFP_KERNEL);
-+              if (!buf) {
-+                      printk(KERN_CRIT "Malloc failure in wbuf recovery. Data loss ensues.\n");
++#define CFCARD_CD_VALID                       IRQ_GPIO(8)
++#define CFCARD_CD_VALID_EDGE          GPIO_BOTH_EDGES
 +
-+                      goto read_failed;
-+              }
++#define CFCARD_RDYINT                 IRQ_GPIO(22)
 +
-+              /* Do the read... */
-+              if (jffs2_cleanmarker_oob(c))
-+                      ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo);
-+              else
-+                      ret = c->mtd->read(c->mtd, start, c->wbuf_ofs - start, &retlen, buf);
-+              
-+              if (ret == -EBADMSG && 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");
++#define RAMSES_KEYBOARD_IRQ           IRQ_GPIO(3)
++#define RAMSES_KEYBOARD_IRQ_EDGE      GPIO_FALLING_EDGE
 +
-+                      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;
++#define SL811HS_IRQ                   IRQ_GPIO(32)
++#define SL811HS_IRQ_EDGE              GPIO_RISING_EDGE
 +
-+                      /* 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 */
++/*
++ * Macros for LED Driver
++ */
 +
++/* leds 0 = ON */
++#define RAMSES_HB_LED (1<<5)  
++#define RAMSES_BUSY_LED       (1<<6)
 +
-+      /* ... 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");
-+              kfree(buf);
-+              return;
-+      }
-+      if (end-start >= c->wbuf_pagesize) {
-+              /* Need to do another write immediately, but it's possible
-+                 that this is just because the wbuf itself is completely
-+                 full, and there's nothing earlier read back from the 
-+                 flash. Hence 'buf' isn't necessarily what we're writing 
-+                 from. */
-+              unsigned char *rewrite_buf = buf?:c->wbuf;
-+              uint32_t towrite = (end-start) - ((end-start)%c->wbuf_pagesize);
++#define RAMSES_LEDS_MASK              (RAMSES_HB_LED | RAMSES_BUSY_LED)
 +
-+              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
-+              if (jffs2_cleanmarker_oob(c))
-+                      ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen,
-+                                              rewrite_buf, NULL, c->oobinfo);
-+              else
-+                      ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, rewrite_buf);
++#define RAMSES_WRITE_LEDS(value)      (RAMSES_CPLD_LED_CONTROL = ((RAMSES_CPLD_LED_CONTROL & ~(RAMSES_LEDS_MASK)) | value))
 +
-+              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);
++/*
++ * macros for MTD driver
++ */
 +
-+                      if (retlen) {
-+                              struct jffs2_raw_node_ref *raw2;
++#define FLASH_WRITE_PROTECT_DISABLE() ((RAMSES_CPLD_FLASH_WE) &= ~(0x1))
++#define FLASH_WRITE_PROTECT_ENABLE()  ((RAMSES_CPLD_FLASH_WE) |= (0x1))
 +
-+                              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;
+--- linux-2.4.21/include/asm-arm/arch-pxa/time.h~pxa-timerint
++++ linux-2.4.21/include/asm-arm/arch-pxa/time.h
+@@ -33,7 +33,7 @@
+ /* IRQs are disabled before entering here from do_gettimeofday() */
+ static unsigned long pxa_gettimeoffset (void)
+ {
+-      unsigned long ticks_to_match, elapsed, usec;
++      long ticks_to_match, elapsed, usec;
+       /* Get ticks before next timer match */
+       ticks_to_match = OSMR0 - OSCR;
+@@ -41,6 +41,10 @@
+       /* We need elapsed ticks since last match */
+       elapsed = LATCH - ticks_to_match;
++      /* don't get fooled by the workaround in pxa_timer_interrupt() */
++      if (elapsed <= 0)
++              return 0;
 +
-+                              jffs2_add_physical_node_ref(c, raw2);
-+                      }
-+                      return;
-+              }
-+              printk(KERN_NOTICE "Recovery of wbuf succeeded to %08x\n", ofs);
+       /* Now convert them to usec */
+       usec = (unsigned long)(elapsed*tick)/LATCH;
+@@ -59,6 +63,15 @@
+        * IRQs are disabled inside the loop to ensure coherence between
+        * lost_ticks (updated in do_timer()) and the match reg value, so we
+        * can use do_gettimeofday() from interrupt handlers.
++       *
++       * HACK ALERT: it seems that the PXA timer regs aren't updated right
++       * away in all cases when a write occurs.  We therefore compare with
++       * 8 instead of 0 in the while() condition below to avoid missing a
++       * match if OSCR has already reached the next OSMR value.
++       * Experience has shown that up to 6 ticks are needed to work around
++       * this problem, but let's use 8 to be conservative.  Note that this
++       * affect things only when the timer IRQ has been delayed by nearly
++       * exactly one tick period which should be a pretty rare event. 
+        */
+       do {
+               do_leds();
+@@ -68,7 +81,7 @@
+               OSSR = OSSR_M0;  /* Clear match on timer 0 */
+               next_match = (OSMR0 += LATCH);
+               restore_flags( flags );
+-      } while( (signed long)(next_match - OSCR) <= 0 );
++      } while( (signed long)(next_match - OSCR) <= 8 );
+ }
+ extern inline void setup_timer (void)
+--- /dev/null
++++ linux-2.4.21/include/asm-arm/bug.h
+@@ -0,0 +1 @@
++/* dummy */
+--- /dev/null
++++ linux-2.4.21/include/asm-arm/sl811-hw.h
+@@ -0,0 +1,202 @@
++/*
++File: include/asm-arm/sl811-hw.h
 +
-+              c->wbuf_len = (end - start) - towrite;
-+              c->wbuf_ofs = ofs + towrite;
-+              memmove(c->wbuf, rewrite_buf + towrite, c->wbuf_len);
-+              /* Don't muck about with c->wbuf_inodes. False positives are harmless. */
-+              if (buf)
-+                      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;
-+      }
++19.09.2003 hne@ist1.de
++Use Kernel 2.4.20 and this source from 2.4.22
++Splitt hardware depens into file sl811-x86.h and sl811-arm.h.
++Functions as inline.
 +
-+      /* 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];
++23.09.2003 hne
++Move Hardware depend header sl811-arm.h into include/asm-arm/sl811-hw.h.
++GPRD as parameter.
 +
-+      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;
-+      }
++24.09.2003 hne
++Use Offset from ADDR to DATA instand of direct io.
 +
-+      raw = first_raw;
-+      while (*raw) {
-+              uint32_t rawlen = ref_totlen(c, jeb, *raw);
++03.10.2003 hne
++Low level only for port io into hardware-include.
++*/
 +
-+              D1(printk(KERN_DEBUG "Refiling block of %08x at %08x(%d) to %08x\n",
-+                        rawlen, ref_offset(*raw), ref_flags(*raw), ofs));
++#ifndef __LINUX_SL811_HW_H
++#define __LINUX_SL811_HW_H
 +
-+              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;
++#ifdef CONFIG_X86
++#define MAX_CONTROLERS                1       /* Max number of sl811 controllers */
++                                      /* Always 1 for this architecture! */
 +
-+              raw = &(*raw)->next_phys;
-+      }
++#define SIZEOF_IO_REGION      1       /* Size for request/release region */
 +
-+      /* 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);
++#define OFFSET_DATA_REG data_off      /* Offset from ADDR_IO to DATA_IO (future) */
++                                      /* Can change by arg */
 +
-+      ACCT_SANITY_CHECK(c,jeb);
-+        D1(ACCT_PARANOIA_CHECK(jeb));
++static int io = 0xf100000e;   /* Base addr_io */
++static int data_off = 1;      /* Offset from addr_io to addr_io */
++static int irq = 44;          /* also change gprd !!! */
++static int gprd = 23;         /* also change irq  !!! */
 +
-+      ACCT_SANITY_CHECK(c,new_jeb);
-+        D1(ACCT_PARANOIA_CHECK(new_jeb));
++MODULE_PARM(io,"i");
++MODULE_PARM_DESC(io,"sl811 address io port 0xf100000e");
++MODULE_PARM(data_off,"i");
++MODULE_PARM_DESC(data_off,"sl811 data io port offset from address port (default 1)");
++MODULE_PARM(irq,"i");
++MODULE_PARM_DESC(irq,"sl811 irq 44(default)");
++MODULE_PARM(gprd,"i");
++MODULE_PARM_DESC(gprd,"sl811 GPRD port 23(default)");
++#endif
 +
-+      spin_unlock(&c->erase_completion_lock);
++#ifdef CONFIG_ARCH_RAMSES
++#define SIZEOF_IO_REGION        8       /* Size for request/release region */
++static void *ramses_sl811hs;            /* dynamically assign virtual address */
++#endif
 +
-+      D1(printk(KERN_DEBUG "wbuf recovery completed OK\n"));
++
++/*
++ * Low level: Read from Data port [arm]
++ */
++static __u8 inline sl811_read_data (struct sl811_hc *hc)
++{
++      __u8 data;
++      data = readb(hc->data_io);
++      rmb();
++//printk("%s: in  %08p %02x\n", __FUNCTION__, hc->data_io, data);
++      return data;
 +}
 +
-+/* 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
-+*/
-+#define NOPAD         0
-+#define PAD_NOACCOUNT 1
-+#define PAD_ACCOUNTING        2
++/*
++ * Low level: Write to index register [arm]
++ */
++static void inline sl811_write_index (struct sl811_hc *hc, __u8 index)
++{
++//printk("%s: out %08p %02x\n", __FUNCTION__, hc->addr_io, index);
++      writeb(index, hc->addr_io);
++      wmb();
++}
 +
-+static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
++/*
++ * Low level: Write to Data port [arm]
++ */
++static void inline sl811_write_data (struct sl811_hc *hc, __u8 data)
 +{
-+      int ret;
-+      size_t retlen;
++//printk("%s: out %08p %02x\n", __FUNCTION__, hc->data_io, data);
++      writeb(data, hc->data_io);
++      wmb();
++}
 +
-+      /* Nothing to do if not write-buffering the flash. In particular, we shouldn't
-+         del_timer() the timer we never initialised. */
-+      if (!jffs2_is_writebuffered(c))
-+              return 0;
++/*
++ * Low level: Write to index register and data port [arm]
++ */
++static void inline sl811_write_index_data (struct sl811_hc *hc, __u8 index, __u8 data)
++{
++      writeb(index, hc->addr_io);
++//printk("%s: out %08p %02x\n", __FUNCTION__, hc->addr_io, index);
++      writeb(data, hc->data_io);
++//printk("%s: out %08p %02x\n", __FUNCTION__, hc->data_io, data);
++      wmb();
++}
 +
-+      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_len)       /* already checked c->wbuf above */
-+              return 0;
++/*
++ * This       function is board specific.  It sets up the interrupt to
++ * be an edge trigger and trigger on the rising       edge
++ */
++static void inline sl811_init_irq(void)
++{
++#ifdef CONFIG_X86
++      GPDR &= ~(1<<gprd);
++      set_GPIO_IRQ_edge(1<<gprd, GPIO_RISING_EDGE);
++#endif
++#ifdef CONFIG_ARCH_PXA
++      int irq_gpio_pin = IRQ_TO_GPIO_2_80(SL811HS_IRQ);
++      GPDR(irq_gpio_pin) &= ~GPIO_bit(irq_gpio_pin);
++      set_GPIO_IRQ_edge(irq_gpio_pin, SL811HS_IRQ_EDGE);
++#endif
++}
 +
-+      /* 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 && !jffs2_dataflash(c)) {
-+              c->wbuf_len = PAD(c->wbuf_len);
++/*****************************************************************
++ *
++ * Function Name: release_regions [arm]
++ *
++ * This function is board specific. It release all io address
++ * from memory (if can).
++ *
++ * Input: struct sl811_hc * *
++ *
++ * Return value  : 0 = OK
++ *
++ *****************************************************************/
++static void inline sl811_release_regions(struct sl811_hc *hc)
++{
++#ifdef CONFIG_X86
++      if (hc->addr_io)
++              release_region(hc->addr_io, SIZEOF_IO_REGION);
++      hc->addr_io = 0;
 +
-+              /* Pad with JFFS2_DIRTY_BITMASK initially.  this helps out ECC'd NOR
-+                 with 8 byte page size */
-+              memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - 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));
-+              }
++      if (hc->data_io)
++              release_region(hc->data_io, SIZEOF_IO_REGION);
++      hc->data_io = 0;
++#endif
++#ifdef CONFIG_ARCH_RAMSES
++      if (ramses_sl811hs) {
++              iounmap(ramses_sl811hs);
++              release_mem_region(SL811HS_PHYS, SIZEOF_IO_REGION);
 +      }
-+      /* 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 
++      hc->addr_io = 0;
++      hc->data_io = 0;
++      RAMSES_CPLD_PERIPH_PWR &= ~USB_HOST_PWR_EN;
++      RAMSES_USB_BUS_OFF();
 +#endif
-+      
-+      if (jffs2_cleanmarker_oob(c))
-+              ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo);
-+      else
-+              ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf);
-+
-+      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;
++/*****************************************************************
++ *
++ * Function Name: request_regions [arm]
++ *
++ * This function is board specific. It request all io address and
++ * maps into memory (if can).
++ *
++ * Input: struct sl811_hc *
++ *
++ * Return value  : 0 = OK
++ *
++ *****************************************************************/
++static int inline sl811_request_regions (struct sl811_hc *hc, int addr_io, int data_io, const char *name)
++{
++#ifdef CONFIG_X86
++      if (!request_region(addr_io, SIZEOF_IO_REGION, name)) {
++              PDEBUG(3, "request address %d failed", addr_io);
++              return -EBUSY;
 +      }
++      hc->addr_io =   addr_io;
 +
-+      spin_lock(&c->erase_completion_lock);
-+
-+      /* Adjust free size of the block if we padded. */
-+      if (pad && !jffs2_dataflash(c)) {
-+              struct jffs2_eraseblock *jeb;
-+
-+              jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
-+
-+              D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of %sblock at %08x\n",
-+                        (jeb==c->nextblock)?"next":"", jeb->offset));
-+
-+              /* 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 (jeb->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",
-+                             jeb->offset, jeb->free_size);
-+                      BUG();
-+              }
-+              jeb->free_size -= (c->wbuf_pagesize - c->wbuf_len);
-+              c->free_size -= (c->wbuf_pagesize - c->wbuf_len);
-+              jeb->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
-+              c->wasted_size += (c->wbuf_pagesize - c->wbuf_len);
++      if (!request_region(data_io, SIZEOF_IO_REGION, MODNAME)) {
++              PDEBUG(3, "request address %d failed", data_io);
++              /* release_region(hc->addr_io, SIZEOF_IO_REGION); */
++              return -EBUSY;
 +      }
++      hc->data_io =   data_io;
++#endif
++#ifdef CONFIG_ARCH_RAMSES
++      RAMSES_USB_BUS_ON();
++      RAMSES_CPLD_PERIPH_PWR |= USB_HOST_PWR_EN;
++      mdelay(300);
 +
-+      /* Stick any now-obsoleted blocks on the erase_pending_list */
-+      jffs2_refile_wbuf_blocks(c);
-+      jffs2_clear_wbuf_ino_list(c);
-+      spin_unlock(&c->erase_completion_lock);
++        if (!request_mem_region(SL811HS_PHYS, SIZEOF_IO_REGION, name)) {
++                printk(KERN_ERR "unable to reserve region\n");
++                return -EBUSY;
++        } else {
++                ramses_sl811hs = ioremap_nocache(SL811HS_PHYS, SIZEOF_IO_REGION);
++              dbg("phys %p -> virt %p\n", SL811HS_PHYS, ramses_sl811hs);
++                if (!ramses_sl811hs) {
++                        printk(KERN_ERR "unable to map region\n");
++                        release_mem_region(SL811HS_PHYS, SIZEOF_IO_REGION);
++                        return -EBUSY;
++                }
++        }
++        hc->addr_io = (unsigned long) ramses_sl811hs;
++        hc->data_io = (unsigned long) ramses_sl811hs+4;
++#endif
 +
-+      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));
-+
-+      if (!c->wbuf)
-+              return 0;
++#endif // __LINUX_SL811_HW_H
+--- linux-2.4.21/include/linux/apm_bios.h~pm
++++ linux-2.4.21/include/linux/apm_bios.h
+@@ -16,6 +16,8 @@
+  * General Public License for more details.
+  */
++#include <linux/pm-devices.h>
 +
-+      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;
-+      }
+ typedef unsigned short        apm_event_t;
+ typedef unsigned short        apm_eventinfo_t;
+@@ -59,6 +61,16 @@
+ };
+ /*
++ * Allow device specific code to register function which
++ * gets the battery power status (see arch/arm/mach-pxa/apm.c).
++ */
++extern void apm_register_get_power_status( int (*fn)(u_char *ac_line_status,
++                              u_char *battery_status,
++                              u_char *battery_flag,
++                              u_char *battery_percentage,
++                              u_short *battery_life));
 +
-+      old_wbuf_ofs = c->wbuf_ofs;
-+      old_wbuf_len = c->wbuf_len;
++/*
+  * The APM function codes
+  */
+ #define       APM_FUNC_INST_CHECK     0x5300
+@@ -168,6 +180,7 @@
+ /*
+  * APM Device IDs
+  */
++#ifdef _i386_
+ #define APM_DEVICE_BIOS               0x0000
+ #define APM_DEVICE_ALL                0x0001
+ #define APM_DEVICE_DISPLAY    0x0100
+@@ -181,6 +194,21 @@
+ #define APM_DEVICE_OLD_ALL    0xffff
+ #define APM_DEVICE_CLASS      0x00ff
+ #define APM_DEVICE_MASK               0xff00
++#endif
 +
-+      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"));
-+              down_write(&c->wbuf_sem);
-+              ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
-+              /* retry flushing wbuf in case jffs2_wbuf_recover
-+                 left some data in the wbuf */
-+              if (ret)
-+                      ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
-+              up_write(&c->wbuf_sem);
-+      } else while (old_wbuf_len &&
-+                    old_wbuf_ofs == c->wbuf_ofs) {
++/*
++ *  APM devices IDs for non-x86
++ */
++#define APM_DEVICE_ALL                PM_SYS_DEV
++#define APM_DEVICE_DISPLAY    PM_DISPLAY_DEV
++#define APM_DEVICE_STORAGE    PM_STORAGE_DEV
++#define APM_DEVICE_PARALLEL   PM_PARALLEL_DEV
++#define APM_DEVICE_SERIAL     PM_SERIAL_DEV
++#define APM_DEVICE_NETWORK    PM_NETWORK_DEV
++#define APM_DEVICE_PCMCIA     PM_PCMCIA_DEV
++#define APM_DEVICE_BATTERY    PM_BATTERY_DEV
++#define APM_DEVICE_TPANEL     PM_TPANEL_DEV
 +
-+              up(&c->alloc_sem);
+ #ifdef __KERNEL__
+ /*
+@@ -214,5 +242,6 @@
+ #define APM_IOC_STANDBY               _IO('A', 1)
+ #define APM_IOC_SUSPEND               _IO('A', 2)
++#define APM_IOC_SET_WAKEUP    _IO('A', 3)
+ #endif        /* LINUX_APM_H */
+--- linux-2.4.21/include/linux/crc32.h~mtd-cvs
++++ linux-2.4.21/include/linux/crc32.h
+@@ -46,4 +46,25 @@
+       return crc;
+ }
+-#endif /* _LINUX_CRC32_H */
++#ifndef CRC32_H
++#define CRC32_H
 +
-+              D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() calls gc pass\n"));
++/* $Id$ */
 +
-+              ret = jffs2_garbage_collect_pass(c);
-+              if (ret) {
-+                      /* GC failed. Flush it with padding instead */
-+                      down(&c->alloc_sem);
-+                      down_write(&c->wbuf_sem);
-+                      ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
-+                      /* retry flushing wbuf in case jffs2_wbuf_recover
-+                         left some data in the wbuf */
-+                      if (ret)
-+                              ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
-+                      up_write(&c->wbuf_sem);
-+                      break;
-+              }
-+              down(&c->alloc_sem);
-+      }
++#include <linux/types.h>
 +
-+      D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() ends...\n"));
++extern const uint32_t crc32_table[256];
 +
-+      up(&c->alloc_sem);
-+      return ret;
-+}
++/* Return a 32-bit CRC of the contents of the buffer. */
 +
-+/* Pad write-buffer to end and write it, wasting space. */
-+int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c)
++static inline uint32_t 
++crc32(uint32_t val, const void *ss, int len)
 +{
-+      int ret;
-+
-+      if (!c->wbuf)
-+              return 0;
-+
-+      down_write(&c->wbuf_sem);
-+      ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
-+      /* retry - maybe wbuf recover left some data in wbuf. */
-+      if (ret)
-+              ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
-+      up_write(&c->wbuf_sem);
-+
-+      return ret;
++      const unsigned char *s = ss;
++        while (--len >= 0)
++                val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8);
++        return val;
 +}
 +
-+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
-+#define PAGE_DIV(x) ( ((unsigned long)(x) / (unsigned long)(c->wbuf_pagesize)) * (unsigned long)(c->wbuf_pagesize) )
-+#define PAGE_MOD(x) ( (unsigned long)(x) % (unsigned long)(c->wbuf_pagesize) )
-+#else
-+#define PAGE_DIV(x) ( (x) & (~(c->wbuf_pagesize - 1)) )
-+#define PAGE_MOD(x) ( (x) & (c->wbuf_pagesize - 1) )
 +#endif
++#endif
+--- /dev/null
++++ linux-2.4.21/include/linux/firmware.h
+@@ -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
+--- linux-2.4.21/include/linux/fs.h~mtd-cvs
++++ linux-2.4.21/include/linux/fs.h
+@@ -1376,6 +1376,7 @@
+ extern void iput(struct inode *);
+ extern void force_delete(struct inode *);
+ extern struct inode * igrab(struct inode *);
++extern struct inode * ilookup(struct super_block *, unsigned long);
+ extern ino_t iunique(struct super_block *, ino_t);
+ typedef int (*find_inode_t)(struct inode *, unsigned long, void *);
+--- linux-2.4.21/include/linux/i2c-id.h~i2c-ds1337
++++ linux-2.4.21/include/linux/i2c-id.h
+@@ -95,13 +95,14 @@
+ #define I2C_DRIVERID_ADV717x  48     /* ADV 7175/7176 video encoder   */
+ #define I2C_DRIVERID_ZR36067  49     /* Zoran 36067 video encoder     */
+ #define I2C_DRIVERID_ZR36120  50     /* Zoran 36120 video encoder     */
+-#define I2C_DRIVERID_24LC32A  51              /* Microchip 24LC32A 32k EEPROM */
++#define I2C_DRIVERID_24LC32A  51     /* Microchip 24LC32A 32k EEPROM  */
++#define I2C_DRIVERID_DS1337     52     /* DS1337 real time clock        */
+-#define I2C_DRIVERID_DS1307   46      /* real time clock: DS1307      */
+-#define I2C_DRIVERID_24LC64   47      /* EEprom 24LC64                */
+-#define I2C_DRIVERID_FM24CLB4 48      /* EEprom FM24CLB4              */
++//#define I2C_DRIVERID_DS1307 46      /* real time clock: DS1307      */
++//#define I2C_DRIVERID_24LC64 47      /* EEprom 24LC64                */
++//#define I2C_DRIVERID_FM24CLB4       48      /* EEprom FM24CLB4              */
+ #define I2C_DRIVERID_EXP0     0xF0    /* experimental use id's        */
+ #define I2C_DRIVERID_EXP1     0xF1
+--- linux-2.4.21/include/linux/input.h~bluetooth
++++ linux-2.4.21/include/linux/input.h
+@@ -472,7 +472,8 @@
+ #define BUS_PCI                       0x01
+ #define BUS_ISAPNP            0x02
+ #define BUS_USB                       0x03
+-#define BUS_HIL                 0x04
++#define BUS_HIL                       0x04
++#define BUS_BLUETOOTH         0x05
+ #define BUS_ISA                       0x10
+ #define BUS_I8042             0x11
+--- linux-2.4.21/include/linux/jffs2.h~mtd-cvs
++++ linux-2.4.21/include/linux/jffs2.h
+@@ -1,50 +1,30 @@
+ /*
+  * JFFS2 -- Journalling Flash File System, Version 2.
+  *
+- * Copyright (C) 2001 Red Hat, Inc.
+- *
+- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+- *
+- * The original JFFS, from which the design for JFFS2 was derived,
+- * was designed and implemented by Axis Communications AB.
+- *
+- * 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.
++ * Copyright (C) 2001-2003 Red Hat, Inc.
+  *
+- * The Original Code is JFFS2 - Journalling Flash File System, version 2
++ * Created by David Woodhouse <dwmw2@infradead.org>
+  *
+- * 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.
++ * For licensing information, see the file 'LICENCE' in the 
++ * jffs2 directory.
+  *
+- * $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. */
 +
-+int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino)
-+{
-+      struct kvec 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 (!jffs2_is_writebuffered(c))
-+              return jffs2_flash_direct_writev(c, invecs, count, to, retlen);
-+      
-+      down_write(&c->wbuf_sem);
-+
-+      /* 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);
-+      }
-+
-+      /* Fixup the wbuf if we are moving to a new eraseblock.  The checks below
-+         fail for ECC'd NOR because cleanmarker == 16, so a block starts at
-+         xxx0010.  */
-+      if (jffs2_nor_ecc(c)) {
-+              if (((c->wbuf_ofs % c->sector_size) == 0) && !c->wbuf_len) {
-+                      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 (SECTOR_ADDR(to) != SECTOR_ADDR(c->wbuf_ofs)) {
-+              /* 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(c, PAD_NOACCOUNT);
-+                      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;
-+                              goto exit;
-+                      }
-+              }
-+              /* set pointer to new block */
-+              c->wbuf_ofs = PAGE_DIV(to);
-+              c->wbuf_len = PAGE_MOD(to);                     
-+      } 
+ #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
+@@ -63,6 +43,8 @@
+ #define JFFS2_COMPR_COPY      0x04
+ #define JFFS2_COMPR_DYNRUBIN  0x05
+ #define JFFS2_COMPR_ZLIB      0x06
++#define JFFS2_COMPR_LZO         0x07
++#define JFFS2_COMPR_LZARI       0x08
+ /* Compatibility flags. */
+ #define JFFS2_COMPAT_MASK 0xc000      /* What do to if an unknown nodetype is found */
+ #define JFFS2_NODE_ACCURATE 0x2000
+@@ -78,16 +60,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 +74,46 @@
+                                          compression type */
++/* These can go once we've made sure we've caught all uses without
++   byteswapping */
 +
-+      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();
-+      }
++typedef struct {
++      uint32_t v32;
++} __attribute__((packed))  jint32_t;
 +
-+      /* 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();
-+      }
++typedef struct {
++      uint32_t m;
++} __attribute__((packed))  jmode_t;
 +
-+      invec = 0;
-+      outvec = 0;
++typedef struct {
++      uint16_t v16;
++} __attribute__((packed)) jint16_t;
 +
-+      /* Fill writebuffer first, if already in use */ 
-+      if (c->wbuf_len) {
-+              uint32_t invec_ofs = 0;
+ 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 +124,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 {
+--- linux-2.4.21/include/linux/jffs2_fs_i.h~mtd-cvs
++++ linux-2.4.21/include/linux/jffs2_fs_i.h
+@@ -1,22 +1,13 @@
+-/* $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>
++#include <asm/semaphore.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 +17,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 +35,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 */
+-
+--- linux-2.4.21/include/linux/jffs2_fs_sb.h~mtd-cvs
++++ linux-2.4.21/include/linux/jffs2_fs_sb.h
+@@ -1,18 +1,23 @@
+-/* $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
++#include <linux/rwsem.h>
+ #define JFFS2_SB_FLAG_RO 1
+-#define JFFS2_SB_FLAG_MOUNTING 2
++#define JFFS2_SB_FLAG_SCANNING 2 /* Flash scanning is in progress */
++#define JFFS2_SB_FLAG_BUILDING 4 /* File system building is in progress */
 +
-+              /* 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;
++struct jffs2_inodirty;
+ /* A struct for the overall file system control.  Pointers to
+    jffs2_sb_info structs are named `c' in the source code.  
+@@ -21,36 +26,44 @@
+ struct jffs2_sb_info {
+       struct mtd_info *mtd;
+-      __u32 highest_ino;
++      uint32_t highest_ino;
++      uint32_t checked_ino;
 +
-+                      thislen = c->wbuf_pagesize - c->wbuf_len;
+       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;
++                                         out-of-order writing of nodes. And GC. */
++      uint32_t cleanmarker_size;      /* Size of an _inline_ CLEANMARKER
++                                       (i.e. zero for OOB CLEANMARKER */
+-      __u32 nr_free_blocks;
+-      __u32 nr_erasing_blocks;
++      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;
+-      __u32 nr_blocks;
++      uint32_t nr_free_blocks;
++      uint32_t nr_erasing_blocks;
 +
-+                      if (thislen >= invecs[invec].iov_len)
-+                              thislen = invecs[invec].iov_len;
-+      
-+                      invec_ofs = thislen;
++      /* 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 */
 +
-+                      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, NOPAD);
-+              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;
-+                      goto exit;
-+              }
-+              outvec_to += donelen;
-+              c->wbuf_ofs = outvec_to;
++      uint32_t nospc_dirty_size;
 +
-+              /* All invecs done ? */
-+              if (invec == count)
-+                      goto alldone;
++      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,35 @@
+       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];
 +
-+              /* 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++;
-+      }
++      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;
 +
-+      /* OK, now we've flushed the wbuf and the start of the bits
-+         we have been asked to write, now to write the rest.... */
++#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
++      /* 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;
 +
-+      /* 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;
-+              }
-+      }
++      struct rw_semaphore wbuf_sem;   /* Protects the write buffer */
 +
-+      /* 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 */
++      /* 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 */
+--- /dev/null
++++ linux-2.4.21/include/linux/mtd/blktrans.h
+@@ -0,0 +1,72 @@
++/*
++ * $Id$
++ *
++ * (C) 2003 David Woodhouse <dwmw2@infradead.org>
++ *
++ * Interface to Linux block layer for MTD 'translation layers'.
++ *
++ */
 +
-+      if (splitvec != -1) {
-+              uint32_t remainder;
++#ifndef __MTD_TRANS_H__
++#define __MTD_TRANS_H__
 +
-+              remainder = outvecs[splitvec].iov_len - split_ofs;
-+              outvecs[splitvec].iov_len = split_ofs;
++#include <asm/semaphore.h>
 +
-+              /* We did cross a page boundary, so we write some now */
-+              if (jffs2_cleanmarker_oob(c))
-+                      ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, c->oobinfo); 
-+              else
-+                      ret = jffs2_flash_direct_writev(c, outvecs, splitvec+1, outvec_to, &wbuf_retlen);
-+              
-+              if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) {
-+                      /* At this point we have no problem,
-+                         c->wbuf is empty. However refile nextblock to avoid
-+                         writing again to same address.
-+                      */
-+                      struct jffs2_eraseblock *jeb;
++struct hd_geometry;
++struct mtd_info;
++struct mtd_blktrans_ops;
++struct file;
++struct inode;
 +
-+                      spin_lock(&c->erase_completion_lock);
++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 */
++};
 +
-+                      jeb = &c->blocks[outvec_to / c->sector_size];
-+                      jffs2_block_refile(c, jeb, REFILE_ANYWAY);
++struct blkcore_priv; /* Differs for 2.4 and 2.5 kernels; private */
 +
-+                      *retlen = 0;
-+                      spin_unlock(&c->erase_completion_lock);
-+                      goto exit;
-+              }
-+              
-+              donelen += wbuf_retlen;
-+              c->wbuf_ofs = PAGE_DIV(outvec_to) + PAGE_DIV(totlen);
++struct mtd_blktrans_ops {
++      char *name;
++      int major;
++      int part_bits;
 +
-+              if (remainder) {
-+                      outvecs[splitvec].iov_base += split_ofs;
-+                      outvecs[splitvec].iov_len = remainder;
-+              } else {
-+                      splitvec++;
-+              }
++      /* 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);
 +
-+      } else {
-+              splitvec = 0;
-+      }
++      /* Block layer ioctls */
++      int (*getgeo)(struct mtd_blktrans_dev *dev, struct hd_geometry *geo);
++      int (*flush)(struct mtd_blktrans_dev *dev);
 +
-+      /* Now splitvec points to the start of the bits we have to copy
-+         into the wbuf */
-+      wbuf_ptr = c->wbuf;
++      /* 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);
 +
-+      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;
++      /* 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);
 +
-+      /* 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;
++      struct list_head devs;
++      struct list_head list;
++      struct module *owner;
 +
-+      if (c->wbuf_len && ino)
-+              jffs2_wbuf_dirties_inode(c, ino);
++      struct mtd_blkcore_priv *blkcore_priv;
++};
 +
-+      ret = 0;
-+      
-+exit:
-+      up_write(&c->wbuf_sem);
-+      return ret;
-+}
++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);
++                               
 +
-+/*
-+ *    This is the entry for flash write.
-+ *    Check, if we work on NAND FLASH, if so build an kvec 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)
++#endif /* __MTD_TRANS_H__ */
+--- linux-2.4.21/include/linux/mtd/cfi.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/cfi.h
+@@ -1,211 +1,86 @@
+ /* 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>
+ #include <linux/mtd/flashchip.h>
++#include <linux/mtd/map.h>
+ #include <linux/mtd/cfi_endian.h>
+-/*
+- * You can optimize the code size and performance by defining only 
+- * the geometry(ies) available on your hardware.
+- * CFIDEV_INTERLEAVE_n, where  represents the interleave (number of chips to fill the bus width)
+- * CFIDEV_BUSWIDTH_n, where n is the bus width in bytes (1, 2, 4 or 8 bytes)
+- *
+- * By default, all (known) geometries are supported.
+- */
+-
+-#ifndef CONFIG_MTD_CFI_GEOMETRY
+-
+-/* The default case - support all but 64-bit, which has
+-   a performance penalty */
+-
+-#define CFIDEV_INTERLEAVE_1 (1)
+-#define CFIDEV_INTERLEAVE_2 (2)
+-#define CFIDEV_INTERLEAVE_4 (4)
+-
+-#define CFIDEV_BUSWIDTH_1 (1)
+-#define CFIDEV_BUSWIDTH_2 (2)
+-#define CFIDEV_BUSWIDTH_4 (4)
+-
+-typedef __u32 cfi_word;
+-
+-#else
+-
+-/* Explicitly configured buswidth/interleave support */
+-
+ #ifdef CONFIG_MTD_CFI_I1
+-#define CFIDEV_INTERLEAVE_1 (1)
+-#endif
+-#ifdef CONFIG_MTD_CFI_I2
+-#define CFIDEV_INTERLEAVE_2 (2)
+-#endif
+-#ifdef CONFIG_MTD_CFI_I4
+-#define CFIDEV_INTERLEAVE_4 (4)
+-#endif
+-#ifdef CONFIG_MTD_CFI_I8
+-#define CFIDEV_INTERLEAVE_8 (8)
+-#endif
+-
+-#ifdef CONFIG_MTD_CFI_B1
+-#define CFIDEV_BUSWIDTH_1 (1)
+-#endif
+-#ifdef CONFIG_MTD_CFI_B2
+-#define CFIDEV_BUSWIDTH_2 (2)
+-#endif
+-#ifdef CONFIG_MTD_CFI_B4
+-#define CFIDEV_BUSWIDTH_4 (4)
+-#endif
+-#ifdef CONFIG_MTD_CFI_B8
+-#define CFIDEV_BUSWIDTH_8 (8)
+-#endif
+-
+-/* pick the largest necessary */
+-#ifdef CONFIG_MTD_CFI_B8
+-typedef __u64 cfi_word;
+-
+-/* This only works if asm/io.h is included first */
+-#ifndef __raw_readll
+-#define __raw_readll(addr)    (*(volatile __u64 *)(addr))
+-#endif
+-#ifndef __raw_writell
+-#define __raw_writell(v, addr)        (*(volatile __u64 *)(addr) = (v))
+-#endif
+-#define CFI_WORD_64
+-#else  /* CONFIG_MTD_CFI_B8 */
+-/* All others can use 32-bits. It's probably more efficient than
+-   the smaller types anyway */
+-typedef __u32 cfi_word;
+-#endif /* CONFIG_MTD_CFI_B8 */
+-
+-#endif
+-
+-/*
+- * The following macros are used to select the code to execute:
+- *   cfi_buswidth_is_*()
+- *   cfi_interleave_is_*()
+- *   [where * is either 1, 2, 4, or 8]
+- * Those macros should be used with 'if' statements.  If only one of few
+- * geometry arrangements are selected, they expand to constants thus allowing
+- * the compiler (most of them being 0) to optimize away all the unneeded code,
+- * while still validating the syntax (which is not possible with embedded 
+- * #if ... #endif constructs).
+- * The exception to this is the 64-bit versions, which need an extension
+- * to the cfi_word type, and cause compiler warnings about shifts being
+- * out of range.
+- */
+-
+-#ifdef CFIDEV_INTERLEAVE_1
+-# ifdef CFIDEV_INTERLEAVE
+-#  undef CFIDEV_INTERLEAVE
+-#  define CFIDEV_INTERLEAVE (cfi->interleave)
+-# else
+-#  define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_1
+-# endif
+-# define cfi_interleave_is_1() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_1)
++#define cfi_interleave(cfi) 1
++#define cfi_interleave_is_1(cfi) (cfi_interleave(cfi) == 1)
+ #else
+-# define cfi_interleave_is_1() (0)
++#define cfi_interleave_is_1(cfi) (0)
+ #endif
+-#ifdef CFIDEV_INTERLEAVE_2
+-# ifdef CFIDEV_INTERLEAVE
+-#  undef CFIDEV_INTERLEAVE
+-#  define CFIDEV_INTERLEAVE (cfi->interleave)
++#ifdef CONFIG_MTD_CFI_I2
++# ifdef cfi_interleave
++#  undef cfi_interleave
++#  define cfi_interleave(cfi) ((cfi)->interleave)
+ # else
+-#  define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_2
++#  define cfi_interleave(cfi) 2
+ # endif
+-# define cfi_interleave_is_2() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_2)
++#define cfi_interleave_is_2(cfi) (cfi_interleave(cfi) == 2)
+ #else
+-# define cfi_interleave_is_2() (0)
++#define cfi_interleave_is_2(cfi) (0)
+ #endif
+-#ifdef CFIDEV_INTERLEAVE_4
+-# ifdef CFIDEV_INTERLEAVE
+-#  undef CFIDEV_INTERLEAVE
+-#  define CFIDEV_INTERLEAVE (cfi->interleave)
++#ifdef CONFIG_MTD_CFI_I4
++# ifdef cfi_interleave
++#  undef cfi_interleave
++#  define cfi_interleave(cfi) ((cfi)->interleave)
+ # else
+-#  define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_4
++#  define cfi_interleave(cfi) 4
+ # endif
+-# define cfi_interleave_is_4() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_4)
++#define cfi_interleave_is_4(cfi) (cfi_interleave(cfi) == 4)
+ #else
+-# define cfi_interleave_is_4() (0)
++#define cfi_interleave_is_4(cfi) (0)
+ #endif
+-#ifdef CFIDEV_INTERLEAVE_8
+-# ifdef CFIDEV_INTERLEAVE
+-#  undef CFIDEV_INTERLEAVE
+-#  define CFIDEV_INTERLEAVE (cfi->interleave)
++#ifdef CONFIG_MTD_CFI_I8
++# ifdef cfi_interleave
++#  undef cfi_interleave
++#  define cfi_interleave(cfi) ((cfi)->interleave)
+ # else
+-#  define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_8
++#  define cfi_interleave(cfi) 8
+ # endif
+-# define cfi_interleave_is_8() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_8)
++#define cfi_interleave_is_8(cfi) (cfi_interleave(cfi) == 8)
+ #else
+-# define cfi_interleave_is_8() (0)
++#define cfi_interleave_is_8(cfi) (0)
+ #endif
+-#ifndef CFIDEV_INTERLEAVE
+-#error You must define at least one interleave to support!
++static inline int cfi_interleave_supported(int i)
 +{
-+      struct kvec vecs[1];
-+
-+      if (!jffs2_is_writebuffered(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);
++      switch (i) {
++#ifdef CONFIG_MTD_CFI_I1
++      case 1:
+ #endif
+-
+-#ifdef CFIDEV_BUSWIDTH_1
+-# ifdef CFIDEV_BUSWIDTH
+-#  undef CFIDEV_BUSWIDTH
+-#  define CFIDEV_BUSWIDTH (map->buswidth)
+-# else
+-#  define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_1
+-# endif
+-# define cfi_buswidth_is_1() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_1)
+-#else
+-# define cfi_buswidth_is_1() (0)
++#ifdef CONFIG_MTD_CFI_I2
++      case 2:
+ #endif
+-
+-#ifdef CFIDEV_BUSWIDTH_2
+-# ifdef CFIDEV_BUSWIDTH
+-#  undef CFIDEV_BUSWIDTH
+-#  define CFIDEV_BUSWIDTH (map->buswidth)
+-# else
+-#  define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_2
+-# endif
+-# define cfi_buswidth_is_2() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_2)
+-#else
+-# define cfi_buswidth_is_2() (0)
++#ifdef CONFIG_MTD_CFI_I4
++      case 4:
+ #endif
+-
+-#ifdef CFIDEV_BUSWIDTH_4
+-# ifdef CFIDEV_BUSWIDTH
+-#  undef CFIDEV_BUSWIDTH
+-#  define CFIDEV_BUSWIDTH (map->buswidth)
+-# else
+-#  define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_4
+-# endif
+-# define cfi_buswidth_is_4() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_4)
+-#else
+-# define cfi_buswidth_is_4() (0)
++#ifdef CONFIG_MTD_CFI_I8
++      case 8:
+ #endif
++              return 1;
+-#ifdef CFIDEV_BUSWIDTH_8
+-# ifdef CFIDEV_BUSWIDTH
+-#  undef CFIDEV_BUSWIDTH
+-#  define CFIDEV_BUSWIDTH (map->buswidth)
+-# else
+-#  define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_8
+-# endif
+-# define cfi_buswidth_is_8() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_8)
+-#else
+-# define cfi_buswidth_is_8() (0)
+-#endif
++      default:
++              return 0;
++      }
 +}
+-#ifndef CFIDEV_BUSWIDTH
+-#error You must define at least one bus width to support!
+-#endif
+ /* NB: these values must represents the number of bytes needed to meet the 
+  *     device type (x8, x16, x32).  Eg. a 32 bit device is 4 x 8 bytes. 
+@@ -222,88 +97,138 @@
+ /* Basic Query Structure */
+ struct cfi_ident {
+-  __u8  qry[3];
+-  __u16 P_ID;
+-  __u16 P_ADR;
+-  __u16 A_ID;
+-  __u16 A_ADR;
+-  __u8  VccMin;
+-  __u8  VccMax;
+-  __u8  VppMin;
+-  __u8  VppMax;
+-  __u8  WordWriteTimeoutTyp;
+-  __u8  BufWriteTimeoutTyp;
+-  __u8  BlockEraseTimeoutTyp;
+-  __u8  ChipEraseTimeoutTyp;
+-  __u8  WordWriteTimeoutMax;
+-  __u8  BufWriteTimeoutMax;
+-  __u8  BlockEraseTimeoutMax;
+-  __u8  ChipEraseTimeoutMax;
+-  __u8  DevSize;
+-  __u16 InterfaceDesc;
+-  __u16 MaxBufWriteSize;
+-  __u8  NumEraseRegions;
+-  __u32 EraseRegionInfo[0]; /* Not host ordered */
++      uint8_t  qry[3];
++      uint16_t P_ID;
++      uint16_t P_ADR;
++      uint16_t A_ID;
++      uint16_t A_ADR;
++      uint8_t  VccMin;
++      uint8_t  VccMax;
++      uint8_t  VppMin;
++      uint8_t  VppMax;
++      uint8_t  WordWriteTimeoutTyp;
++      uint8_t  BufWriteTimeoutTyp;
++      uint8_t  BlockEraseTimeoutTyp;
++      uint8_t  ChipEraseTimeoutTyp;
++      uint8_t  WordWriteTimeoutMax;
++      uint8_t  BufWriteTimeoutMax;
++      uint8_t  BlockEraseTimeoutMax;
++      uint8_t  ChipEraseTimeoutMax;
++      uint8_t  DevSize;
++      uint16_t InterfaceDesc;
++      uint16_t MaxBufWriteSize;
++      uint8_t  NumEraseRegions;
++      uint32_t EraseRegionInfo[0]; /* Not host ordered */
+ } __attribute__((packed));
+ /* Extended Query Structure for both PRI and ALT */
+ struct cfi_extquery {
+-  __u8  pri[3];
+-  __u8  MajorVersion;
+-  __u8  MinorVersion;
++      uint8_t  pri[3];
++      uint8_t  MajorVersion;
++      uint8_t  MinorVersion;
+ } __attribute__((packed));
+ /* Vendor-Specific PRI for Intel/Sharp Extended Command Set (0x0001) */
+ struct cfi_pri_intelext {
+-  __u8  pri[3];
+-  __u8  MajorVersion;
+-  __u8  MinorVersion;
+-  __u32 FeatureSupport;
+-  __u8  SuspendCmdSupport;
+-  __u16 BlkStatusRegMask;
+-  __u8  VccOptimal;
+-  __u8  VppOptimal;
+-  __u8  NumProtectionFields;
+-  __u16 ProtRegAddr;
+-  __u8  FactProtRegSize;
+-  __u8  UserProtRegSize;
++      uint8_t  pri[3];
++      uint8_t  MajorVersion;
++      uint8_t  MinorVersion;
++      uint32_t FeatureSupport; /* if bit 31 is set then an additional uint32_t feature
++                                  block follows - FIXME - not currently supported */
++      uint8_t  SuspendCmdSupport;
++      uint16_t BlkStatusRegMask;
++      uint8_t  VccOptimal;
++      uint8_t  VppOptimal;
++      uint8_t  NumProtectionFields;
++      uint16_t ProtRegAddr;
++      uint8_t  FactProtRegSize;
++      uint8_t  UserProtRegSize;
++      uint8_t  extra[0];
++} __attribute__((packed));
 +
-+/*
-+      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;
-+
-+      if (!jffs2_is_writebuffered(c))
-+              return c->mtd->read(c->mtd, ofs, len, retlen, buf);
-+
-+      /* Read flash */
-+      if (jffs2_cleanmarker_oob(c))
-+              ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo);
-+      else
-+              ret = c->mtd->read(c->mtd, ofs, len, retlen, buf);
-+
-+      if ( (ret == -EBADMSG) && (*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;
-+      }       
-+
-+      /* 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 (SECTOR_ADDR(ofs) != SECTOR_ADDR(c->wbuf_ofs))
-+              return ret;
-+
-+      /* Lock only if we have reason to believe wbuf contains relevant data,
-+         so that checking an erased block during wbuf recovery space allocation
-+         does not deadlock. */
-+      down_read(&c->wbuf_sem);
++struct cfi_intelext_otpinfo {
++      uint32_t ProtRegAddr;
++      uint16_t FactGroups;
++      uint8_t  FactProtRegSize;
++      uint16_t UserGroups;
++      uint8_t  UserProtRegSize;
++} __attribute__((packed));
 +
-+      if (ofs >= c->wbuf_ofs) {
-+              owbf = (ofs - c->wbuf_ofs);     /* offset in write buffer */
-+              if (owbf > c->wbuf_len)         /* is read beyond write buffer ? */
-+                      goto exit;
-+              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 ? */
-+                      goto exit;
-+              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);
++struct cfi_intelext_blockinfo {
++      uint16_t NumIdentBlocks;
++      uint16_t BlockSize;
++      uint16_t MinBlockEraseCycles;
++      uint8_t  BitsPerCell;
++      uint8_t  BlockCap;
++} __attribute__((packed));
 +
-+exit:
-+      up_read(&c->wbuf_sem);
-+      return ret;
-+}
++struct cfi_intelext_regioninfo {
++      uint16_t NumIdentPartitions;
++      uint8_t  NumOpAllowed;
++      uint8_t  NumOpAllowedSimProgMode;
++      uint8_t  NumOpAllowedSimEraMode;
++      uint8_t  NumBlockTypes;
++      struct cfi_intelext_blockinfo BlockTypes[1];
++} __attribute__((packed));
 +
-+/*
-+ *    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;
++/* Vendor-Specific PRI for AMD/Fujitsu Extended Command Set (0x0002) */
 +
-+      /* allocate a buffer for all oob data in this sector */
-+      oob_size = c->mtd->oobsize;
-+      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;
++struct cfi_pri_amdstd {
++      uint8_t  pri[3];
++      uint8_t  MajorVersion;
++      uint8_t  MinorVersion;
++      uint8_t  SiliconRevision; /* bits 1-0: Address Sensitive Unlock */
++      uint8_t  EraseSuspend;
++      uint8_t  BlkProt;
++      uint8_t  TmpBlkUnprotect;
++      uint8_t  BlkProtUnprot;
++      uint8_t  SimultaneousOps;
++      uint8_t  BurstMode;
++      uint8_t  PageMode;
++      uint8_t  VppMin;
++      uint8_t  VppMax;
++      uint8_t  TopBottom;
+ } __attribute__((packed));
+ struct cfi_pri_query {
+-  __u8  NumFields;
+-  __u32 ProtField[1]; /* Not host ordered */
++      uint8_t  NumFields;
++      uint32_t ProtField[1]; /* Not host ordered */
+ } __attribute__((packed));
+ struct cfi_bri_query {
+-  __u8  PageModeReadCap;
+-  __u8  NumFields;
+-  __u32 ConfField[1]; /* Not host ordered */
++      uint8_t  PageModeReadCap;
++      uint8_t  NumFields;
++      uint32_t ConfField[1]; /* Not host ordered */
+ } __attribute__((packed));
+-#define P_ID_NONE 0
+-#define P_ID_INTEL_EXT 1
+-#define P_ID_AMD_STD 2
+-#define P_ID_INTEL_STD 3
+-#define P_ID_AMD_EXT 4
+-#define P_ID_MITSUBISHI_STD 256
+-#define P_ID_MITSUBISHI_EXT 257
+-#define P_ID_RESERVED 65535
++#define P_ID_NONE               0x0000
++#define P_ID_INTEL_EXT          0x0001
++#define P_ID_AMD_STD            0x0002
++#define P_ID_INTEL_STD          0x0003
++#define P_ID_AMD_EXT            0x0004
++#define P_ID_WINBOND            0x0006
++#define P_ID_ST_ADV             0x0020
++#define P_ID_MITSUBISHI_STD     0x0100
++#define P_ID_MITSUBISHI_EXT     0x0101
++#define P_ID_SST_PAGE           0x0102
++#define P_ID_INTEL_PERFORMANCE  0x0200
++#define P_ID_INTEL_DATA         0x0210
++#define P_ID_RESERVED           0xffff
+ #define CFI_MODE_CFI  1
+ #define CFI_MODE_JEDEC        0
+ struct cfi_private {
+-      __u16 cmdset;
++      uint16_t cmdset;
+       void *cmdset_priv;
+       int interleave;
+       int device_type;
+       int cfi_mode;           /* Are we a JEDEC device pretending to be CFI? */
+       int addr_unlock1;
+       int addr_unlock2;
+-      int fast_prog;
+       struct mtd_info *(*cmdset_setup)(struct map_info *);
+       struct cfi_ident *cfiq; /* For now only one. We insist that all devs
+                                 must be of the same type. */
+@@ -314,107 +239,81 @@
+       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.
+  */
+-static inline __u32 cfi_build_cmd_addr(__u32 cmd_ofs, int interleave, int type)
++static inline uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs, int interleave, int type)
+ {
+       return (cmd_ofs * type) * interleave;
+ }
+ /*
+- * Transforms the CFI command for the given geometry (bus width & interleave.
++ * Transforms the CFI command for the given geometry (bus width & interleave).
++ * It looks too long to be inline, but in the common case it should almost all
++ * get optimised away. 
+  */
+-static inline cfi_word cfi_build_cmd(u_char cmd, struct map_info *map, struct cfi_private *cfi)
++static inline map_word cfi_build_cmd(u_long cmd, struct map_info *map, struct cfi_private *cfi)
+ {
+-      cfi_word val = 0;
++      map_word val = { {0} };
++      int wordwidth, words_per_bus, chip_mode, chips_per_word;
++      unsigned long onecmd;
++      int i;
+-      if (cfi_buswidth_is_1()) {
+-              /* 1 x8 device */
+-              val = cmd;
+-      } else if (cfi_buswidth_is_2()) {
+-              if (cfi_interleave_is_1()) {
+-                      /* 1 x16 device in x16 mode */
+-                      val = cpu_to_cfi16(cmd);
+-              } else if (cfi_interleave_is_2()) {
+-                      /* 2 (x8, x16 or x32) devices in x8 mode */
+-                      val = cpu_to_cfi16((cmd << 8) | cmd);
+-              }
+-      } else if (cfi_buswidth_is_4()) {
+-              if (cfi_interleave_is_1()) {
+-                      /* 1 x32 device in x32 mode */
+-                      val = cpu_to_cfi32(cmd);
+-              } else if (cfi_interleave_is_2()) {
+-                      /* 2 x16 device in x16 mode */
+-                      val = cpu_to_cfi32((cmd << 16) | cmd);
+-              } else if (cfi_interleave_is_4()) {
+-                      /* 4 (x8, x16 or x32) devices in x8 mode */
+-                      val = (cmd << 16) | cmd;
+-                      val = cpu_to_cfi32((val << 8) | val);
+-              }
+-#ifdef CFI_WORD_64
+-      } else if (cfi_buswidth_is_8()) {
+-              if (cfi_interleave_is_1()) {
+-                      /* 1 x64 device in x64 mode */
+-                      val = cpu_to_cfi64(cmd);
+-              } else if (cfi_interleave_is_2()) {
+-                      /* 2 x32 device in x32 mode */
+-                      val = cmd;
+-                      val = cpu_to_cfi64((val << 32) | val);
+-              } else if (cfi_interleave_is_4()) {
+-                      /* 4 (x16, x32 or x64) devices in x16 mode */
+-                      val = (cmd << 16) | cmd;
+-                      val = cpu_to_cfi64((val << 32) | val);
+-              } else if (cfi_interleave_is_8()) {
+-                      /* 8 (x8, x16 or x32) devices in x8 mode */
+-                      val = (cmd << 8) | cmd;
+-                      val = (val << 16) | val;
+-                      val = (val << 32) | val;
+-                      val = cpu_to_cfi64(val);
+-              }
+-#endif /* CFI_WORD_64 */
++      /* We do it this way to give the compiler a fighting chance 
++         of optimising away all the crap for 'bankwidth' larger than
++         an unsigned long, in the common case where that support is
++         disabled */
++      if (map_bankwidth_is_large(map)) {
++              wordwidth = sizeof(unsigned long);
++              words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1
++      } else {
++              wordwidth = map_bankwidth(map);
++              words_per_bus = 1;
+       }
+-      return val;
+-}
+-#define CMD(x)  cfi_build_cmd((x), map, cfi)
+-/*
+- * Read a value according to the bus width.
+- */
++      chip_mode = map_bankwidth(map) / cfi_interleave(cfi);
++      chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map);
+-static inline cfi_word cfi_read(struct map_info *map, __u32 addr)
+-{
+-      if (cfi_buswidth_is_1()) {
+-              return map->read8(map, addr);
+-      } else if (cfi_buswidth_is_2()) {
+-              return map->read16(map, addr);
+-      } else if (cfi_buswidth_is_4()) {
+-              return map->read32(map, addr);
+-      } else if (cfi_buswidth_is_8()) {
+-              return map->read64(map, addr);
+-      } else {
+-              return 0;
++      /* First, determine what the bit-pattern should be for a single
++         device, according to chip mode and endianness... */
++      switch (chip_mode) {
++      default: BUG();
++      case 1:
++              onecmd = cmd;
++              break;
++      case 2:
++              onecmd = cpu_to_cfi16(cmd);
++              break;
++      case 4:
++              onecmd = cpu_to_cfi32(cmd);
++              break;
+       }
+-}
+-/*
+- * Write a value according to the bus width.
+- */
++      /* Now replicate it across the size of an unsigned long, or 
++         just to the bus width as appropriate */
++      switch (chips_per_word) {
++      default: BUG();
++#if BITS_PER_LONG >= 64
++      case 8:
++              onecmd |= (onecmd << (chip_mode * 32));
++#endif
++      case 4:
++              onecmd |= (onecmd << (chip_mode * 16));
++      case 2:
++              onecmd |= (onecmd << (chip_mode * 8));
++      case 1:
++              ;
 +      }
-+      
-+      /* Special check for first page */
-+      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;
+-static inline void cfi_write(struct map_info *map, cfi_word val, __u32 addr)
+-{
+-      if (cfi_buswidth_is_1()) {
+-              map->write8(map, val, addr);
+-      } else if (cfi_buswidth_is_2()) {
+-              map->write16(map, val, addr);
+-      } else if (cfi_buswidth_is_4()) {
+-              map->write32(map, val, addr);
+-      } else if (cfi_buswidth_is_8()) {
+-              map->write64(map, val, addr);
++      /* And finally, for the multi-word case, replicate it 
++         in all words in the structure */
++      for (i=0; i < words_per_bus; i++) {
++              val.x[i] = onecmd;
+       }
 +
-+              if (buf[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;
-+              }
-+      }
++      return val;
+ }
++#define CMD(x)  cfi_build_cmd((x), map, cfi)
+ /*
+  * Sends a CFI command to a bank of flash for the given geometry.
+@@ -423,50 +322,47 @@
+  * If prev_val is non-null, it will be set to the value at the command address,
+  * before the command was written.
+  */
+-static inline __u32 cfi_send_gen_cmd(u_char cmd, __u32 cmd_addr, __u32 base,
++static inline uint32_t cfi_send_gen_cmd(u_char cmd, uint32_t cmd_addr, uint32_t base,
+                               struct map_info *map, struct cfi_private *cfi,
+-                              int type, cfi_word *prev_val)
++                              int type, map_word *prev_val)
+ {
+-      cfi_word val;
+-      __u32 addr = base + cfi_build_cmd_addr(cmd_addr, CFIDEV_INTERLEAVE, type);
++      map_word val;
++      uint32_t addr = base + cfi_build_cmd_addr(cmd_addr, cfi_interleave(cfi), type);
+       val = cfi_build_cmd(cmd, map, cfi);
+       if (prev_val)
+-              *prev_val = cfi_read(map, addr);
++              *prev_val = map_read(map, addr);
+-      cfi_write(map, val, addr);
++      map_write(map, val, addr);
+       return addr - base;
+ }
+-static inline __u8 cfi_read_query(struct map_info *map, __u32 addr)
++static inline uint8_t cfi_read_query(struct map_info *map, uint32_t addr)
+ {
+-      if (cfi_buswidth_is_1()) {
+-              return map->read8(map, addr);
+-      } else if (cfi_buswidth_is_2()) {
+-              return cfi16_to_cpu(map->read16(map, addr));
+-      } else if (cfi_buswidth_is_4()) {
+-              return cfi32_to_cpu(map->read32(map, addr));
+-      } else if (cfi_buswidth_is_8()) {
+-              return cfi64_to_cpu(map->read64(map, addr));
++      map_word val = map_read(map, addr);
 +
-+      /* we know, we are aligned :) */        
-+      for (page = oob_size; page < len; page += sizeof(long)) {
-+              unsigned long dat = *(unsigned long *)(&buf[page]);
-+              if(dat != -1) {
-+                      ret = 1; 
-+                      goto out;
-+              }
++      if (map_bankwidth_is_1(map)) {
++              return val.x[0];
++      } else if (map_bankwidth_is_2(map)) {
++              return cfi16_to_cpu(val.x[0]);
+       } else {
+-              return 0;
++              /* No point in a 64-bit byteswap since that would just be
++                 swapping the responses from different chips, and we are
++                 only interested in one chip (a representative sample) */
++              return cfi32_to_cpu(val.x[0]);
+       }
+ }
+ static inline void cfi_udelay(int us)
+ {
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
+-      unsigned long t = us * HZ / 1000000;
+-      if (t) {
+-              set_current_state(TASK_UNINTERRUPTIBLE);
+-              schedule_timeout(t);
+-              return;
+-      }
+-#endif
++      if (us >= 1000) {
++              msleep((us+999)/1000);
++      } else {
+       udelay(us);
+       cond_resched();
 +      }
+ }
+ static inline void cfi_spin_lock(spinlock_t *mutex)
+@@ -479,5 +375,28 @@
+       spin_unlock_bh(mutex);
+ }
++struct cfi_extquery *cfi_read_pri(struct map_info *map, uint16_t adr, uint16_t size,
++                           const char* name);
++struct cfi_fixup {
++      uint16_t mfr;
++      uint16_t id;
++      void (*fixup)(struct mtd_info *mtd, void* param);
++      void* param;
++};
 +
-+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[2 * NAND_MAX_OOBSIZE];
-+      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++) {
-+              /* Check first if the block is bad. */
-+              if (c->mtd->block_isbad (c->mtd, offset)) {
-+                      D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n", jeb->offset));
-+                      return 2;
-+              }
-+              /*
-+                 *    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 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;
-+}
++#define CFI_MFR_ANY 0xffff
++#define CFI_ID_ANY  0xffff
 +
-+int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
-+{
-+      struct  jffs2_unknown_node n;
-+      int     ret;
-+      size_t  retlen;
++#define CFI_MFR_AMD 0x0001
++#define CFI_MFR_ST  0x0020    /* STMicroelectronics */
 +
-+      n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
-+      n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
-+      n.totlen = cpu_to_je32(8);
++void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup* fixups);
 +
-+      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;
-+}
++typedef int (*varsize_frob_t)(struct map_info *map, struct flchip *chip,
++                            unsigned long adr, int len, void *thunk);
 +
-+/* 
-+ * On NAND we try to mark this block bad. If the block was erased more
-+ * than MAX_ERASE_FAILURES we mark it finaly bad.
-+ * 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 cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
++      loff_t ofs, size_t len, void *thunk);
 +
-+int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset)
+ #endif /* __MTD_CFI_H__ */
+--- linux-2.4.21/include/linux/mtd/compatmac.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/compatmac.h
+@@ -1,583 +1,223 @@
+-
+ /*
+- * 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 */
+-#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__
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10)
++#error "This kernel is too old: not supported by this file"
+ #endif
+-/* 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
++      /* O(1) scheduler stuff. */
+-/* changed the prototype of read/write */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5) && !defined(__rh_config_h__)
++#include <linux/sched.h>
++static inline void __recalc_sigpending(void)
 +{
-+      int     ret;
-+
-+      /* if the count is < max, we try to write the counter to the 2nd page oob area */
-+      if( ++jeb->bad_count < MAX_ERASE_FAILURES)
-+              return 0;
-+
-+      if (!c->mtd->block_markbad)
-+              return 1; // What else can we do?
-+
-+      D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Marking bad block at %08x\n", bad_offset));
-+      ret = c->mtd->block_markbad(c->mtd, bad_offset);
-+      
-+      if (ret) {
-+              D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
-+              return ret;
-+      }
-+      return 1;
++      recalc_sigpending(current);
 +}
-+
-+#define NAND_JFFS2_OOB16_FSDALEN      8
-+
-+static struct nand_oobinfo jffs2_oobinfo_docecc = {
-+      .useecc = MTD_NANDECC_PLACE,
-+      .eccbytes = 6,
-+      .eccpos = {0,1,2,3,4,5}
-+};
-+
-+
-+int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c)
-+{
-+      struct nand_oobinfo *oinfo = &c->mtd->oobinfo;
-+
-+      /* Do this only, if we have an oob buffer */
-+      if (!c->mtd->oobsize)
-+              return 0;
-+      
-+      /* Cleanmarker is out-of-band, so inline size zero */
-+      c->cleanmarker_size = 0;
-+
-+      /* Should we use autoplacement ? */
-+      if (oinfo && oinfo->useecc == MTD_NANDECC_AUTOPLACE) {
-+              D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n"));
-+              /* Get the position of the free bytes */
-+              if (!oinfo->oobfree[0][1]) {
-+                      printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep. Autoplacement selected and no empty space in oob\n");
-+                      return -ENOSPC;
-+              }
-+              c->fsdata_pos = oinfo->oobfree[0][0];
-+              c->fsdata_len = oinfo->oobfree[0][1];
-+              if (c->fsdata_len > 8)
-+                      c->fsdata_len = 8;
-+      } else {
-+              /* This is just a legacy fallback and should go away soon */
-+              switch(c->mtd->ecctype) {
-+              case MTD_ECC_RS_DiskOnChip:
-+                      printk(KERN_WARNING "JFFS2 using DiskOnChip hardware ECC without autoplacement. Fix it!\n");
-+                      c->oobinfo = &jffs2_oobinfo_docecc;
-+                      c->fsdata_pos = 6;
-+                      c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN;
-+                      c->badblock_pos = 15;
-+                      break;
-+      
-+              default:
-+                      D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n"));
-+                      return -EINVAL;
-+              }
-+      }
-+      return 0;
++#undef recalc_sigpending
++#define recalc_sigpending() __recalc_sigpending ()
+-#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
++#define set_user_nice(tsk, n) do { (tsk)->nice = n; } while(0)
+ #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
+-
+-
+-/* 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
++      daemonize();
+-#ifndef THIS_MODULE
+-#ifdef MODULE
+-#define THIS_MODULE (&__this_module)
+-#else
+-#define THIS_MODULE (NULL)
+-#endif
+-#endif
++      spin_lock_irq(&current->sigmask_lock);
++      sigfillset(&current->blocked);
++      recalc_sigpending();
++      spin_unlock_irq(&current->sigmask_lock);
 +}
-+
-+int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
++#undef daemonize
++#define daemonize(fmt, ...) do {                                              \
++      snprintf(current->comm, sizeof(current->comm), fmt ,##__VA_ARGS__);     \
++      __daemonize_modvers();                                                  \
++      } while(0)
+-#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
++static inline int dequeue_signal_lock(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
 +{
-+      int res;
-+
-+      /* Initialise write buffer */
-+      init_rwsem(&c->wbuf_sem);
-+      c->wbuf_pagesize = c->mtd->oobblock;
-+      c->wbuf_ofs = 0xFFFFFFFF;
-+      
-+      c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
-+      if (!c->wbuf)
-+              return -ENOMEM;
-+
-+      res = jffs2_nand_set_oobinfo(c);
-+
-+#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 res;
++      unsigned long flags;
++      unsigned long ret;
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)
+-#define set_current_state(state_value)                        \
+-        do { current->state = (state_value); } while (0)
+-#endif
++      spin_lock_irqsave(&current->sigmask_lock, flags);
++      ret = dequeue_signal(mask, info);
++      spin_unlock_irqrestore(&current->sigmask_lock, flags);
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0) 
+-static inline int invalidate_device(kdev_t dev, int do_sync) {
++      return ret;
 +}
-+
-+void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
+-      if (do_sync)
+-              fsync_dev(dev);
++static inline int allow_signal(int sig)
 +{
-+      kfree(c->wbuf);
-+}
-+
-+int jffs2_dataflash_setup(struct jffs2_sb_info *c) {
-+      c->cleanmarker_size = 0;                /* No cleanmarkers needed */
-+      
-+      /* Initialize write buffer */
-+      init_rwsem(&c->wbuf_sem);
-+      c->wbuf_pagesize = c->sector_size;
-+      c->wbuf_ofs = 0xFFFFFFFF;
-+
-+      c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
-+      if (!c->wbuf)
-+              return -ENOMEM;
-+
-+      printk(KERN_INFO "JFFS2 write-buffering enabled (%i)\n", c->wbuf_pagesize);
-+
-+      return 0;
-+}
-+
-+void jffs2_dataflash_cleanup(struct jffs2_sb_info *c) {
-+      kfree(c->wbuf);
-+}
-+
-+int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c) {
-+      /* Cleanmarker is actually larger on the flashes */
-+      c->cleanmarker_size = 16;
-+
-+      /* Initialize write buffer */
-+      init_rwsem(&c->wbuf_sem);
-+      c->wbuf_pagesize = c->mtd->eccsize;
-+      c->wbuf_ofs = 0xFFFFFFFF;
-+
-+      c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
-+      if (!c->wbuf)
-+              return -ENOMEM;
-+
++      if (sig < 1 || sig > _NSIG)
++              return -EINVAL;
+       
+-      invalidate_buffers(dev);
++        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;
+ }
+-#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);
++static inline int disallow_signal(int sig)
++{
++      if (sig < 1 || sig > _NSIG)
++              return -EINVAL;
+       
+-      if (sb)
+-              res = invalidate_inodes(sb);
++      spin_lock_irq(&current->sigmask_lock);
++      sigaddset(&current->blocked, sig);
++      recalc_sigpending();
+-      invalidate_buffers(dev);
+-      return res;
++      current->sig->action[sig-1].sa.sa_handler = SIG_DFL;
++      spin_unlock_irq(&current->sigmask_lock);
 +      return 0;
-+}
+ }
 +
-+void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c) {
-+      kfree(c->wbuf);
-+}
---- linux-2.4.21/fs/jffs2/write.c~mtd-cvs
-+++ linux-2.4.21/fs/jffs2/write.c
-@@ -1,154 +1,70 @@
- /*
-  * JFFS2 -- Journalling Flash File System, Version 2.
-  *
-- * Copyright (C) 2001 Red Hat, Inc.
-- *
-- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
-- *
-- * The original JFFS, from which the design for JFFS2 was derived,
-- * was designed and implemented by Axis Communications AB.
-- *
-- * 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.
-+ * Copyright (C) 2001-2003 Red Hat, Inc.
-  *
-- * The Original Code is JFFS2 - Journalling Flash File System, version 2
-+ * Created by David Woodhouse <dwmw2@infradead.org>
-  *
-- * 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.
-+ * For licensing information, see the file 'LICENCE' in this directory.
-  *
-- * $Id$
-+ * $Id$
-  *
-  */
++#define PF_FREEZE 0
++#define refrigerator(x) do { ; } while(0)
+ #endif
  
- #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 "crc32.h"
-+#include "compr.h"
+-#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; })
++      /* Module bits */
  
--/* 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));
+-#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; })
++#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
  
-       ic = jffs2_alloc_inode_cache();
-       if (!ic) {
--              return ERR_PTR(-ENOMEM);
--      }
--      memset(ic, 0, sizeof(*ic));
--      
--      inode = new_inode(sb);
--      
--      if (!inode) {
--              jffs2_free_inode_cache(ic);
--              return ERR_PTR(-ENOMEM);
-+              return -ENOMEM;
-       }
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,7)
+-struct completion {
+-      struct semaphore s;
+-};
  
--      /* Alloc jffs2_inode_info when that's split in 2.5 */
-+      memset(ic, 0, sizeof(*ic));
+-#define complete(c) up(&(c)->s)
+-#define wait_for_completion(c) down(&(c)->s)
+-#define init_completion(c) init_MUTEX_LOCKED(&(c)->s);
++      /* Random filesystem stuff, only for JFFS2 really */
  
--      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);
++#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,4,9)
+-/* This came later */
+-#define complete_and_exit(c, r) do { complete(c); do_exit(r); } while(0)
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,12)
++#define PageUptodate(x) Page_Uptodate(x)
+ #endif
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9) || \
+-    (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) && !defined(__rh_config_h__))
 -
--      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;
+-#include <linux/genhd.h>
 -
--      insert_inode_hash(inode);
-+      f->inocache->ino = ++c->highest_ino;
-+      f->inocache->state = INO_STATE_PRESENT;
--      return inode;
+-static inline void add_gendisk(struct gendisk *gp)
+-{
+-      gp->next = gendisk_head;
+-      gendisk_head = gp;
 -}
-+      ri->ino = cpu_to_je32(f->inocache->ino);
--/* 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)
+-
+-static inline void del_gendisk(struct gendisk *gp)
 -{
--      unsigned long i;
--      size_t totlen = 0, thislen;
--      int ret = 0;
-+      D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino));
-+      jffs2_add_ino_cache(c, f->inocache);
+-      struct gendisk *gd, **gdp;
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48)
++#define get_seconds() CURRENT_TIME
++#endif
  
--      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)
+-      for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
+-              if (*gdp == gp) {
+-                      gd = *gdp; *gdp = gd->next;
 -                      break;
--              to += vecs[i].iov_len;
--      }
--      if (retlen)
--              *retlen = totlen;
--      return ret;
+-              }
 -}
-+      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);
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,53)
++#define generic_file_readonly_mmap generic_file_mmap
+ #endif
  
-+      f->highest_version = 1;
-+      ri->version = cpu_to_je32(f->highest_version);
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) && defined(MODULE)
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,70)
  
--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;
+-#define module_init(func)             \
+-mod_init_t init_module(void) {                \
+-      return func();                  \
+-}
++#include <linux/kmod.h>
++#include <linux/string.h>
+-#define module_exit(func)             \
+-mod_exit_t cleanup_module(void) {     \
+-      return func();                  \
++static inline char *strlcpy(char *dest, const char *src, int len)
++{
++      dest[len-1] = 0;
++      return strncpy(dest, src, len-1);
  }
+-#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
  
--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)
+-/* 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)
++static inline int do_old_request_module(const char *mod)
  {
-       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]);
-       }
+-      recalc_sigpending(current);
++      return request_module(mod);
  }
+-#undef recalc_sigpending
+-#define recalc_sigpending() __recalc_sigpending ()
+-#endif
 -
--      
-+#endif
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5)
+-#define parent_ino(d) ((d)->d_parent->d_inode->i_ino)
+-#endif
++#undef request_module
++#define request_module(fmt, ...) \
++ ({ char modname[32]; snprintf(modname, 31, fmt ,##__VA_ARGS__); do_old_request_module(modname); })
+-#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
++#endif /* 2.5.70 */
+-#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
++#ifndef container_of
++#define container_of(ptr, type, member) ({                 \
++      const typeof( ((type *)0)->member ) *__mptr = (ptr); \
++      (type *)( (char *)__mptr - offsetof(type,member) );})
+ #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)
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,7)
++#define kvec iovec
++#define __user 
+ #endif
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21)
+-#define rq_data_dir(x)        ((x)->cmd)
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,28)
++#define msleep(x) \
++      set_current_state(TASK_UNINTERRUPTIBLE); \
++      schedule_timeout((x)*(HZ/1000));
+ #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)) 
+-
++#ifndef __iomem
++#define __iomem
+ #endif
+-/* Removed cos it broke stuff. Where is this required anyway? 
+- * #ifndef QUEUE_EMPTY
+- * #define QUEUE_EMPTY  (!CURRENT)
+- * #endif
++#ifndef list_for_each_entry_safe
++/**
++ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
++ * @pos:      the type * to use as a loop counter.
++ * @n:                another type * to use as temporary storage
++ * @head:     the head for your list.
++ * @member:   the name of the list_struct within the struct.
+  */
+-#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
++#define list_for_each_entry_safe(pos, n, head, member)                        \
++      for (pos = list_entry((head)->next, typeof(*pos), member),      \
++              n = list_entry(pos->member.next, typeof(*pos), member); \
++           &pos->member != (head);                                    \
++           pos = n, n = list_entry(n->member.next, typeof(*n), member))
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,12)
+-#define PageUptodate(x) Page_Uptodate(x)
+ #endif
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48)
+-#define get_seconds() CURRENT_TIME
++#ifndef DEFINE_SPINLOCK
++#define DEFINE_SPINLOCK(x) spinlock_t x = SPIN_LOCK_UNLOCKED
+ #endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,53)
+-#define generic_file_readonly_mmap generic_file_mmap
++#ifndef DEFINE_RWLOCK
++#define DEFINE_RWLOCK(x) rwlock_t x = RW_LOCK_UNLOCKED
+ #endif
+ #endif /* __LINUX_MTD_COMPATMAC_H__ */
+--- linux-2.4.21/include/linux/mtd/doc2000.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/doc2000.h
+@@ -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,22 +46,51 @@
+ #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))
+-#define WriteDOC_(d, adr, reg)  writeb(d, ((unsigned long)adr) + (reg))
++#define ReadDOC_(adr, reg)      readb((void __iomem *)(adr) + (reg))
++#define WriteDOC_(d, adr, reg)  writeb(d, (void __iomem *)(adr) + (reg))
+ #define DOC_IOREMAP_LEN 0x2000
+ #endif
+@@ -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,18 +156,21 @@
+ #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
+ struct DiskOnChip {
+       unsigned long physadr;
+-      unsigned long virtadr;
++      void __iomem *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;
+--- linux-2.4.21/include/linux/mtd/flashchip.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/flashchip.h
+@@ -6,7 +6,7 @@
+  *
+  * (C) 2000 Red Hat. GPLd.
+  *
+- * $Id$
++ * $Id$
+  *
+  */
  
- /* 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 */
+@@ -29,6 +29,7 @@
+       FL_ERASE_SUSPENDED,
+       FL_WRITING,
+       FL_WRITING_TO_BUFFER,
++      FL_OTP_WRITE,
+       FL_WRITE_SUSPENDING,
+       FL_WRITE_SUSPENDED,
+       FL_PM_SUSPENDED,
+@@ -37,13 +38,16 @@
+       FL_LOCKING,
+       FL_UNLOCKING,
+       FL_POINT,
++      FL_XIP_WHILE_ERASING,
++      FL_XIP_WHILE_WRITING,
+       FL_UNKNOWN
+ } flstate_t;
+ /* NOTE: confusingly, this can be used to refer to more than one chip at a time, 
+-   if they're interleaved. */
++   if they're interleaved.  This can even refer to individual partitions on
++   the same physical chip when present. */
+ struct flchip {
+       unsigned long start; /* Offset within the map */
+@@ -58,6 +62,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
+@@ -65,8 +74,17 @@
+       int word_write_time;
+       int buffer_write_time;
+       int erase_time;
++
++      void *priv;
+ };
  
--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)
++/* This is used to handle contention on write/erase operations
++   between partitions of the same physical chip. */
++struct flchip_shared {
++      spinlock_t lock;
++      struct flchip *writing;
++      struct flchip *erasing;
++};
  
- {
--      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;
--      struct iovec vecs[2];
-+      size_t retlen;
-+      struct kvec 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;
+ #endif /* __MTD_FLASHCHIP_H__ */
+--- linux-2.4.21/include/linux/mtd/gen_probe.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/gen_probe.h
+@@ -1,7 +1,7 @@
+ /*
+  * (C) 2001, 2001 Red Hat, Inc.
+  * GPL'd
+- * $Id$
++ * $Id$
+  */
  
--      writecheck(c->mtd, flash_ofs);
-+      D1(writecheck(c, flash_ofs));
+ #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 <linux/bitops.h>
  
--      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,37 @@
-               jffs2_free_raw_node_ref(raw);
-               return ERR_PTR(-ENOMEM);
-       }
--      raw->flash_offset = flash_ofs;
--      raw->totlen = PAD(ri->totlen);
--      raw->next_phys = NULL;
+ 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);
+ };
  
--      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;
+ struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp);
+--- /dev/null
++++ linux-2.4.21/include/linux/mtd/inftl.h
+@@ -0,0 +1,57 @@
++/*
++ *    inftl.h -- defines to support the Inverse NAND Flash Translation Layer
++ *
++ *    (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
++ *
++ *    $Id$
++ */
 +
-+      /* 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;
++#ifndef __MTD_INFTL_H__
++#define __MTD_INFTL_H__
 +
-+      if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(ri->version) < f->highest_version)) {
-+              BUG_ON(!retried);
-+              D1(printk(KERN_DEBUG "jffs2_write_dnode : dnode_version %d, "
-+                              "highest version %d -> updating dnode\n", 
-+                              je32_to_cpu(ri->version), f->highest_version));
-+              ri->version = cpu_to_je32(++f->highest_version);
-+              ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
-+      }
++#ifndef __KERNEL__
++#error This is a kernel header. Perhaps include nftl-user.h instead?
++#endif
 +
-+      ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen,
-+                               (alloc_mode==ALLOC_GC)?0:f->inocache->ino);
++#include <linux/mtd/blktrans.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nftl.h>
 +
-       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);
++#include <mtd/inftl-user.h>
 +
-               /* Mark the space as dirtied */
-               if (retlen) {
-                       /* Doesn't belong to any inode */
-@@ -229,48 +162,98 @@
-                          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];
++#ifndef INFTL_MAJOR
++#define INFTL_MAJOR 94
++#endif
++#define INFTL_PARTN_BITS 4
 +
-+                      retried = 1;
++#ifdef __KERNEL__
 +
-+                      D1(printk(KERN_DEBUG "Retrying failed write.\n"));
-+                      
-+                      ACCT_SANITY_CHECK(c,jeb);
-+                      D1(ACCT_PARANOIA_CHECK(jeb));
++struct INFTLrecord {
++      struct mtd_blktrans_dev mbd;
++      __u16 MediaUnit;
++      __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;
++        struct nand_oobinfo oobinfo;
++};
 +
-+                      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));
++int INFTL_mount(struct INFTLrecord *s);
++int INFTL_formatblock(struct INFTLrecord *s, int block);
 +
-+                              ACCT_SANITY_CHECK(c,jeb);
-+                              D1(ACCT_PARANOIA_CHECK(jeb));
++#endif /* __KERNEL__ */
 +
-+                              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);
++#endif /* __MTD_INFTL_H__ */
+--- linux-2.4.21/include/linux/mtd/jedec.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/jedec.h
+@@ -7,14 +7,13 @@
+  *
+  * See the AMD flash databook for information on how to operate the interface.
+  *
+- * $Id$
++ * $Id$
+  */
  
-       /* Link into per-inode list */
-+      spin_lock(&c->erase_completion_lock);
-       raw->next_in_ino = f->inocache->nodes;
-       f->inocache->nodes = raw;
-+      spin_unlock(&c->erase_completion_lock);
+ #ifndef __LINUX_MTD_JEDEC_H__
+ #define __LINUX_MTD_JEDEC_H__
  
--      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);
-+      }
+ #include <linux/types.h>
+-#include <linux/mtd/map.h>
  
--      f->inocache->nodes = raw;
-       return fn;
- }
+ #define MAX_JEDEC_CHIPS 16
  
--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;
--      struct iovec vecs[2];
-+      size_t retlen;
-+      struct kvec vecs[2];
-+      int retried = 0;
-       int ret;
+--- linux-2.4.21/include/linux/mtd/map.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/map.h
+@@ -1,23 +1,176 @@
  
--      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));
+ /* Overhauled routines for dealing with different mmap regions of flash */
+-/* $Id$ */
++/* $Id$ */
  
--      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 +274,457 @@
-               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;
+ #ifndef __LINUX_MTD_MAP_H__
+ #define __LINUX_MTD_MAP_H__
  
--      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;
+ #include <linux/config.h>
+ #include <linux/types.h>
+-#include <linux/mtd/mtd.h>
+-#include <linux/slab.h>
++#include <linux/list.h>
++#include <linux/mtd/compatmac.h>
++#include <asm/unaligned.h>
++#include <asm/system.h>
++#include <asm/io.h>
++#include <asm/bug.h>
 +
-+ 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;
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_1
++#define map_bankwidth(map) 1
++#define map_bankwidth_is_1(map) (map_bankwidth(map) == 1)
++#define map_bankwidth_is_large(map) (0)
++#define map_words(map) (1)
++#define MAX_MAP_BANKWIDTH 1
++#else
++#define map_bankwidth_is_1(map) (0)
++#endif
 +
-+      if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(rd->version) < f->highest_version)) {
-+              BUG_ON(!retried);
-+              D1(printk(KERN_DEBUG "jffs2_write_dirent : dirent_version %d, "
-+                                   "highest version %d -> updating dirent\n",
-+                                   je32_to_cpu(rd->version), f->highest_version));
-+              rd->version = cpu_to_je32(++f->highest_version);
-+              fd->version = je32_to_cpu(rd->version);
-+              rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_2
++# ifdef map_bankwidth
++#  undef map_bankwidth
++#  define map_bankwidth(map) ((map)->bankwidth)
++# else
++#  define map_bankwidth(map) 2
++#  define map_bankwidth_is_large(map) (0)
++#  define map_words(map) (1)
++# endif
++#define map_bankwidth_is_2(map) (map_bankwidth(map) == 2)
++#undef MAX_MAP_BANKWIDTH
++#define MAX_MAP_BANKWIDTH 2
++#else
++#define map_bankwidth_is_2(map) (0)
++#endif
++
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_4
++# ifdef map_bankwidth
++#  undef map_bankwidth
++#  define map_bankwidth(map) ((map)->bankwidth)
++# else
++#  define map_bankwidth(map) 4
++#  define map_bankwidth_is_large(map) (0)
++#  define map_words(map) (1)
++# endif
++#define map_bankwidth_is_4(map) (map_bankwidth(map) == 4)
++#undef MAX_MAP_BANKWIDTH
++#define MAX_MAP_BANKWIDTH 4
++#else
++#define map_bankwidth_is_4(map) (0)
++#endif
++
++/* ensure we never evaluate anything shorted than an unsigned long
++ * to zero, and ensure we'll never miss the end of an comparison (bjd) */
++
++#define map_calc_words(map) ((map_bankwidth(map) + (sizeof(unsigned long)-1))/ sizeof(unsigned long))
++
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_8
++# ifdef map_bankwidth
++#  undef map_bankwidth
++#  define map_bankwidth(map) ((map)->bankwidth)
++#  if BITS_PER_LONG < 64
++#   undef map_bankwidth_is_large
++#   define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
++#   undef map_words
++#   define map_words(map) map_calc_words(map)
++#  endif
++# else
++#  define map_bankwidth(map) 8
++#  define map_bankwidth_is_large(map) (BITS_PER_LONG < 64)
++#  define map_words(map) map_calc_words(map)
++# endif
++#define map_bankwidth_is_8(map) (map_bankwidth(map) == 8)
++#undef MAX_MAP_BANKWIDTH
++#define MAX_MAP_BANKWIDTH 8
++#else
++#define map_bankwidth_is_8(map) (0)
++#endif
++
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_16
++# ifdef map_bankwidth
++#  undef map_bankwidth
++#  define map_bankwidth(map) ((map)->bankwidth)
++#  undef map_bankwidth_is_large
++#  define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
++#  undef map_words
++#  define map_words(map) map_calc_words(map)
++# else
++#  define map_bankwidth(map) 16
++#  define map_bankwidth_is_large(map) (1)
++#  define map_words(map) map_calc_words(map)
++# endif
++#define map_bankwidth_is_16(map) (map_bankwidth(map) == 16)
++#undef MAX_MAP_BANKWIDTH
++#define MAX_MAP_BANKWIDTH 16
++#else
++#define map_bankwidth_is_16(map) (0)
++#endif
++
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_32
++# ifdef map_bankwidth
++#  undef map_bankwidth
++#  define map_bankwidth(map) ((map)->bankwidth)
++#  undef map_bankwidth_is_large
++#  define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
++#  undef map_words
++#  define map_words(map) map_calc_words(map)
++# else
++#  define map_bankwidth(map) 32
++#  define map_bankwidth_is_large(map) (1)
++#  define map_words(map) map_calc_words(map)
++# endif
++#define map_bankwidth_is_32(map) (map_bankwidth(map) == 32)
++#undef MAX_MAP_BANKWIDTH
++#define MAX_MAP_BANKWIDTH 32
++#else
++#define map_bankwidth_is_32(map) (0)
++#endif
++
++#ifndef map_bankwidth
++#error "No bus width supported. What's the point?"
++#endif
++
++static inline int map_bankwidth_supported(int w)
++{
++      switch (w) {
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_1
++      case 1:
++#endif
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_2
++      case 2:
++#endif
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_4
++      case 4:
++#endif
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_8
++      case 8:
++#endif
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_16
++      case 16:
++#endif
++#ifdef CONFIG_MTD_MAP_BANK_WIDTH_32
++      case 32:
++#endif
++              return 1;
++
++      default:
++              return 0;
 +      }
++}
++
++#define MAX_MAP_LONGS ( ((MAX_MAP_BANKWIDTH*8) + BITS_PER_LONG - 1) / BITS_PER_LONG )
++
++typedef union {
++      unsigned long x[MAX_MAP_LONGS];
++} map_word;
+ /* 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
+    paging etc. correctly if your device needs it. Then you pass it off
+-   to a chip driver which deals with a mapped device - generally either
+-   do_cfi_probe() or do_ram_probe(), either of which will return a 
+-   struct mtd_info if they liked what they saw. At which point, you
+-   fill in the mtd->module with your own module address, and register 
+-   it.
++   to a chip probe routine -- either JEDEC or CFI probe or both -- via
++   do_map_probe(). If a chip is recognised, the probe code will invoke the
++   appropriate chip driver (if present) and return a struct mtd_info.
++   At which point, you fill in the mtd->module with your own module 
++   address, and register it with the MTD core code. Or you could partition
++   it and register the partitions instead, or keep it for your own private
++   use; whatever.
+    
+    The mtd->priv field will point to the struct map_info, and any further
+    private data required by the chip driver is linked from the 
+@@ -29,39 +182,45 @@
+ struct map_info {
+       char *name;
+       unsigned long size;
+-      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);  
+-      /* If it returned a 'long' I'd call it readl.
+-       * It doesn't.
+-       * I won't.
+-       * dwmw2 */
++      unsigned long phys;
++#define NO_XIP (-1UL)
+       
++      void __iomem *virt;
++      void *cached;
++
++      int bankwidth; /* in octets. This isn't necessarily the width
++                     of actual bus cycles -- it's the repeat interval
++                    in bytes, before you are talking to the first chip again.
++                    */
 +
-+      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];
++#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
++      map_word (*read)(struct map_info *, unsigned long);
+       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);
 +
-+                      retried = 1;
++      void (*write)(struct map_info *, const map_word, unsigned long);
+       void (*copy_to)(struct map_info *, unsigned long, const void *, ssize_t);
  
-+                      D1(printk(KERN_DEBUG "Retrying failed write.\n"));
-+
-+                      ACCT_SANITY_CHECK(c,jeb);
-+                      D1(ACCT_PARANOIA_CHECK(jeb));
+-      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
++      /* It's possible for the map driver to use cached memory in its
++         copy_from implementation (and _only_ with copy_from).  However,
++         when the chip driver knows some flash area has changed contents,
++         it will signal it to the map driver through this routine to let
++         the map driver invalidate the corresponding cache as needed.
++         If there is no cache to care about this can be set to NULL. */
++      void (*inval_cache)(struct map_info *, unsigned long, ssize_t);
++      /* 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 */
 +
-+                      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);
-+                      }
+       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 +233,193 @@
+ 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);
 +
-+                      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);
++#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)
  
-+      spin_lock(&c->erase_completion_lock);
-+      raw->next_in_ino = f->inocache->nodes;
-       f->inocache->nodes = raw;
-+      spin_unlock(&c->erase_completion_lock);
++#define INVALIDATE_CACHED_RANGE(map, from, size) \
++      do { if(map->inval_cache) map->inval_cache(map, from, size); } while(0)
+-/*
+- * 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)
 +
-+      if (retried) {
-+              ACCT_SANITY_CHECK(c,NULL);
++static inline int map_word_equal(struct map_info *map, map_word val1, map_word val2)
+ {
+-      struct map_info *map = mtd->priv;
++      int i;
++      for (i=0; i<map_words(map); i++) {
++              if (val1.x[i] != val2.x[i])
++                      return 0;
 +      }
++      return 1;
++}
+-      if (map->fldrv->destroy)
+-              map->fldrv->destroy(mtd);
+-#ifdef CONFIG_MODULES
+-      if (map->fldrv->module)
+-              __MOD_DEC_USE_COUNT(map->fldrv->module);
++static inline map_word map_word_and(struct map_info *map, map_word val1, map_word val2)
++{
++      map_word r;
++      int i;
 +
-       return fd;
- }
++      for (i=0; i<map_words(map); i++) {
++              r.x[i] = val1.x[i] & val2.x[i];
++      }
++      return r;
++}
 +
-+/* 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)
++static inline map_word map_word_clr(struct map_info *map, map_word val1, map_word val2)
 +{
-+      int ret = 0;
-+      uint32_t writtenlen = 0;
++      map_word r;
++      int i;
 +
-+              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;
-+              uint16_t comprtype = JFFS2_COMPR_NONE;
-+              uint32_t phys_ofs, alloclen;
-+              uint32_t datalen, cdatalen;
-+              int retried = 0;
++      for (i=0; i<map_words(map); i++) {
++              r.x[i] = val1.x[i] & ~val2.x[i];
++      }
++      return r;
++}
 +
-+      retry:
-+              D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset));
++static inline map_word map_word_or(struct map_info *map, map_word val1, map_word val2)
++{
++      map_word r;
++      int i;
 +
-+              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);
++      for (i=0; i<map_words(map); i++) {
++              r.x[i] = val1.x[i] | val2.x[i];
++      }
++      return r;
++}
 +
-+              comprtype = jffs2_compress(c, f, buf, &comprbuf, &datalen, &cdatalen);
++#define map_word_andequal(m, a, b, z) map_word_equal(m, z, map_word_and(m, a, b))
 +
-+              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));
++static inline int map_word_bitsset(struct map_info *map, map_word val1, map_word val2)
++{
++      int i;
 +
-+              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 & 0xff;
-+              ri->usercompr = (comprtype >> 8 ) & 0xff;
-+              ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
-+              ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
++      for (i=0; i<map_words(map); i++) {
++              if (val1.x[i] & val2.x[i])
++                      return 1;
++      }
++      return 0;
++}
 +
-+              fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, ALLOC_NORETRY);
++static inline map_word map_word_load(struct map_info *map, const void *ptr)
++{
++      map_word r;
 +
-+              jffs2_free_comprbuf(comprbuf, buf);
++      if (map_bankwidth_is_1(map))
++              r.x[0] = *(unsigned char *)ptr;
++      else if (map_bankwidth_is_2(map))
++              r.x[0] = get_unaligned((uint16_t *)ptr);
++      else if (map_bankwidth_is_4(map))
++              r.x[0] = get_unaligned((uint32_t *)ptr);
++#if BITS_PER_LONG >= 64
++      else if (map_bankwidth_is_8(map))
++              r.x[0] = get_unaligned((uint64_t *)ptr);
+ #endif
+-      kfree(mtd);
++      else if (map_bankwidth_is_large(map))
++              memcpy(r.x, ptr, map->bankwidth);
 +
-+              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);
++      return r;
+ }
+-#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 map_word map_word_load_partial(struct map_info *map, map_word orig, const unsigned char *buf, int start, int len)
++{
++      int i;
 +
-+                      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;
++      if (map_bankwidth_is_large(map)) {
++              char *dest = (char *)&orig;
++              memcpy(dest+start, buf, len);
++      } else {
++              for (i=start; i < start+len; i++) {
++                      int bitpos;
++#ifdef __LITTLE_ENDIAN
++                      bitpos = i*8;
++#else /* __BIG_ENDIAN */
++                      bitpos = (map_bankwidth(map)-1-i)*8;
++#endif
++                      orig.x[0] &= ~(0xff << bitpos);
++                      orig.x[0] |= buf[i-start] << bitpos;
 +              }
-+              D1(printk(KERN_DEBUG "increasing writtenlen by %d\n", datalen));
-+              writtenlen += datalen;
-+              offset += datalen;
-+              writelen -= datalen;
-+              buf += datalen;
 +      }
-+      *retlen = writtenlen;
-+      return ret;
++      return orig;
 +}
 +
-+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)
++static inline map_word map_word_ff(struct map_info *map)
 +{
-+      struct jffs2_raw_dirent *rd;
-+      struct jffs2_full_dnode *fn;
-+      struct jffs2_full_dirent *fd;
-+      uint32_t alloclen, phys_ofs;
-+      int ret;
++      map_word r;
++      int i;
 +
-+      /* 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;
++      for (i=0; i<map_words(map); i++) {
++              r.x[i] = ~0UL;
 +      }
++      return r;
++}
 +
-+      ri->data_crc = cpu_to_je32(0);
-+      ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
++static inline map_word inline_map_read(struct map_info *map, unsigned long ofs)
++{
++      map_word r;
 +
-+      fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
++      if (map_bankwidth_is_1(map))
++              r.x[0] = __raw_readb(map->virt + ofs);
++      else if (map_bankwidth_is_2(map))
++              r.x[0] = __raw_readw(map->virt + ofs);
++      else if (map_bankwidth_is_4(map))
++              r.x[0] = __raw_readl(map->virt + ofs);
++#if BITS_PER_LONG >= 64
++      else if (map_bankwidth_is_8(map))
++              r.x[0] = __raw_readq(map->virt + ofs);
++#endif
++      else if (map_bankwidth_is_large(map))
++              memcpy_fromio(r.x, map->virt+ofs, map->bankwidth);
 +
-+      D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n",
-+                jemode_to_cpu(ri->mode)));
++      return r;
++}
 +
-+      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;
++static inline void inline_map_write(struct map_info *map, const map_word datum, unsigned long ofs)
++{
++      if (map_bankwidth_is_1(map))
++              __raw_writeb(datum.x[0], map->virt + ofs);
++      else if (map_bankwidth_is_2(map))
++              __raw_writew(datum.x[0], map->virt + ofs);
++      else if (map_bankwidth_is_4(map))
++              __raw_writel(datum.x[0], map->virt + ofs);
++#if BITS_PER_LONG >= 64
++      else if (map_bankwidth_is_8(map))
++              __raw_writeq(datum.x[0], map->virt + ofs);
++#endif
++      else if (map_bankwidth_is_large(map))
++              memcpy_toio(map->virt+ofs, datum.x, map->bankwidth);
++      mb();
++}
 +
-+      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;
-+      }
++static inline void inline_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
++{
++      if (map->cached)
++              memcpy(to, (char *)map->cached + from, len);
++      else
++              memcpy_fromio(to, map->virt + from, len);
++}
 +
-+      rd = jffs2_alloc_raw_dirent();
-+      if (!rd) {
-+              /* Argh. Now we treat it like a normal delete */
-+              jffs2_complete_reservation(c);
-+              return -ENOMEM;
-+      }
++static inline void inline_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
++{
++      memcpy_toio(map->virt + to, from, len);
++}
 +
-+      down(&dir_f->sem);
++#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
++#define map_read(map, ofs) (map)->read(map, ofs)
++#define map_copy_from(map, to, from, len) (map)->copy_from(map, to, from, len)
++#define map_write(map, datum, ofs) (map)->write(map, datum, ofs)
++#define map_copy_to(map, to, from, len) (map)->copy_to(map, to, from, len)
 +
-+      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));
++extern void simple_map_init(struct map_info *);
++#define map_is_linear(map) (map->phys != NO_XIP)
 +
-+      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));
++#else
++#define map_read(map, ofs) inline_map_read(map, ofs)
++#define map_copy_from(map, to, from, len) inline_map_copy_from(map, to, from, len)
++#define map_write(map, datum, ofs) inline_map_write(map, datum, ofs)
++#define map_copy_to(map, to, from, len) inline_map_copy_to(map, to, from, len)
 +
-+      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);
-+      }
++#define simple_map_init(map) BUG_ON(!map_bankwidth_supported((map)->bankwidth))
++#define map_is_linear(map) ({ (void)(map); 1; })
++
++#endif /* !CONFIG_MTD_COMPLEX_MAPPINGS */
+ #endif /* __LINUX_MTD_MAP_H__ */
+--- linux-2.4.21/include/linux/mtd/mtd.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/mtd.h
+@@ -1,123 +1,45 @@
+-
+-/* $Id$ */
++/* 
++ * $Id$
++ *
++ * Copyright (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> et al.
++ *
++ * Released under GPL
++ */
+ #ifndef __MTD_MTD_H__
+ #define __MTD_MTD_H__
+-#ifdef __KERNEL__
++#ifndef __KERNEL__
++#error This is a kernel header. Perhaps include mtd-user.h instead?
++#endif
+ #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>
+-#endif /* __KERNEL__ */
+-
+-struct erase_info_user {
+-      u_int32_t start;
+-      u_int32_t length;
+-};
+-
+-struct mtd_oob_buf {
+-      u_int32_t start;
+-      u_int32_t length;
+-      unsigned char *ptr;
+-};
+-
++#include <linux/mtd/compatmac.h>
++#include <mtd/mtd-abi.h>
+ #define MTD_CHAR_MAJOR 90
+ #define MTD_BLOCK_MAJOR 31
+ #define MAX_MTD_DEVICES 16
+-
+-
+-#define MTD_ABSENT            0
+-#define MTD_RAM                       1
+-#define MTD_ROM                       2
+-#define MTD_NORFLASH          3
+-#define MTD_NANDFLASH         4
+-#define MTD_PEROM             5
+-#define MTD_OTHER             14
+-#define MTD_UNKNOWN           15
+-
+-
+-
+-#define MTD_CLEAR_BITS                1       // Bits can be cleared (flash)
+-#define MTD_SET_BITS          2       // Bits can be set
+-#define MTD_ERASEABLE         4       // Has an erase function
+-#define MTD_WRITEB_WRITEABLE  8       // Direct IO is possible
+-#define MTD_VOLATILE          16      // Set for RAMs
+-#define MTD_XIP                       32      // eXecute-In-Place possible
+-#define MTD_OOB                       64      // Out-of-band data (NAND flash)
+-#define MTD_ECC                       128     // Device capable of automatic ECC
+-
+-// Some common devices / combinations of capabilities
+-#define MTD_CAP_ROM           0
+-#define MTD_CAP_RAM           (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE)
+-#define MTD_CAP_NORFLASH        (MTD_CLEAR_BITS|MTD_ERASEABLE)
+-#define MTD_CAP_NANDFLASH       (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB)
+-#define MTD_WRITEABLE         (MTD_CLEAR_BITS|MTD_SET_BITS)
+-
+-
+-// Types of automatic ECC/Checksum available
+-#define MTD_ECC_NONE          0       // No automatic ECC available
+-#define MTD_ECC_RS_DiskOnChip 1       // Automatic ECC on DiskOnChip
+-#define MTD_ECC_SW            2       // SW ECC for Toshiba & Samsung devices
+-
+-struct mtd_info_user {
+-      u_char type;
+-      u_int32_t flags;
+-      u_int32_t size;  // Total size of the MTD
+-      u_int32_t erasesize;
+-      u_int32_t oobblock;  // Size of OOB blocks (e.g. 512)
+-      u_int32_t oobsize;   // Amount of OOB data per block (e.g. 16)
+-      u_int32_t ecctype;
+-      u_int32_t eccsize;
+-};
+-
+-struct region_info_user {
+-      u_int32_t offset;               /* At which this region starts, 
+-                                       * from the beginning of the MTD */
+-      u_int32_t erasesize;            /* For this region */
+-      u_int32_t numblocks;            /* Number of blocks in this region */
+-      u_int32_t regionindex;
+-};
+-
+-#define MEMGETINFO              _IOR('M', 1, struct mtd_info_user)
+-#define MEMERASE                _IOW('M', 2, struct erase_info_user)
+-#define MEMWRITEOOB             _IOWR('M', 3, struct mtd_oob_buf)
+-#define MEMREADOOB              _IOWR('M', 4, struct mtd_oob_buf)
+-#define MEMLOCK                 _IOW('M', 5, struct erase_info_user)
+-#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)
+-
+-#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;
+-
+-      /* User-space ioctl definitions */
+-
+-
+-#else /* __KERNEL__ */
+-
+-
+ #define MTD_ERASE_PENDING             0x01
+ #define MTD_ERASING           0x02
+ #define MTD_ERASE_SUSPEND     0x04
+ #define MTD_ERASE_DONE          0x08
+ #define MTD_ERASE_FAILED        0x10
++/* If the erase fails, fail_addr might indicate exactly which block failed.  If
++   fail_addr = 0xffffffff, the failure was not at the device level or was not
++   specific to any particular block. */
+ struct erase_info {
+       struct mtd_info *mtd;
+       u_int32_t addr;
+       u_int32_t len;
++      u_int32_t fail_addr;
+       u_long time;
+       u_long retries;
+       u_int dev;
+@@ -147,13 +69,18 @@
+       u_int32_t oobblock;  // Size of OOB blocks (e.g. 512)
+       u_int32_t oobsize;   // Amount of OOB data per block (e.g. 16)
++      u_int32_t oobavail;  // Number of bytes in OOB area available for fs 
+       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;
 +
-+      /* Link the fd into the inode's list, obsoleting an old
-+         one if necessary. */
-+      jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+       /* Data for variable erase regions. If numeraseregions is zero,
+        * it means that the whole device has erasesize as given above. 
+        */
+@@ -163,7 +90,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 +102,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);
+@@ -187,24 +113,24 @@
+        * flash devices. The user data is one time programmable but the
+        * factory data is read only. 
+        */
+-      int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+-
++      int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
+       int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+-
+-      /* This function is not yet implemented */
++      int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
++      int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+       int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
++      int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
+-      /* iovec-based read/write methods. We need these especially for NAND flash,
++      /* kvec-based read/write methods. We need these especially for NAND flash,
+          with its limited number of write cycles per erase.
+          NB: The 'count' parameter is the number of _vectors_, each of 
+          which contains an (ofs, len) tuple.
+       */
+-      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);
+-      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);
++      int (*readv) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, size_t *retlen);
++      int (*readv_ecc) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, 
++              size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
++      int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
++      int (*writev_ecc) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, 
++              size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
+       /* Sync */
+       void (*sync) (struct mtd_info *mtd);
+@@ -217,7 +143,14 @@
+       int (*suspend) (struct mtd_info *mtd);
+       void (*resume) (struct mtd_info *mtd);
++      /* Bad block management functions */
++      int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
++      int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
 +
-+      jffs2_complete_reservation(c);
-+      up(&dir_f->sem);
+       void *priv;
 +
-+      return 0;
-+}
++      struct module *owner;
++      int usecount;
+ };
+@@ -226,44 +159,27 @@
+ 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;
+-      
+-      ret = __get_mtd_device(mtd, num);
+-
+-      if (ret && ret->module && !try_inc_mod_count(ret->module))
+-              return NULL;
+-
+-      return ret;
+-}
++extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num);
+-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;
+ };
+ extern void register_mtd_user (struct mtd_notifier *new);
+ extern int unregister_mtd_user (struct mtd_notifier *old);
+-int default_mtd_writev(struct mtd_info *mtd, const struct iovec *vecs,
++int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
+                      unsigned long count, loff_t to, size_t *retlen);
+-int default_mtd_readv(struct mtd_info *mtd, struct iovec *vecs,
++int default_mtd_readv(struct mtd_info *mtd, struct kvec *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 +192,17 @@
+ #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 */
 +
 +
-+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)
++#ifdef CONFIG_MTD_PARTITIONS
++void mtd_erase_callback(struct erase_info *instr);
++#else
++static inline void mtd_erase_callback(struct erase_info *instr)
 +{
-+      struct jffs2_raw_dirent *rd;
-+      struct jffs2_full_dirent *fd;
-+      uint32_t alloclen, phys_ofs;
-+      int ret;
-+
-+      if (1 /* alternative branch needs testing */ || 
-+          !jffs2_can_mark_obsolete(c)) {
-+              /* We can't mark stuff obsolete on the medium. We need to write a deletion dirent */
-+
-+              rd = jffs2_alloc_raw_dirent();
-+              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);
-+      } else {
-+              struct jffs2_full_dirent **prev = &dir_f->dents;
-+              uint32_t nhash = full_name_hash(name, namelen);
-+
-+              down(&dir_f->sem);
-+
-+              while ((*prev) && (*prev)->nhash <= nhash) {
-+                      if ((*prev)->nhash == nhash && 
-+                          !memcmp((*prev)->name, name, namelen) &&
-+                          !(*prev)->name[namelen]) {
-+                              struct jffs2_full_dirent *this = *prev;
-+
-+                              D1(printk(KERN_DEBUG "Marking old dirent node (ino #%u) @%08x obsolete\n",
-+                                        this->ino, ref_offset(this->raw)));
-+
-+                              *prev = this->next;
-+                              jffs2_mark_node_obsolete(c, (this->raw));
-+                              jffs2_free_full_dirent(this);
-+                              break;
-+                      }
-+                      prev = &((*prev)->next);
-+              }
-+              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) { 
++      if (instr->callback)
++              instr->callback(instr);
++}
++#endif
+ /*
+  * Debugging macro and defines
+@@ -288,13 +214,13 @@
+ #ifdef CONFIG_MTD_DEBUG
+ #define DEBUG(n, args...)                     \
+-      if (n <=  CONFIG_MTD_DEBUG_VERBOSE) {   \
++      do {                                            \
++              if (n <= CONFIG_MTD_DEBUG_VERBOSE)      \
+               printk(KERN_INFO args); \
+-      }
++      } while(0)
+ #else /* CONFIG_MTD_DEBUG */
+-#define DEBUG(n, args...)
+-#endif /* CONFIG_MTD_DEBUG */
++#define DEBUG(n, args...) do { } while(0)
+-#endif /* __KERNEL__ */
++#endif /* CONFIG_MTD_DEBUG */
+ #endif /* __MTD_MTD_H__ */
+--- linux-2.4.21/include/linux/mtd/nand.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/nand.h
+@@ -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
+@@ -44,27 +44,61 @@
+  *                    NAND_YAFFS_OOB
+  *  11-25-2002 tglx   Added Manufacturer code FUJITSU, NATIONAL
+  *                    Split manufacturer and device ID structures 
++ *
++ *  02-08-2004 tglx   added option field to nand structure for chip anomalities
++ *  05-25-2004 tglx   added bad block table support, ST-MICRO manufacturer id
++ *                    update of nand_chip structure description
++ *  01-17-2005 dmarlin        added extended commands for AG-AND device and added option 
++ *                    for BBT_AUTO_REFRESH.
++ *  01-20-2005 dmarlin        added optional pointer to hardware specific callback for 
++ *                    extra error status checks.
+  */
+ #ifndef __LINUX_MTD_NAND_H
+ #define __LINUX_MTD_NAND_H
+ #include <linux/config.h>
+-#include <linux/sched.h>
++#include <linux/wait.h>
++#include <linux/spinlock.h>
++#include <linux/mtd/mtd.h>
+-/*
+- * Searches for a NAND device
++struct mtd_info;
++/* Scan and identify a NAND device */
++extern int nand_scan (struct mtd_info *mtd, int max_chips);
++/* Free resources held by the NAND device */
++extern void nand_release (struct mtd_info *mtd);
 +
-+              down(&dead_f->sem);
++/* Read raw data from the device without ECC */
++extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen);
 +
-+              if (S_ISDIR(OFNI_EDONI_2SFFJ(dead_f)->i_mode)) {
-+                      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);
-+      }
++/* The maximum number of NAND chips in an array */
++#define NAND_MAX_CHIPS                8
 +
-+      jffs2_complete_reservation(c);
++/* This constant declares the max. oobsize / page, which
++ * is supported now. If you add a chip with bigger oobsize/page
++ * adjust this accordingly.
+  */
+-extern int nand_scan (struct mtd_info *mtd);
++#define NAND_MAX_OOBSIZE      64
+ /*
+  * Constants for hardware specific CLE/ALE/NCE function
+ */
++/* Select the chip by setting nCE to low */
+ #define NAND_CTL_SETNCE       1
++/* Deselect the chip by setting nCE to high */
+ #define NAND_CTL_CLRNCE               2
++/* Select the command latch by setting CLE to high */
+ #define NAND_CTL_SETCLE               3
++/* Deselect the command latch by setting CLE to low */
+ #define NAND_CTL_CLRCLE               4
++/* Select the address latch by setting ALE to high */
+ #define NAND_CTL_SETALE               5
++/* Deselect the address latch by setting ALE to low */
+ #define NAND_CTL_CLRALE               6
++/* Set write protection by setting WP to high. Not used! */
++#define NAND_CTL_SETWP                7
++/* Clear write protection by setting WP to low. Not used! */
++#define NAND_CTL_CLRWP                8
+ /*
+  * Standard NAND flash commands
+@@ -75,35 +109,132 @@
+ #define NAND_CMD_READOOB      0x50
+ #define NAND_CMD_ERASE1               0x60
+ #define NAND_CMD_STATUS               0x70
++#define NAND_CMD_STATUS_MULTI 0x71
+ #define NAND_CMD_SEQIN                0x80
+ #define NAND_CMD_READID               0x90
+ #define NAND_CMD_ERASE2               0xd0
+ #define NAND_CMD_RESET                0xff
++/* Extended commands for large page devices */
++#define NAND_CMD_READSTART    0x30
++#define NAND_CMD_CACHEDPROG   0x15
 +
-+      return 0;
-+}
++/* Extended commands for AG-AND device */
++/* 
++ * Note: the command for NAND_CMD_DEPLETE1 is really 0x00 but 
++ *       there is no way to distinguish that from NAND_CMD_READ0
++ *       until the remaining sequence of commands has been completed
++ *       so add a high order bit and mask it off in the command.
++ */
++#define NAND_CMD_DEPLETE1     0x100
++#define NAND_CMD_DEPLETE2     0x38
++#define NAND_CMD_STATUS_MULTI 0x71
++#define NAND_CMD_STATUS_ERROR 0x72
++/* multi-bank error status (banks 0-3) */
++#define NAND_CMD_STATUS_ERROR0        0x73
++#define NAND_CMD_STATUS_ERROR1        0x74
++#define NAND_CMD_STATUS_ERROR2        0x75
++#define NAND_CMD_STATUS_ERROR3        0x76
++#define NAND_CMD_STATUS_RESET 0x7f
++#define NAND_CMD_STATUS_CLEAR 0xff
 +
++/* Status bits */
++#define NAND_STATUS_FAIL      0x01
++#define NAND_STATUS_FAIL_N1   0x02
++#define NAND_STATUS_TRUE_READY        0x20
++#define NAND_STATUS_READY     0x40
++#define NAND_STATUS_WP                0x80
 +
-+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;
+ /* 
+  * Constants for ECC_MODES
+- *
+- * NONE:      No ECC
+- * SOFT:      Software ECC 3 byte ECC per 256 Byte data
+- * HW3_256:   Hardware ECC 3 byte ECC per 256 Byte data
+- * HW3_512:   Hardware ECC 3 byte ECC per 512 Byte data
+- *
+- *
+-*/
++ */
 +
-+      rd = jffs2_alloc_raw_dirent();
-+      if (!rd)
-+              return -ENOMEM;
++/* No ECC. Usage is not recommended ! */
+ #define NAND_ECC_NONE         0
++/* Software ECC 3 byte ECC per 256 Byte data */
+ #define NAND_ECC_SOFT         1
++/* Hardware ECC 3 byte ECC per 256 Byte data */
+ #define NAND_ECC_HW3_256      2
++/* Hardware ECC 3 byte ECC per 512 Byte data */
+ #define NAND_ECC_HW3_512      3
++/* Hardware ECC 3 byte ECC per 512 Byte data */
+ #define NAND_ECC_HW6_512      4
+-#define NAND_ECC_DISKONCHIP   5
++/* Hardware ECC 8 byte ECC per 512 Byte data */
++#define NAND_ECC_HW8_512      6
++/* Hardware ECC 12 byte ECC per 2048 Byte data */
++#define NAND_ECC_HW12_2048    7
+ /*
+  * Constants for Hardware ECC
+-*/
++ */
++/* Reset Hardware ECC for read */
+ #define NAND_ECC_READ         0
++/* Reset Hardware ECC for write */
+ #define NAND_ECC_WRITE                1
++/* Enable Hardware ECC before syndrom is read back from flash */
++#define NAND_ECC_READSYN      2
 +
-+      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);
++/* Bit mask for flags passed to do_nand_read_ecc */
++#define NAND_GET_DEVICE               0x80
 +
-+      /* 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;
++/* Option constants for bizarre disfunctionality and real
++*  features
++*/
++/* Chip can not auto increment pages */
++#define NAND_NO_AUTOINCR      0x00000001
++/* Buswitdh is 16 bit */
++#define NAND_BUSWIDTH_16      0x00000002
++/* Device supports partial programming without padding */
++#define NAND_NO_PADDING               0x00000004
++/* Chip has cache program function */
++#define NAND_CACHEPRG         0x00000008
++/* Chip has copy back function */
++#define NAND_COPYBACK         0x00000010
++/* AND Chip which has 4 banks and a confusing page / block 
++ * assignment. See Renesas datasheet for further information */
++#define NAND_IS_AND           0x00000020
++/* Chip has a array of 4 pages which can be read without
++ * additional ready /busy waits */
++#define NAND_4PAGE_ARRAY      0x00000040 
++/* Chip requires that BBT is periodically rewritten to prevent
++ * bits from adjacent blocks from 'leaking' in altering data.
++ * This happens with the Renesas AG-AND chips, possibly others.  */
++#define BBT_AUTO_REFRESH      0x00000080
 +
-+      rd->type = type;
++/* Options valid for Samsung large page devices */
++#define NAND_SAMSUNG_LP_OPTIONS \
++      (NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK)
 +
-+      rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
-+      rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
++/* Macros to identify the above */
++#define NAND_CANAUTOINCR(chip) (!(chip->options & NAND_NO_AUTOINCR))
++#define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING))
++#define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG))
++#define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK))
 +
-+      fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL);
-+      
-+      jffs2_free_raw_dirent(rd);
++/* Mask to zero out the chip options, which come from the id table */
++#define NAND_CHIPOPTIONS_MSK  (0x0000ffff & ~NAND_NO_AUTOINCR)
 +
-+      if (IS_ERR(fd)) {
-+              jffs2_complete_reservation(c);
-+              up(&dir_f->sem);
-+              return PTR_ERR(fd);
-+      }
++/* Non chip related options */
++/* Use a flash based bad block table. This option is passed to the
++ * default bad block table function. */
++#define NAND_USE_FLASH_BBT    0x00010000
++/* The hw ecc generator provides a syndrome instead a ecc value on read 
++ * This can only work if we have the ecc bytes directly behind the 
++ * data bytes. Applies for DOC and AG-AND Renesas HW Reed Solomon generators */
++#define NAND_HWECC_SYNDROME   0x00020000
++/* This option skips the bbt scan during initialization. */
++#define NAND_SKIP_BBTSCAN     0x00040000
 +
-+      /* File it. This will mark the old one obsolete. */
-+      jffs2_add_fd_to_list(c, fd, &dir_f->dents);
++/* Options set by nand scan */
++/* Nand scan has allocated oob_buf */
++#define NAND_OOBBUF_ALLOC     0x40000000
++/* Nand scan has allocated data_buf */
++#define NAND_DATABUF_ALLOC    0x80000000
 +
-+      jffs2_complete_reservation(c);
-+      up(&dir_f->sem);
+       
+ /*
++ * nand_state_t - chip states
+  * Enumeration for NAND flash chip state
+  */
+ typedef enum {
+@@ -111,73 +242,137 @@
+       FL_READING,
+       FL_WRITING,
+       FL_ERASING,
+-      FL_SYNCING
++      FL_SYNCING,
++      FL_CACHEDPRG,
+ } nand_state_t;
++/* Keep gcc happy */
++struct nand_chip;
+-/*
+- * NAND Private Flash Chip Data
+- *
+- * Structure overview:
+- *
+- *  IO_ADDR_R - address to read the 8 I/O lines of the flash device 
+- *
+- *  IO_ADDR_W - address to write the 8 I/O lines of the flash device 
+- *
+- *  hwcontrol - hardwarespecific function for accesing control-lines
+- *
+- *  dev_ready - hardwarespecific function for accesing device ready/busy line
+- *
+- *  waitfunc - hardwarespecific function for wait on ready
+- *
+- *  calculate_ecc - function for ecc calculation or readback from ecc hardware
+- *
+- *  correct_data - function for ecc correction, matching to ecc generator (sw/hw)
+- *
+- *  enable_hwecc - function to enable (reset) hardware ecc generator
+- *
+- *  eccmod - mode of ecc: see constants
+- *
+- *  eccsize - databytes used per ecc-calculation
+- *
+- *  chip_delay - chip dependent delay for transfering data from array to read regs (tR)
+- *
+- *  chip_lock - spinlock used to protect access to this structure
+- *
+- *  wq - wait queue to sleep on if a NAND operation is in progress
+- *
+- *  state - give the current state of the NAND device
+- *
+- *  page_shift - number of address bits in a page (column address bits)
+- *
+- *  data_buf - data buffer passed to/from MTD user modules
+- *
+- *  data_cache - data cache for redundant page access and shadow for
+- *             ECC failure
+- *
+- *  cache_page - number of last valid page in page_cache 
++/**
++ * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independend devices
++ * @lock:               protection lock  
++ * @active:           the mtd device which holds the controller currently
+  */
++struct nand_hw_control {
++      spinlock_t       lock;
++      struct nand_chip *active;
++};
 +
-+      return 0;
-+}
---- /dev/null
-+++ linux-2.4.21/fs/jffs2/writev.c
-@@ -0,0 +1,50 @@
-+/*
-+ * JFFS2 -- Journalling Flash File System, Version 2.
-+ *
-+ * Copyright (C) 2001, 2002 Red Hat, Inc.
-+ *
-+ * Created by David Woodhouse <dwmw2@infradead.org>
-+ *
-+ * For licensing information, see the file 'LICENCE' in this directory.
-+ *
-+ * $Id$
++/**
++ * struct nand_chip - NAND Private Flash Chip Data
++ * @IO_ADDR_R:                [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device 
++ * @IO_ADDR_W:                [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device 
++ * @read_byte:                [REPLACEABLE] read one byte from the chip
++ * @write_byte:               [REPLACEABLE] write one byte to the chip
++ * @read_word:                [REPLACEABLE] read one word from the chip
++ * @write_word:               [REPLACEABLE] write one word to the chip
++ * @write_buf:                [REPLACEABLE] write data from the buffer to the chip
++ * @read_buf:         [REPLACEABLE] read data from the chip into the buffer
++ * @verify_buf:               [REPLACEABLE] verify buffer contents against the chip data
++ * @select_chip:      [REPLACEABLE] select chip nr
++ * @block_bad:                [REPLACEABLE] check, if the block is bad
++ * @block_markbad:    [REPLACEABLE] mark the block bad
++ * @hwcontrol:                [BOARDSPECIFIC] hardwarespecific function for accesing control-lines
++ * @dev_ready:                [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line
++ *                    If set to NULL no access to ready/busy is available and the ready/busy information
++ *                    is read from the chip status register
++ * @cmdfunc:          [REPLACEABLE] hardwarespecific function for writing commands to the chip
++ * @waitfunc:         [REPLACEABLE] hardwarespecific function for wait on ready
++ * @calculate_ecc:    [REPLACEABLE] function for ecc calculation or readback from ecc hardware
++ * @correct_data:     [REPLACEABLE] function for ecc correction, matching to ecc generator (sw/hw)
++ * @enable_hwecc:     [BOARDSPECIFIC] function to enable (reset) hardware ecc generator. Must only
++ *                    be provided if a hardware ECC is available
++ * @erase_cmd:                [INTERN] erase command write function, selectable due to AND support
++ * @scan_bbt:         [REPLACEABLE] function to scan bad block table
++ * @eccmode:          [BOARDSPECIFIC] mode of ecc, see defines 
++ * @eccsize:          [INTERN] databytes used per ecc-calculation
++ * @eccbytes:                 [INTERN] number of ecc bytes per ecc-calculation step
++ * @eccsteps:         [INTERN] number of ecc calculation steps per page
++ * @chip_delay:               [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR)
++ * @chip_lock:                [INTERN] spinlock used to protect access to this structure and the chip
++ * @wq:                       [INTERN] wait queue to sleep on if a NAND operation is in progress
++ * @state:            [INTERN] the current state of the NAND device
++ * @page_shift:               [INTERN] number of address bits in a page (column address bits)
++ * @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock
++ * @bbt_erase_shift:  [INTERN] number of address bits in a bbt entry
++ * @chip_shift:               [INTERN] number of address bits in one chip
++ * @data_buf:         [INTERN] internal buffer for one page + oob 
++ * @oob_buf:          [INTERN] oob buffer for one eraseblock
++ * @oobdirty:         [INTERN] indicates that oob_buf must be reinitialized
++ * @data_poi:         [INTERN] pointer to a data buffer
++ * @options:          [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about
++ *                    special functionality. See the defines for further explanation
++ * @badblockpos:      [INTERN] position of the bad block marker in the oob area
++ * @numchips:         [INTERN] number of physical chips
++ * @chipsize:         [INTERN] the size of one chip for multichip arrays
++ * @pagemask:         [INTERN] page number mask = number of (pages / chip) - 1
++ * @pagebuf:          [INTERN] holds the pagenumber which is currently in data_buf
++ * @autooob:          [REPLACEABLE] the default (auto)placement scheme
++ * @bbt:              [INTERN] bad block table pointer
++ * @bbt_td:           [REPLACEABLE] bad block table descriptor for flash lookup
++ * @bbt_md:           [REPLACEABLE] bad block table mirror descriptor
++ * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial bad block scan 
++ * @controller:               [OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices
++ * @priv:             [OPTIONAL] pointer to private chip date
++ * @errstat:          [OPTIONAL] hardware specific function to perform additional error status checks 
++ *                    (determine if errors are correctable)
++ */
++ 
+ struct nand_chip {
+-      unsigned long   IO_ADDR_R;
+-      unsigned long   IO_ADDR_W;
+-      void            (*hwcontrol)(int cmd);
+-      int             (*dev_ready)(void);
++      void  __iomem   *IO_ADDR_R;
++      void  __iomem   *IO_ADDR_W;
++      
++      u_char          (*read_byte)(struct mtd_info *mtd);
++      void            (*write_byte)(struct mtd_info *mtd, u_char byte);
++      u16             (*read_word)(struct mtd_info *mtd);
++      void            (*write_word)(struct mtd_info *mtd, u16 word);
++      
++      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, loff_t ofs, int getchip);
++      int             (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
++      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);
++      int             (*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);
++      void            (*erase_cmd)(struct mtd_info *mtd, int page);
++      int             (*scan_bbt)(struct mtd_info *mtd);
+       int             eccmode;
+       int             eccsize;
++      int             eccbytes;
++      int             eccsteps;
+       int             chip_delay;
+       spinlock_t      chip_lock;
+       wait_queue_head_t wq;
+       nand_state_t    state;
+       int             page_shift;
++      int             phys_erase_shift;
++      int             bbt_erase_shift;
++      int             chip_shift;
+       u_char          *data_buf;
++      u_char          *oob_buf;
++      int             oobdirty;
+       u_char          *data_poi;
+-      u_char          *data_cache;
+-      int             cache_page;
++      unsigned int    options;
++      int             badblockpos;
++      int             numchips;
++      unsigned long   chipsize;
++      int             pagemask;
++      int             pagebuf;
++      struct nand_oobinfo     *autooob;
++      uint8_t         *bbt;
++      struct nand_bbt_descr   *bbt_td;
++      struct nand_bbt_descr   *bbt_md;
++      struct nand_bbt_descr   *badblock_pattern;
++      struct nand_hw_control  *controller;
++      void            *priv;
++      int             (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
+ };
+ /*
+@@ -187,46 +382,35 @@
+ #define NAND_MFR_SAMSUNG      0xec
+ #define NAND_MFR_FUJITSU      0x04
+ #define NAND_MFR_NATIONAL     0x8f
++#define NAND_MFR_RENESAS      0x07
++#define NAND_MFR_STMICRO      0x20
+-/*
+- * NAND Flash Device ID Structure
+- *
+- * Structure overview:
+- *
+- *  name - Identify the device type
+- *
+- *  id -  device ID code
+- *
+- *  chipshift - total number of address bits for the device which
+- *              is used to calculate address offsets and the total
+- *              number of bytes the device is capable of.
+- *
+- *  page256 - denotes if flash device has 256 byte pages or not.
+- *
+- *  pageadrlen - number of bytes minus one needed to hold the
+- *               complete address into the flash array. Keep in
+- *               mind that when a read or write is done to a
+- *               specific address, the address is input serially
+- *               8 bits at a time. This structure member is used
+- *               by the read/write routines as a loop index for
+- *               shifting the address out 8 bits at a time.
++/**
++ * struct nand_flash_dev - NAND Flash Device ID Structure
+  *
+- *  erasesize - size of an erase block in the flash device.
++ * @name:     Identify the device type
++ * @id:       device ID code
++ * @pagesize:         Pagesize in bytes. Either 256 or 512 or 0
++ *            If the pagesize is 0, then the real pagesize 
++ *            and the eraseize are determined from the
++ *            extended id bytes in the chip
++ * @erasesize:        Size of an erase block in the flash device.
++ * @chipsize:         Total chipsize in Mega Bytes
++ * @options:  Bitfield to store chip relevant options
+  */
+ struct nand_flash_dev {
+-      char * name;
++      char *name;
+       int id;
+-      int chipshift;
++      unsigned long pagesize;
++      unsigned long chipsize;
+       unsigned long erasesize;
+-      char page256;
++      unsigned long options;
+ };
+-/*
+- * NAND Flash Manufacturer ID Structure
+- *
+- *  name - Manufacturer name
+- *
+- *  id - manufacturer ID code of device.
++/**
++ * struct nand_manufacturers - NAND Flash Manufacturer ID Structure
++ * @name:     Manufacturer name
++ * @id:       manufacturer ID code of device.
+ */
+ struct nand_manufacturers {
+       int id;
+@@ -236,39 +420,88 @@
+ extern struct nand_flash_dev nand_flash_ids[];
+ extern struct nand_manufacturers nand_manuf_ids[];
+-/*
+-* Constants for oob configuration
+-*/
+-#define NAND_BADBLOCK_POS             5
++/** 
++ * struct nand_bbt_descr - bad block table descriptor
++ * @options:  options for this descriptor
++ * @pages:    the page(s) where we find the bbt, used with option BBT_ABSPAGE
++ *            when bbt is searched, then we store the found bbts pages here.
++ *            Its an array and supports up to 8 chips now
++ * @offs:     offset of the pattern in the oob area of the page
++ * @veroffs:  offset of the bbt version counter in the oob are of the page
++ * @version:  version read from the bbt page during scan
++ * @len:      length of the pattern, if 0 no pattern check is performed
++ * @maxblocks:        maximum number of blocks to search for a bbt. This number of
++ *            blocks is reserved at the end of the device where the tables are 
++ *            written.
++ * @reserved_block_code: if non-0, this pattern denotes a reserved (rather than
++ *              bad) block in the stored bbt
++ * @pattern:  pattern to identify bad block table or factory marked good / 
++ *            bad blocks, can be NULL, if len = 0
 + *
++ * Descriptor for the bad block table marker and the descriptor for the 
++ * pattern which identifies good and bad blocks. The assumption is made
++ * that the pattern and the version count are always located in the oob area
++ * of the first block.
 + */
-+
-+#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 kvec *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 kvec *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);
-+}
-+
---- linux-2.4.21/include/asm-arm/arch-pxa/hardware.h~ramses
-+++ linux-2.4.21/include/asm-arm/arch-pxa/hardware.h
-@@ -127,16 +127,20 @@
-  * Implementation specifics
++struct nand_bbt_descr {
++      int     options;
++      int     pages[NAND_MAX_CHIPS];
++      int     offs;
++      int     veroffs;
++      uint8_t version[NAND_MAX_CHIPS];
++      int     len;
++      int     maxblocks;
++      int     reserved_block_code;
++      uint8_t *pattern;
++};
+-#define NAND_NONE_OOB                 0
+-#define NAND_JFFS2_OOB                        1
+-#define NAND_YAFFS_OOB                        2
++/* Options for the bad block table descriptors */
+-#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
++/* The number of bits used per block in the bbt on the device */
++#define NAND_BBT_NRBITS_MSK   0x0000000F
++#define NAND_BBT_1BIT         0x00000001
++#define NAND_BBT_2BIT         0x00000002
++#define NAND_BBT_4BIT         0x00000004
++#define NAND_BBT_8BIT         0x00000008
++/* The bad block table is in the last good block of the device */
++#define       NAND_BBT_LASTBLOCK      0x00000010
++/* The bbt is at the given page, else we must scan for the bbt */
++#define NAND_BBT_ABSPAGE      0x00000020
++/* The bbt is at the given page, else we must scan for the bbt */
++#define NAND_BBT_SEARCH               0x00000040
++/* bbt is stored per chip on multichip devices */
++#define NAND_BBT_PERCHIP      0x00000080
++/* bbt has a version counter at offset veroffs */
++#define NAND_BBT_VERSION      0x00000100
++/* Create a bbt if none axists */
++#define NAND_BBT_CREATE               0x00000200
++/* Search good / bad pattern through all pages of a block */
++#define NAND_BBT_SCANALLPAGES 0x00000400
++/* Scan block empty during good / bad block scan */
++#define NAND_BBT_SCANEMPTY    0x00000800
++/* Write bbt if neccecary */
++#define NAND_BBT_WRITE                0x00001000
++/* Read and write back block contents when writing bbt */
++#define NAND_BBT_SAVECONTENT  0x00002000
++/* Search good / bad pattern on the first and the second page */
++#define NAND_BBT_SCAN2NDPAGE  0x00004000
+-#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
++/* The maximum number of blocks to scan for a bbt */
++#define NAND_BBT_SCAN_MAXBLOCKS       4
+-#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
++extern int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd);
++extern int nand_update_bbt (struct mtd_info *mtd, loff_t offs);
++extern int nand_default_bbt (struct mtd_info *mtd);
++extern int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt);
++extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt);
++extern int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
++                             size_t * retlen, u_char * buf, u_char * oob_buf,
++                             struct nand_oobinfo *oobsel, int flags);
+-#define NAND_JFFS2_OOB8_FSDAPOS               6
+-#define NAND_JFFS2_OOB16_FSDAPOS      8
+-#define NAND_JFFS2_OOB8_FSDALEN               2
+-#define NAND_JFFS2_OOB16_FSDALEN      8
++/*
++* Constants for oob configuration
++*/
++#define NAND_SMALL_BADBLOCK_POS               5
++#define NAND_LARGE_BADBLOCK_POS               0
+ #endif /* __LINUX_MTD_NAND_H */
+--- linux-2.4.21/include/linux/mtd/nand_ecc.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/nand_ecc.h
+@@ -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.
   */
  
--//#ifdef CONFIG_ARCH_LUBBOCK
-+#ifdef CONFIG_ARCH_LUBBOCK
- #include "lubbock.h"
--//#endif
-+#endif
+-/*
+- * 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;
  
--//#ifdef CONFIG_ARCH_PXA_IDP
-+#ifdef CONFIG_ARCH_PXA_IDP
- #include "idp.h"
--//#endif
-+#endif
+ /*
+  * Calculate 3 byte ECC code for 256 byte block
+  */
+-void nand_calculate_ecc (const u_char *dat, u_char *ecc_code);
++int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
  
--//#ifdef CONFIG_ARCH_PXA_CERF
-+#ifdef CONFIG_ARCH_PXA_CERF
- #include "cerf.h"
--//#endif
-+#endif
+ /*
+  * 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);
 +
-+#ifdef CONFIG_ARCH_RAMSES
-+#include "ramses.h"
-+#endif
++#endif /* __MTD_NAND_ECC_H__ */
+--- linux-2.4.21/include/linux/mtd/nftl.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/nftl.h
+@@ -1,81 +1,16 @@
+-
+-/* 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>
++ */
  
- #endif  /* _ASM_ARCH_HARDWARE_H */
---- linux-2.4.21/include/asm-arm/arch-pxa/irqs.h~ramses
-+++ linux-2.4.21/include/asm-arm/arch-pxa/irqs.h
-@@ -105,14 +105,13 @@
- #define S0_BVD1_STSCHG        SA1111_IRQ(53)
- #define S1_BVD1_STSCHG        SA1111_IRQ(54)
+ #ifndef __MTD_NFTL_H__
+ #define __MTD_NFTL_H__
  
--#define SA1111_IRQ_MAX        SA1111_IRQ(54)
+-#ifndef __BOOT__
+ #include <linux/mtd/mtd.h>
+-#endif
+-
+-/* Block Control Information */
+-
+-struct nftl_bci {
+-      unsigned char ECCSig[6];
+-      __u8 Status;
+-      __u8 Status1;
+-}__attribute__((packed));
+-
+-/* Unit Control Information */
+-
+-struct nftl_uci0 {
+-      __u16 VirtUnitNum;
+-      __u16 ReplUnitNum;
+-      __u16 SpareVirtUnitNum;
+-      __u16 SpareReplUnitNum;
+-} __attribute__((packed));
+-
+-struct nftl_uci1 {
+-      __u32 WearInfo;
+-      __u16 EraseMark;
+-      __u16 EraseMark1;
+-} __attribute__((packed));
+-
+-struct nftl_uci2 {
+-        __u16 FoldMark;
+-        __u16 FoldMark1;
+-      __u32 unused;
+-} __attribute__((packed));
+-
+-union nftl_uci {
+-      struct nftl_uci0 a;
+-      struct nftl_uci1 b;
+-      struct nftl_uci2 c;
+-};
+-
+-struct nftl_oob {
+-      struct nftl_bci b;
+-      union nftl_uci u;
+-};
+-
+-/* NFTL Media Header */
+-
+-struct NFTLMediaHeader {
+-      char DataOrgID[6];
+-      __u16 NumEraseUnits;
+-      __u16 FirstPhysicalEUN;
+-      __u32 FormattedSize;
+-      unsigned char UnitSizeFactor;
+-} __attribute__((packed));
+-
+-#define MAX_ERASE_ZONES (8192 - 512)
+-
+-#define ERASE_MARK 0x3c69
+-#define SECTOR_FREE 0xff
+-#define SECTOR_USED 0x55
+-#define SECTOR_IGNORE 0x11
+-#define SECTOR_DELETED 0x00
+-
+-#define FOLD_MARK_IN_PROGRESS 0x5555
+-
+-#define ZONE_GOOD 0xff
+-#define ZONE_BAD_ORIGINAL 0
+-#define ZONE_BAD_MARKED 7
++#include <linux/mtd/blktrans.h>
  
- #undef NR_IRQS
- #define NR_IRQS               (SA1111_IRQ_MAX + 1)
+-#ifdef __KERNEL__
++#include <mtd/nftl-user.h>
  
- #endif        // defined(CONFIG_SA1111)
+ /* these info are used in ReplUnitTable */
+ #define BLOCK_NIL          0xffff /* last block of a chain */
+@@ -84,8 +19,7 @@
+ #define BLOCK_RESERVED     0xfffc /* bios block or bad block */
  
--#if defined(CONFIG_ARCH_LUBBOCK) || defined(CONFIG_ARCH_PXA_IDP) 
-+#if defined(CONFIG_ARCH_LUBBOCK) || defined(CONFIG_ARCH_PXA_IDP)
- #if CONFIG_SA1111
- #define LUBBOCK_IRQ(x)        (SA1111_IRQ_MAX + 1 + (x))
- #else
-@@ -132,6 +131,3 @@
- #define NR_IRQS               (LUBBOCK_LAST_IRQ + 1)
+ struct NFTLrecord {
+-      struct mtd_info *mtd;
+-      struct semaphore mutex;
++      struct mtd_blktrans_dev mbd;
+       __u16 MediaUnit, SpareMediaUnit;
+       __u32 EraseSize;
+       struct NFTLMediaHeader MediaHdr;
+@@ -97,13 +31,13 @@
+       __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 */
+         unsigned int nb_blocks;               /* number of physical blocks */
+         unsigned int nb_boot_blocks;  /* number of blocks used by the bios */
+         struct erase_info instr;
++      struct nand_oobinfo oobinfo;
+ };
  
- #endif        // CONFIG_ARCH_LUBBOCK
--
--
+ int NFTL_mount(struct NFTLrecord *s);
+@@ -114,9 +48,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__ */
 -
---- linux-2.4.21/include/asm-arm/arch-pxa/pxa-regs.h~ramses
-+++ linux-2.4.21/include/asm-arm/arch-pxa/pxa-regs.h
-@@ -1051,6 +1051,7 @@
- #define PGSR1         __REG(0x40F00024)  /* Power Manager GPIO Sleep State Register for GP[63-32] */
- #define PGSR2         __REG(0x40F00028)  /* Power Manager GPIO Sleep State Register for GP[84-64] */
- #define RCSR          __REG(0x40F00030)  /* Reset Controller Status Register */
-+#define PMFW          __REG(0x40F00034)  /* Power Manager Fast-Sleep Wakeup Configuration Register */
+ #endif /* __MTD_NFTL_H__ */
+--- linux-2.4.21/include/linux/mtd/partitions.h~mtd-cvs
++++ linux-2.4.21/include/linux/mtd/partitions.h
+@@ -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,26 @@
+ #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 *);
  
- #define PSSR_RDH      (1 << 5)        /* Read Disable Hold */
- #define PSSR_PH               (1 << 4)        /* Peripheral Control Hold */
---- /dev/null
-+++ linux-2.4.21/include/asm-arm/arch-pxa/ramses.h
-@@ -0,0 +1,364 @@
-+/*
-+ *  linux/include/asm-arm/arch-pxa/ramses.h
-+ *
-+ * 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.
-+ *
-+ * Copyright (c) 2002,2003 M&N Logistik-Lösungen Online GmbH
-+ *
-+ * 2001-09-13: Cliff Brake <cbrake@accelent.com>
-+ *             Initial code
-+ *
-+ * 2002-10-08: adaption from PXA IDP to Ramses
-+ * 
-+ */
-+
-+
-+/*
-+ * Note: this file must be safe to include in assembly files
-+ */
-+
-+#define RAMSES_FLASH_PHYS     (PXA_CS0_PHYS)
-+#define RAMSES_ALT_FLASH_PHYS (PXA_CS1_PHYS)
-+#define RAMSES_MEDIAQ_PHYS    (PXA_CS3_PHYS)
-+#define RAMSES_CONTROL_PHYS   (PXA_CS4_PHYS)
-+#define RAMSES_IDE_PHYS               (PXA_CS5_PHYS + 0x03000000)
-+#define RAMSES_ETH_PHYS               (PXA_CS5_PHYS + 0x03400000)
-+#define RAMSES_COREVOLT_PHYS  (PXA_CS5_PHYS + 0x03800000)
-+#define RAMSES_CPLD_PHYS      (PXA_CS5_PHYS + 0x03C00000)
-+
 +/*
-+ * virtual memory map
-+ */
-+
-+#define RAMSES_IDE_BASE               (0xf0000000)
-+#define RAMSES_IDE_SIZE               (1*1024*1024)
-+#define RAMSES_ETH_BASE               (RAMSES_IDE_BASE + RAMSES_IDE_SIZE)
-+#define RAMSES_ETH_SIZE               (1*1024*1024)
-+#define RAMSES_COREVOLT_BASE  (RAMSES_ETH_BASE + RAMSES_ETH_SIZE)
-+#define RAMSES_COREVOLT_SIZE  (1*1024*1024)
-+#define RAMSES_CPLD_BASE      (RAMSES_COREVOLT_BASE + RAMSES_COREVOLT_SIZE)
-+#define RAMSES_CPLD_SIZE      (1*1024*1024)
-+#define RAMSES_CONTROL_BASE   (RAMSES_CPLD_BASE + RAMSES_CPLD_SIZE)
-+#define RAMSES_CONTROL_SIZE   (1*1024*1024)
-+
-+#if (RAMSES_CONTROL_BASE + RAMSES_CONTROL_SIZE) > 0xfc000000
-+#error Your custom IO space is getting a bit large !!
-+#endif
-+
-+#define CPLD_P2V(x)           ((x) - RAMSES_CPLD_PHYS + RAMSES_CPLD_BASE)
-+#define CPLD_V2P(x)           ((x) - RAMSES_CPLD_BASE + RAMSES_CPLD_PHYS)
-+#define CTRL_P2V(x)           ((x) - RAMSES_CONTROL_PHYS + RAMSES_CONTROL_BASE)
-+#define CTRL_V2P(x)           ((x) - RAMSES_CONTROL_BASE + RAMSES_CONTROL_PHYS)
-+#define CORE_P2V(x)           ((x) - RAMSES_COREVOLT_PHYS + RAMSES_COREVOLT_BASE)
-+#define CORE_V2P(x)           ((x) - RAMSES_COREVOLT_BASE + RAMSES_COREVOLT_PHYS)
-+
-+//smc91111 driver compatibility issue
-+#define ETH_BASE              RAMSES_ETH_BASE
-+
-+#ifndef __ASSEMBLY__
-+#  define __CPLD_REG(x)               (*((volatile unsigned long *)CPLD_P2V(x)))
-+#  define __CTRL_REG(x)         (*((volatile unsigned long *)CTRL_P2V(x)))
-+#  define __CORE_REG(x)         (*((volatile unsigned long *)CORE_P2V(x)))
-+#else
-+#  define __CPLD_REG(x)               CPLD_P2V(x)
-+#  define __CTRL_REG(x)               CTRL_P2V(x)
-+#  define __CORE_REG(x)               CORE_P2V(x)
-+#endif
-+
-+/* CPLD addresses */
-+
-+#define RAMSES_CPLD_PERIPH_PWR_               (RAMSES_CPLD_PHYS + 0x04)
-+#define RAMSES_CPLD_PERIPH_PWR                __CPLD_REG(RAMSES_CPLD_PERIPH_PWR_)
-+#define PER_RESET                     (1 << 4)
-+#define PER_PWR_EN                    (1 << 3)
-+#define USB_HOST_PWR_EN                       (1 << 2)
-+#define CORE_VAR_EN                   (1 << 0)
-+
-+#define RAMSES_CPLD_LED_CONTROL_      (RAMSES_CPLD_PHYS + 0x08)
-+#define RAMSES_CPLD_LED_CONTROL               __CPLD_REG(RAMSES_CPLD_LED_CONTROL_)
-+#define CPLD_LED2                     (1 << 6)
-+#define CPLD_LED1                     (1 << 5)
-+#define GSM_ACTIVE                    (1 << 4)
-+
-+#define RAMSES_CPLD_KB_COL_HIGH_      (RAMSES_CPLD_PHYS + 0x0C)
-+#define RAMSES_CPLD_KB_COL_HIGH               __CPLD_REG(RAMSES_CPLD_KB_COL_HIGH_)
-+// kbch(7)..kbch(13) on bit 0..6
-+
-+#define RAMSES_CPLD_KB_COL_LOW_               (RAMSES_CPLD_PHYS + 0x10)
-+#define RAMSES_CPLD_KB_COL_LOW                __CPLD_REG(RAMSES_CPLD_KB_COL_LOW_)
-+// kbcl(0)..kbch(6) on bit 0..6
-+
-+#define RAMSES_CPLD_PCCARD_EN_                (RAMSES_CPLD_PHYS + 0x14)
-+#define RAMSES_CPLD_PCCARD_EN         __CPLD_REG(RAMSES_CPLD_PCCARD_EN_)
-+#define PCC1_RESET                    (1 << 7)
-+#define PCC0_RESET                    (1 << 6)
-+#define PCC1_ENABLE                   (1 << 1)
-+#define PCC0_ENABLE                   (1 << 0)
-+
-+#define RAMSES_CPLD_PCCARD_PWR_               (RAMSES_CPLD_PHYS + 0x28)
-+#define RAMSES_CPLD_PCCARD_PWR                __CPLD_REG(RAMSES_CPLD_PCCARD_PWR_)
-+#define PCC1_PWR3                     (1 << 7)
-+#define PCC1_PWR2                     (1 << 6)
-+#define PCC1_PWR1                     (1 << 5)
-+#define PCC1_PWR0                     (1 << 4)
-+#define PCC0_PWR3                     (1 << 3)
-+#define PCC0_PWR2                     (1 << 2)
-+#define PCC0_PWR1                     (1 << 1)
-+#define PCC0_PWR0                     (1 << 0)
-+
-+#define RAMSES_CPLD_MISC_CTRL_                (RAMSES_CPLD_PHYS + 0x2C)
-+#define RAMSES_CPLD_MISC_CTRL         __CPLD_REG(RAMSES_CPLD_MISC_CTRL_)
-+#define RAMSES_IRDA_MD1                       (1 << 5)
-+#define RAMSES_IRDA_MD0                       (1 << 4)
-+#define RAMSES_FIR                    (1 << 3)
-+
-+#define RAMSES_CPLD_LCD_              (RAMSES_CPLD_PHYS + 0x30)
-+#define RAMSES_CPLD_LCD                       __CPLD_REG(RAMSES_CPLD_LCD_)
-+#define RAMSES_LCD_PINC                       (1 << 7)
-+#define RAMSES_LCD_PUP                        (1 << 6)
-+#define RAMSES_LCD_PCS                        (1 << 5)
-+#define RAMSES_LCD_DISPOFF            (1 << 2)
-+#define RAMSES_LCD_VCC                        (1 << 0)
-+
-+#define RAMSES_CPLD_FLASH_WE_         (RAMSES_CPLD_PHYS + 0x34)
-+#define RAMSES_CPLD_FLASH_WE          __CPLD_REG(RAMSES_CPLD_FLASH_WE_)
-+#define RAMSES_FLASH_WE                       (1 << 0)
-+
-+/* Read-Only registers */
-+
-+#define RAMSES_CPLD_KB_ROW_           (RAMSES_CPLD_PHYS + 0x50)
-+#define RAMSES_CPLD_KB_ROW            __CPLD_REG(RAMSES_CPLD_KB_ROW_)
-+// kbr(0)..kbr(6) on bits 0..6
-+
-+#define RAMSES_CPLD_PCCARD0_STATUS_   (RAMSES_CPLD_PHYS + 0x54)
-+#define RAMSES_CPLD_PCCARD0_STATUS    __CPLD_REG(RAMSES_CPLD_PCCARD0_STATUS_)
-+#define RAMSES_CPLD_PCCARD1_STATUS_   (RAMSES_CPLD_PHYS + 0x58)
-+#define RAMSES_CPLD_PCCARD1_STATUS    __CPLD_REG(RAMSES_CPLD_PCCARD1_STATUS_) 
-+#define _PCC_WRPROT                   (1 << 7)
-+#define _PCC_S16                      (1 << 7)
-+#define _PCC_RESET                    (1 << 6)
-+#define _PCC_IRQ                      (1 << 5)
-+#define _PCC_INPACK                   (1 << 4)
-+#define PCC_BVD2                      (1 << 3)
-+#define PCC_BVD1                      (1 << 2)
-+#define PCC_VS2                               (1 << 1)
-+#define PCC_VS1                               (1 << 0)
-+
-+#define RAMSES_CPLD_MISC_STATUS_      (RAMSES_CPLD_PHYS + 0x5C)
-+#define RAMSES_CPLD_MISC_STATUS               __CPLD_REG(RAMSES_CPLD_MISC_STATUS_)
-+#define RAMSES_MMC_WRPROT             (1 << 7)
-+#define RAMSES_USB_OVERCURR           (1 << 4)
-+#define RAMSES_CHG_STS                        (1 << 2)
-+#define RAMSES_WALL_IN                        (1 << 1)
-+#define RAMSES_USB_D_CON              (1 << 0)
-+
-+#define RAMSES_CPLD_YEAR_             (RAMSES_CPLD_PHYS + 0x60)
-+#define RAMSES_CPLD_YEAR              __CPLD_REG(RAMSES_CPLD_YEAR_)
-+
-+#define RAMSES_CPLD_MONTH_            (RAMSES_CPLD_PHYS + 0x64)
-+#define RAMSES_CPLD_MONTH             __CPLD_REG(RAMSES_CPLD_MONTH_)
-+
-+#define RAMSES_CPLD_DAY_              (RAMSES_CPLD_PHYS + 0x68)
-+#define RAMSES_CPLD_DAY                       __CPLD_REG(RAMSES_CPLD_DAY_)
-+
-+#define RAMSES_CPLD_REV_              (RAMSES_CPLD_PHYS + 0x6C)
-+#define RAMSES_CPLD_REV                       __CPLD_REG(RAMSES_CPLD_REV_)
-+
-+#define RAMSES_CPLD_VSTAT_            (RAMSES_CPLD_PHYS + 0x7C)
-+#define RAMSES_CPLD_VSTAT             __CPLD_REG(RAMSES_CPLD_VSTAT_)
-+#define RAMSES_BWE                    (1 << 1)
-+
-+
-+/* Flags for ramses_flags */
-+
-+#define RAMSES_FLAGS_LCD_FBTURN               (1<<0)
-+/* MUST stay bit 0 */
-+#define RAMSES_FLAGS_SCANNER_BEAM     (1<<1)
-+#define RAMSES_FLAGS_KEY_SCAN         (1<<2)
-+#define RAMSES_FLAGS_KEY_SUSPEND      (1<<3)
-+#define RAMSES_FLAGS_KEY_OFF          (1<<4)
-+
-+
-+/* Offset in SMC EEPROM for LCD type */
-+#define RAMSES_LCD_TYPE_OFFSET                0x23
-+
-+
-+/* The control register on the I/O board */
-+
-+#define RAMSES_CONTROL_                 (RAMSES_CONTROL_PHYS + 0)
-+#define RAMSES_CONTROL                  __CTRL_REG(RAMSES_CONTROL_)
-+// 5c00 = 0101 1100 0000 0000
-+#define RAMSES_CONTROL_SCANNER_TRIG_  (1 << 15)
-+#define RAMSES_CONTROL_SCANNER_WAKE_  (1 << 14)
-+#define RAMSES_CONTROL_SCANNER_PWR    (1 << 13)
-+#define RAMSES_CONTROL_LED_BLUE_      (1 << 12)
-+
-+#define RAMSES_CONTROL_LED_ORANGE_    (1 << 11)
-+#define RAMSES_CONTROL_GSM_RESET      (1 << 10)
-+#define RAMSES_CONTROL_GSM_BOOT               (1 << 9)
-+#define RAMSES_CONTROL_GSM_PWR                (1 << 8)
-+
-+#define RAMSES_CONTROL_POWEROFF               (1 << 7)
-+#define       RAMSES_CONTROL_USB_INTERN       (1 << 6)
-+#define RAMSES_CONTROL_MMC_PWR                (1 << 5)
-+#define RAMSES_CONTROL_UART_PWR               (1 << 4)
-+
-+#define RAMSES_CONTROL_LCD_BLIGHT     (1 << 3)
-+#define RAMSES_CONTROL_USB            (1 << 2)
-+
-+#define RAMSES_POWER_OFF()        { ramses_control_shadow |=  RAMSES_CONTROL_POWEROFF;      RAMSES_CONTROL = ramses_control_shadow; }
-+
-+// Active low
-+#define RAMSES_SCANNER_TRIG_ON()  { ramses_control_shadow &= ~RAMSES_CONTROL_SCANNER_TRIG_; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_SCANNER_TRIG_OFF() { ramses_control_shadow |=  RAMSES_CONTROL_SCANNER_TRIG_; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_SCANNER_WAKE_ON()  { ramses_control_shadow &= ~RAMSES_CONTROL_SCANNER_WAKE_; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_SCANNER_WAKE_OFF() { ramses_control_shadow |=  RAMSES_CONTROL_SCANNER_WAKE_; RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_LED_BLUE_ON()      { ramses_control_shadow &= ~RAMSES_CONTROL_LED_BLUE_;     RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_LED_BLUE_OFF()     { ramses_control_shadow |=  RAMSES_CONTROL_LED_BLUE_;     RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_LED_ORANGE_ON()    { ramses_control_shadow &= ~RAMSES_CONTROL_LED_ORANGE_;   RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_LED_ORANGE_OFF()   { ramses_control_shadow |=  RAMSES_CONTROL_LED_ORANGE_;   RAMSES_CONTROL = ramses_control_shadow; }
-+
-+// Active high
-+#define RAMSES_SCANNER_ON()       { ramses_control_shadow |=  RAMSES_CONTROL_SCANNER_PWR;   RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_SCANNER_OFF()      { ramses_control_shadow &= ~RAMSES_CONTROL_SCANNER_PWR;   RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_GSM_RESET_ON()     { ramses_control_shadow |=  RAMSES_CONTROL_GSM_RESET;     RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_GSM_RESET_OFF()    { ramses_control_shadow &= ~RAMSES_CONTROL_GSM_RESET;     RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_GSM_BOOT_ON()      { ramses_control_shadow |=  RAMSES_CONTROL_GSM_BOOT;      RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_GSM_BOOT_OFF()     { ramses_control_shadow &= ~RAMSES_CONTROL_GSM_BOOT;      RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_GSM_ON()           { ramses_control_shadow |=  RAMSES_CONTROL_GSM_PWR;       RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_GSM_OFF()          { ramses_control_shadow &= ~RAMSES_CONTROL_GSM_PWR;       RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_USB_INTERN()       { ramses_control_shadow |=  RAMSES_CONTROL_USB_INTERN;    RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_USB_EXTERN()       { ramses_control_shadow &= ~RAMSES_CONTROL_USB_INTERN;    RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_UART_ON()          { ramses_control_shadow |=  RAMSES_CONTROL_UART_PWR;      RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_UART_OFF()         { ramses_control_shadow &= ~RAMSES_CONTROL_UART_PWR;      RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_MMC_ON()           { ramses_control_shadow |=  RAMSES_CONTROL_MMC_PWR;       RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_MMC_OFF()          { ramses_control_shadow &= ~RAMSES_CONTROL_MMC_PWR;       RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_LCD_BLIGHT_ON()    { ramses_control_shadow |=  RAMSES_CONTROL_LCD_BLIGHT;    RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_LCD_BLIGHT_OFF()   { ramses_control_shadow &= ~RAMSES_CONTROL_LCD_BLIGHT;    RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_USB_BUS_ON()       { ramses_control_shadow |=  RAMSES_CONTROL_USB;           RAMSES_CONTROL = ramses_control_shadow; }
-+#define RAMSES_USB_BUS_OFF()      { ramses_control_shadow &= ~RAMSES_CONTROL_USB;           RAMSES_CONTROL = ramses_control_shadow; }
-+
-+// Corevolt settings
-+#define RAMSES_COREVOLT_          (RAMSES_COREVOLT_PHYS)
-+#define RAMSES_COREVOLT           __CORE_REG(RAMSES_COREVOLT_)
-+
-+// Battery protocol
-+#define HDQ_TMP               0x02
-+#define HDQ_LMD               0x05
-+#define HDQ_VSB               0x0b
-+#define HDQ_CACT      0x0d
-+#define HDQ_SAEH      0x0f
-+#define HDQ_SAEL      0x10
-+#define HDQ_RCAC      0x11
-+#define HDQ_DCR               0x18
-+
-+
-+#ifndef __ASSEMBLY__
-+
-+/* Ramses specific functions */
-+void ramses_lcd_power_on(void);
-+void ramses_lcd_power_off(void);
-+void ramses_lcd_backlight_on(void);
-+void ramses_lcd_backlight_off(void);
-+void ramses_lcd_set_intensity(int i);
-+#ifdef OLDCODE
-+void ramses_lcd_set_pwm1(int p);
-+#endif
-+void ramses_lcd_set_brightness(int b);
-+void ramses_lcd_set_contrast(int c);
-+int ramses_lcd_get_intensity(void);
-+int ramses_lcd_get_brightness(void);
-+int ramses_lcd_get_contrast(void);
-+int ramses_hdq_get_reg(unsigned char reg);
-+void ramses_shut_off(void);
-+void ramses_set_corevolt(int volt);
-+
-+
-+/* shadow registers for write only registers */
-+extern u16 ramses_control_shadow;
-+extern int ramses_corevolt_shadow;
-+extern u16 ramses_lcd_type;
-+extern int ramses_lcd_pwm1_shadow;
-+
++ * Functions dealing with the various ways of partitioning the space
++ */
 +
-+/* flag register for various settings */
-+extern unsigned int ramses_flags;
++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);
++};
 +
-+/* 
-+ * macros to write to write only register
-+ *
-+ * none of these macros are protected from 
-+ * multiple drivers using them in interrupt context.
-+ */
++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 WRITE_RAMSES_CONTROL(value, mask) \
-+{\
-+      ramses_control_shadow = ((value & mask) | (ramses_control_shadow & ~mask));\
-+      RAMSES_CONTROL = ramses_control_shadow;\
-+}
-+#endif
++#define put_partition_parser(p) do { module_put((p)->owner); } while(0)
 +
+ #endif
+--- /dev/null
++++ linux-2.4.21/include/linux/mtd/physmap.h
+@@ -0,0 +1,61 @@
 +/*
-+ *  USB Host
++ * For boards with physically mapped flash and using 
++ * drivers/mtd/maps/physmap.c mapping driver.
 + *
-+ *  The SL811HS is selected with nCS3 and some address bits:
++ * $Id$
++ *
++ * 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.
 + *
-+ *                               12    8    4
-+ *  nA14, nCS[3], address mask 1011 1111 1111 0000 = xBf00
 + */
-+#define SL811HS_PHYS (PXA_CS3_PHYS+0xBFF0)
-+#define SL811HS_DATA (PXA_CS3_PHYS+0xBFF4)
-+
-+
-+
-+
-+
-+
-+
-+#define PCC_DETECT(x) (GPLR(7 + (x)) & GPIO_bit(7 + (x)))
-+
-+
-+
-+
-+
-+/* A listing of interrupts used by external hardware devices */
-+
-+#define TOUCH_PANEL_IRQ                       IRQ_GPIO(21)
-+#define TOUCH_PANEL_IRQ_EDGE          GPIO_FALLING_EDGE
-+
-+#define ETHERNET_IRQ                  IRQ_GPIO(4)
-+#define ETHERNET_IRQ_EDGE             GPIO_RISING_EDGE
 +
-+#define CFCARD_CD_VALID                       IRQ_GPIO(8)
-+#define CFCARD_CD_VALID_EDGE          GPIO_BOTH_EDGES
++#ifndef __LINUX_MTD_PHYSMAP__
 +
-+#define CFCARD_RDYINT                 IRQ_GPIO(22)
++#include <linux/config.h>
 +
-+#define RAMSES_KEYBOARD_IRQ           IRQ_GPIO(3)
-+#define RAMSES_KEYBOARD_IRQ_EDGE      GPIO_FALLING_EDGE
++#if defined(CONFIG_MTD_PHYSMAP) 
 +
-+#define SL811HS_IRQ                   IRQ_GPIO(32)
-+#define SL811HS_IRQ_EDGE              GPIO_RISING_EDGE
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/partitions.h>
 +
 +/*
-+ * Macros for LED Driver
++ * The map_info for physmap.  Board can override size, buswidth, phys,
++ * (*set_vpp)(), etc in their initial setup routine.
 + */
-+
-+/* leds 0 = ON */
-+#define RAMSES_HB_LED (1<<5)  
-+#define RAMSES_BUSY_LED       (1<<6)
-+
-+#define RAMSES_LEDS_MASK              (RAMSES_HB_LED | RAMSES_BUSY_LED)
-+
-+#define RAMSES_WRITE_LEDS(value)      (RAMSES_CPLD_LED_CONTROL = ((RAMSES_CPLD_LED_CONTROL & ~(RAMSES_LEDS_MASK)) | value))
++extern struct map_info physmap_map;
 +
 +/*
-+ * macros for MTD driver
++ * Board needs to specify the exact mapping during their setup time.
 + */
++static inline void physmap_configure(unsigned long addr, unsigned long size, int bankwidth, void (*set_vpp)(struct map_info *, int) )
++{
++      physmap_map.phys = addr;
++      physmap_map.size = size;
++      physmap_map.bankwidth = bankwidth;
++      physmap_map.set_vpp = set_vpp;
++}
 +
-+#define FLASH_WRITE_PROTECT_DISABLE() ((RAMSES_CPLD_FLASH_WE) &= ~(0x1))
-+#define FLASH_WRITE_PROTECT_ENABLE()  ((RAMSES_CPLD_FLASH_WE) |= (0x1))
-+
-+
---- linux-2.4.21/include/asm-arm/arch-pxa/time.h~pxa-timerint
-+++ linux-2.4.21/include/asm-arm/arch-pxa/time.h
-@@ -33,7 +33,7 @@
- /* IRQs are disabled before entering here from do_gettimeofday() */
- static unsigned long pxa_gettimeoffset (void)
- {
--      unsigned long ticks_to_match, elapsed, usec;
-+      long ticks_to_match, elapsed, usec;
-       /* Get ticks before next timer match */
-       ticks_to_match = OSMR0 - OSCR;
-@@ -41,6 +41,10 @@
-       /* We need elapsed ticks since last match */
-       elapsed = LATCH - ticks_to_match;
-+      /* don't get fooled by the workaround in pxa_timer_interrupt() */
-+      if (elapsed <= 0)
-+              return 0;
++#if defined(CONFIG_MTD_PARTITIONS)
 +
-       /* Now convert them to usec */
-       usec = (unsigned long)(elapsed*tick)/LATCH;
-@@ -59,6 +63,15 @@
-        * IRQs are disabled inside the loop to ensure coherence between
-        * lost_ticks (updated in do_timer()) and the match reg value, so we
-        * can use do_gettimeofday() from interrupt handlers.
-+       *
-+       * HACK ALERT: it seems that the PXA timer regs aren't updated right
-+       * away in all cases when a write occurs.  We therefore compare with
-+       * 8 instead of 0 in the while() condition below to avoid missing a
-+       * match if OSCR has already reached the next OSMR value.
-+       * Experience has shown that up to 6 ticks are needed to work around
-+       * this problem, but let's use 8 to be conservative.  Note that this
-+       * affect things only when the timer IRQ has been delayed by nearly
-+       * exactly one tick period which should be a pretty rare event. 
-        */
-       do {
-               do_leds();
-@@ -68,7 +81,7 @@
-               OSSR = OSSR_M0;  /* Clear match on timer 0 */
-               next_match = (OSMR0 += LATCH);
-               restore_flags( flags );
--      } while( (signed long)(next_match - OSCR) <= 0 );
-+      } while( (signed long)(next_match - OSCR) <= 8 );
- }
- extern inline void setup_timer (void)
---- /dev/null
-+++ linux-2.4.21/include/asm-arm/bug.h
-@@ -0,0 +1 @@
-+/* dummy */
---- /dev/null
-+++ linux-2.4.21/include/asm-arm/sl811-hw.h
-@@ -0,0 +1,202 @@
 +/*
-+File: include/asm-arm/sl811-hw.h
-+
-+19.09.2003 hne@ist1.de
-+Use Kernel 2.4.20 and this source from 2.4.22
-+Splitt hardware depens into file sl811-x86.h and sl811-arm.h.
-+Functions as inline.
-+
-+23.09.2003 hne
-+Move Hardware depend header sl811-arm.h into include/asm-arm/sl811-hw.h.
-+GPRD as parameter.
-+
-+24.09.2003 hne
-+Use Offset from ADDR to DATA instand of direct io.
-+
-+03.10.2003 hne
-+Low level only for port io into hardware-include.
-+*/
++ * 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);
 +
-+#ifndef __LINUX_SL811_HW_H
-+#define __LINUX_SL811_HW_H
++#endif /* defined(CONFIG_MTD_PARTITIONS) */
++#endif /* defined(CONFIG_MTD) */
 +
-+#ifdef CONFIG_X86
-+#define MAX_CONTROLERS                1       /* Max number of sl811 controllers */
-+                                      /* Always 1 for this architecture! */
++#endif /* __LINUX_MTD_PHYSMAP__ */
 +
-+#define SIZEOF_IO_REGION      1       /* Size for request/release region */
+--- /dev/null
++++ linux-2.4.21/include/linux/mtd/plat-ram.h
+@@ -0,0 +1,35 @@
++/* linux/include/mtd/plat-ram.h
++ *
++ * (c) 2004 Simtec Electronics
++ *    http://www.simtec.co.uk/products/SWLINUX/
++ *    Ben Dooks <ben@simtec.co.uk>
++ *
++ * Generic platform device based RAM map
++ *
++ * $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
++ * published by the Free Software Foundation.
++ *
++ */
 +
-+#define OFFSET_DATA_REG data_off      /* Offset from ADDR_IO to DATA_IO (future) */
-+                                      /* Can change by arg */
++#ifndef __LINUX_MTD_PLATRAM_H
++#define __LINUX_MTD_PLATRAM_H __FILE__
 +
-+static int io = 0xf100000e;   /* Base addr_io */
-+static int data_off = 1;      /* Offset from addr_io to addr_io */
-+static int irq = 44;          /* also change gprd !!! */
-+static int gprd = 23;         /* also change irq  !!! */
++#define PLATRAM_RO (0)
++#define PLATRAM_RW (1)
 +
-+MODULE_PARM(io,"i");
-+MODULE_PARM_DESC(io,"sl811 address io port 0xf100000e");
-+MODULE_PARM(data_off,"i");
-+MODULE_PARM_DESC(data_off,"sl811 data io port offset from address port (default 1)");
-+MODULE_PARM(irq,"i");
-+MODULE_PARM_DESC(irq,"sl811 irq 44(default)");
-+MODULE_PARM(gprd,"i");
-+MODULE_PARM_DESC(gprd,"sl811 GPRD port 23(default)");
-+#endif
++struct platdata_mtd_ram {
++      char                    *mapname;
++      char                   **probes;
++      struct mtd_partition    *partitions;
++      int                      nr_partitions;
++      int                      bankwidth;
 +
-+#ifdef CONFIG_ARCH_RAMSES
-+#define SIZEOF_IO_REGION        8       /* Size for request/release region */
-+static void *ramses_sl811hs;            /* dynamically assign virtual address */
-+#endif
++      /* control callbacks */
 +
++      void    (*set_rw)(struct device *dev, int to);
++};
 +
++#endif /* __LINUX_MTD_PLATRAM_H */
+--- /dev/null
++++ linux-2.4.21/include/linux/mtd/xip.h
+@@ -0,0 +1,107 @@
 +/*
-+ * Low level: Read from Data port [arm]
++ * MTD primitives for XIP support
++ *
++ * Author:    Nicolas Pitre
++ * Created:   Nov 2, 2004
++ * Copyright: (C) 2004 MontaVista Software, Inc.
++ *
++ * This XIP support for MTD has been loosely inspired
++ * by an earlier patch authored by David Woodhouse.
++ *
++ * 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$
 + */
-+static __u8 inline sl811_read_data (struct sl811_hc *hc)
-+{
-+      __u8 data;
-+      data = readb(hc->data_io);
-+      rmb();
-+//printk("%s: in  %08p %02x\n", __FUNCTION__, hc->data_io, data);
-+      return data;
-+}
 +
-+/*
-+ * Low level: Write to index register [arm]
-+ */
-+static void inline sl811_write_index (struct sl811_hc *hc, __u8 index)
-+{
-+//printk("%s: out %08p %02x\n", __FUNCTION__, hc->addr_io, index);
-+      writeb(index, hc->addr_io);
-+      wmb();
-+}
++#ifndef __LINUX_MTD_XIP_H__
++#define __LINUX_MTD_XIP_H__
 +
-+/*
-+ * Low level: Write to Data port [arm]
-+ */
-+static void inline sl811_write_data (struct sl811_hc *hc, __u8 data)
-+{
-+//printk("%s: out %08p %02x\n", __FUNCTION__, hc->data_io, data);
-+      writeb(data, hc->data_io);
-+      wmb();
-+}
++#include <linux/config.h>
++
++#ifdef CONFIG_MTD_XIP
 +
 +/*
-+ * Low level: Write to index register and data port [arm]
++ * Function that are modifying the flash state away from array mode must
++ * obviously not be running from flash.  The __xipram is therefore marking
++ * those functions so they get relocated to ram.
 + */
-+static void inline sl811_write_index_data (struct sl811_hc *hc, __u8 index, __u8 data)
-+{
-+      writeb(index, hc->addr_io);
-+//printk("%s: out %08p %02x\n", __FUNCTION__, hc->addr_io, index);
-+      writeb(data, hc->data_io);
-+//printk("%s: out %08p %02x\n", __FUNCTION__, hc->data_io, data);
-+      wmb();
-+}
-+
++#define __xipram __attribute__ ((__section__ (".data")))
 +
 +/*
-+ * This       function is board specific.  It sets up the interrupt to
-+ * be an edge trigger and trigger on the rising       edge
++ * We really don't want gcc to guess anything.
++ * We absolutely _need_ proper inlining.
 + */
-+static void inline sl811_init_irq(void)
-+{
-+#ifdef CONFIG_X86
-+      GPDR &= ~(1<<gprd);
-+      set_GPIO_IRQ_edge(1<<gprd, GPIO_RISING_EDGE);
-+#endif
-+#ifdef CONFIG_ARCH_PXA
-+      int irq_gpio_pin = IRQ_TO_GPIO_2_80(SL811HS_IRQ);
-+      GPDR(irq_gpio_pin) &= ~GPIO_bit(irq_gpio_pin);
-+      set_GPIO_IRQ_edge(irq_gpio_pin, SL811HS_IRQ_EDGE);
-+#endif
-+}
++#include <linux/compiler.h>
 +
-+/*****************************************************************
-+ *
-+ * Function Name: release_regions [arm]
-+ *
-+ * This function is board specific. It release all io address
-+ * from memory (if can).
-+ *
-+ * Input: struct sl811_hc * *
++/*
++ * Each architecture has to provide the following macros.  They must access
++ * the hardware directly and not rely on any other (XIP) functions since they
++ * won't be available when used (flash not in array mode).
 + *
-+ * Return value  : 0 = OK
++ * xip_irqpending()
 + *
-+ *****************************************************************/
-+static void inline sl811_release_regions(struct sl811_hc *hc)
-+{
-+#ifdef CONFIG_X86
-+      if (hc->addr_io)
-+              release_region(hc->addr_io, SIZEOF_IO_REGION);
-+      hc->addr_io = 0;
-+
-+      if (hc->data_io)
-+              release_region(hc->data_io, SIZEOF_IO_REGION);
-+      hc->data_io = 0;
-+#endif
-+#ifdef CONFIG_ARCH_RAMSES
-+      if (ramses_sl811hs) {
-+              iounmap(ramses_sl811hs);
-+              release_mem_region(SL811HS_PHYS, SIZEOF_IO_REGION);
-+      }
-+      hc->addr_io = 0;
-+      hc->data_io = 0;
-+      RAMSES_CPLD_PERIPH_PWR &= ~USB_HOST_PWR_EN;
-+      RAMSES_USB_BUS_OFF();
-+#endif
-+}
-+
-+/*****************************************************************
++ *    return non zero when any hardware interrupt is pending.
 + *
-+ * Function Name: request_regions [arm]
++ * xip_currtime()
 + *
-+ * This function is board specific. It request all io address and
-+ * maps into memory (if can).
++ *    return a platform specific time reference to be used with
++ *    xip_elapsed_since().
 + *
-+ * Input: struct sl811_hc *
++ * xip_elapsed_since(x)
 + *
-+ * Return value  : 0 = OK
++ *    return in usecs the elapsed timebetween now and the reference x as
++ *    returned by xip_currtime().
 + *
-+ *****************************************************************/
-+static int inline sl811_request_regions (struct sl811_hc *hc, int addr_io, int data_io, const char *name)
-+{
-+#ifdef CONFIG_X86
-+      if (!request_region(addr_io, SIZEOF_IO_REGION, name)) {
-+              PDEBUG(3, "request address %d failed", addr_io);
-+              return -EBUSY;
-+      }
-+      hc->addr_io =   addr_io;
++ *    note 1: convertion to usec can be approximated, as long as the
++ *            returned value is <= the real elapsed time.
++ *    note 2: this should be able to cope with a few seconds without
++ *            overflowing.
++ */
 +
-+      if (!request_region(data_io, SIZEOF_IO_REGION, MODNAME)) {
-+              PDEBUG(3, "request address %d failed", data_io);
-+              /* release_region(hc->addr_io, SIZEOF_IO_REGION); */
-+              return -EBUSY;
-+      }
-+      hc->data_io =   data_io;
-+#endif
-+#ifdef CONFIG_ARCH_RAMSES
-+      RAMSES_USB_BUS_ON();
-+      RAMSES_CPLD_PERIPH_PWR |= USB_HOST_PWR_EN;
-+      mdelay(300);
++#if defined(CONFIG_ARCH_SA1100) || defined(CONFIG_ARCH_PXA)
 +
-+        if (!request_mem_region(SL811HS_PHYS, SIZEOF_IO_REGION, name)) {
-+                printk(KERN_ERR "unable to reserve region\n");
-+                return -EBUSY;
-+        } else {
-+                ramses_sl811hs = ioremap_nocache(SL811HS_PHYS, SIZEOF_IO_REGION);
-+              dbg("phys %p -> virt %p\n", SL811HS_PHYS, ramses_sl811hs);
-+                if (!ramses_sl811hs) {
-+                        printk(KERN_ERR "unable to map region\n");
-+                        release_mem_region(SL811HS_PHYS, SIZEOF_IO_REGION);
-+                        return -EBUSY;
-+                }
-+        }
-+        hc->addr_io = (unsigned long) ramses_sl811hs;
-+        hc->data_io = (unsigned long) ramses_sl811hs+4;
++#include <asm/hardware.h>
++#ifdef CONFIG_ARCH_PXA
++#include <asm/arch/pxa-regs.h>
 +#endif
 +
-+      return 0;
-+}
++#define xip_irqpending()      (ICIP & ICMR)
 +
-+#endif // __LINUX_SL811_HW_H
---- linux-2.4.21/include/linux/apm_bios.h~pm
-+++ linux-2.4.21/include/linux/apm_bios.h
-@@ -16,6 +16,8 @@
-  * General Public License for more details.
-  */
-+#include <linux/pm-devices.h>
++/* we sample OSCR and convert desired delta to usec (1/4 ~= 1000000/3686400) */
++#define xip_currtime()                (OSCR)
++#define xip_elapsed_since(x)  (signed)((OSCR - (x)) / 4)
 +
- typedef unsigned short        apm_event_t;
- typedef unsigned short        apm_eventinfo_t;
-@@ -59,6 +61,16 @@
- };
- /*
-+ * Allow device specific code to register function which
-+ * gets the battery power status (see arch/arm/mach-pxa/apm.c).
-+ */
-+extern void apm_register_get_power_status( int (*fn)(u_char *ac_line_status,
-+                              u_char *battery_status,
-+                              u_char *battery_flag,
-+                              u_char *battery_percentage,
-+                              u_short *battery_life));
++#else
++
++#warning "missing IRQ and timer primitives for XIP MTD support"
++#warning "some of the XIP MTD support code will be disabled"
++#warning "your system will therefore be unresponsive when writing or erasing flash"
++
++#define xip_irqpending()      (0)
++#define xip_currtime()                (0)
++#define xip_elapsed_since(x)  (0)
 +
-+/*
-  * The APM function codes
-  */
- #define       APM_FUNC_INST_CHECK     0x5300
-@@ -168,6 +180,7 @@
- /*
-  * APM Device IDs
-  */
-+#ifdef _i386_
- #define APM_DEVICE_BIOS               0x0000
- #define APM_DEVICE_ALL                0x0001
- #define APM_DEVICE_DISPLAY    0x0100
-@@ -181,6 +194,21 @@
- #define APM_DEVICE_OLD_ALL    0xffff
- #define APM_DEVICE_CLASS      0x00ff
- #define APM_DEVICE_MASK               0xff00
 +#endif
 +
 +/*
-+ *  APM devices IDs for non-x86
++ * xip_cpu_idle() is used when waiting for a delay equal or larger than
++ * the system timer tick period.  This should put the CPU into idle mode
++ * to save power and to be woken up only when some interrupts are pending.
++ * As above, this should not rely upon standard kernel code.
 + */
-+#define APM_DEVICE_ALL                PM_SYS_DEV
-+#define APM_DEVICE_DISPLAY    PM_DISPLAY_DEV
-+#define APM_DEVICE_STORAGE    PM_STORAGE_DEV
-+#define APM_DEVICE_PARALLEL   PM_PARALLEL_DEV
-+#define APM_DEVICE_SERIAL     PM_SERIAL_DEV
-+#define APM_DEVICE_NETWORK    PM_NETWORK_DEV
-+#define APM_DEVICE_PCMCIA     PM_PCMCIA_DEV
-+#define APM_DEVICE_BATTERY    PM_BATTERY_DEV
-+#define APM_DEVICE_TPANEL     PM_TPANEL_DEV
 +
- #ifdef __KERNEL__
- /*
-@@ -214,5 +242,6 @@
- #define APM_IOC_STANDBY               _IO('A', 1)
- #define APM_IOC_SUSPEND               _IO('A', 2)
-+#define APM_IOC_SET_WAKEUP    _IO('A', 3)
- #endif        /* LINUX_APM_H */
---- linux-2.4.21/include/linux/crc32.h~mtd-cvs
-+++ linux-2.4.21/include/linux/crc32.h
-@@ -46,4 +46,25 @@
-       return crc;
- }
--#endif /* _LINUX_CRC32_H */
-+#ifndef CRC32_H
-+#define CRC32_H
++#if defined(CONFIG_CPU_XSCALE)
++#define xip_cpu_idle()  asm volatile ("mcr p14, 0, %0, c7, c0, 0" :: "r" (1))
++#else
++#define xip_cpu_idle()  do { } while (0)
++#endif
 +
-+/* $Id$ */
++#else
 +
-+#include <linux/types.h>
++#define __xipram
 +
-+extern const uint32_t crc32_table[256];
++#endif /* CONFIG_MTD_XIP */
 +
-+/* Return a 32-bit CRC of the contents of the buffer. */
++#endif /* __LINUX_MTD_XIP_H__ */
+--- linux-2.4.21/include/linux/net.h~bluetooth
++++ linux-2.4.21/include/linux/net.h
+@@ -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);
+--- /dev/null
++++ linux-2.4.21/include/linux/pm-devices.h
+@@ -0,0 +1,41 @@
++#ifndef _LINUX_PM_DEV_H
++#define _LINUX_PM_DEV_H
 +
-+static inline uint32_t 
-+crc32(uint32_t val, const void *ss, int len)
++/*
++ * Copyright 2002 Montavista Software (mlocke@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, 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.
++ */
++
++
++
++/*
++ * Device types
++ */
++enum
 +{
-+      const unsigned char *s = ss;
-+        while (--len >= 0)
-+                val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8);
-+        return val;
-+}
++        PM_UNKNOWN_DEV = 0, /* generic */
++        PM_SYS_DEV,         /* system device (fan, KB controller, ...) */
++        PM_PCI_DEV,         /* PCI device */
++        PM_USB_DEV,         /* USB device */
++        PM_SCSI_DEV,        /* SCSI device */
++        PM_ISA_DEV,         /* ISA device */
++        PM_MTD_DEV,         /* Memory Technology Device */
++      PM_TPANEL_DEV,      /* Memory Technology Device */
++        PM_STORAGE_DEV,      /* Memory Technology Device */
++        PM_NETWORK_DEV,      /* Memory Technology Device */
++        PM_PCMCIA_DEV,      /* Memory Technology Device */
++        PM_DISPLAY_DEV,      /* Memory Technology Device */
++        PM_SERIAL_DEV,      /* Memory Technology Device */
++        PM_BATTERY_DEV,      /* Memory Technology Device */
++};
 +
 +#endif
-+#endif
---- linux-2.4.21/include/linux/fs.h~mtd-cvs
-+++ linux-2.4.21/include/linux/fs.h
-@@ -1376,6 +1376,7 @@
- extern void iput(struct inode *);
- extern void force_delete(struct inode *);
- extern struct inode * igrab(struct inode *);
-+extern struct inode * ilookup(struct super_block *, unsigned long);
- extern ino_t iunique(struct super_block *, ino_t);
- typedef int (*find_inode_t)(struct inode *, unsigned long, void *);
---- linux-2.4.21/include/linux/i2c-id.h~i2c-ds1337
-+++ linux-2.4.21/include/linux/i2c-id.h
-@@ -95,13 +95,14 @@
- #define I2C_DRIVERID_ADV717x  48     /* ADV 7175/7176 video encoder   */
- #define I2C_DRIVERID_ZR36067  49     /* Zoran 36067 video encoder     */
- #define I2C_DRIVERID_ZR36120  50     /* Zoran 36120 video encoder     */
--#define I2C_DRIVERID_24LC32A  51              /* Microchip 24LC32A 32k EEPROM */
-+#define I2C_DRIVERID_24LC32A  51     /* Microchip 24LC32A 32k EEPROM  */
-+#define I2C_DRIVERID_DS1337     52     /* DS1337 real time clock        */
+--- linux-2.4.21/include/linux/pm.h~pm
++++ linux-2.4.21/include/linux/pm.h
+@@ -24,6 +24,7 @@
+ #ifdef __KERNEL__
  
--#define I2C_DRIVERID_DS1307   46      /* real time clock: DS1307      */
--#define I2C_DRIVERID_24LC64   47      /* EEprom 24LC64                */
--#define I2C_DRIVERID_FM24CLB4 48      /* EEprom FM24CLB4              */
-+//#define I2C_DRIVERID_DS1307 46      /* real time clock: DS1307      */
-+//#define I2C_DRIVERID_24LC64 47      /* EEprom 24LC64                */
-+//#define I2C_DRIVERID_FM24CLB4       48      /* EEprom FM24CLB4              */
+ #include <linux/config.h>
++#include <linux/pm-devices.h>
+ #include <linux/list.h>
  
- #define I2C_DRIVERID_EXP0     0xF0    /* experimental use id's        */
- #define I2C_DRIVERID_EXP1     0xF1
---- linux-2.4.21/include/linux/jffs2.h~mtd-cvs
-+++ linux-2.4.21/include/linux/jffs2.h
-@@ -1,50 +1,30 @@
  /*
-  * JFFS2 -- Journalling Flash File System, Version 2.
-  *
-- * Copyright (C) 2001 Red Hat, Inc.
-- *
-- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
-- *
-- * The original JFFS, from which the design for JFFS2 was derived,
-- * was designed and implemented by Axis Communications AB.
-- *
-- * 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.
-+ * Copyright (C) 2001-2003 Red Hat, Inc.
-  *
-- * The Original Code is JFFS2 - Journalling Flash File System, version 2
-+ * Created by David Woodhouse <dwmw2@infradead.org>
-  *
-- * 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.
-+ * For licensing information, see the file 'LICENCE' in the 
-+ * jffs2 directory.
-  *
-- * $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
-@@ -63,6 +43,8 @@
- #define JFFS2_COMPR_COPY      0x04
- #define JFFS2_COMPR_DYNRUBIN  0x05
- #define JFFS2_COMPR_ZLIB      0x06
-+#define JFFS2_COMPR_LZO         0x07
-+#define JFFS2_COMPR_LZARI       0x08
- /* Compatibility flags. */
- #define JFFS2_COMPAT_MASK 0xc000      /* What do to if an unknown nodetype is found */
- #define JFFS2_NODE_ACCURATE 0x2000
-@@ -78,16 +60,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)
+@@ -50,20 +51,6 @@
  
- // 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)
+ typedef int pm_request_t;
  
--/* Same as the non_ECC versions, but with extra space for real 
-- * ECC instead of just the checksum. For use on NAND flash 
+-/*
+- * Device types
 - */
--//#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 +74,46 @@
-                                          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;
-+
- 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 +124,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 {
---- linux-2.4.21/include/linux/jffs2_fs_i.h~mtd-cvs
-+++ linux-2.4.21/include/linux/jffs2_fs_i.h
-@@ -1,22 +1,13 @@
--/* $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>
-+#include <asm/semaphore.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 +17,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 +35,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;
+-enum
+-{
+-      PM_UNKNOWN_DEV = 0, /* generic */
+-      PM_SYS_DEV,         /* system device (fan, KB controller, ...) */
+-      PM_PCI_DEV,         /* PCI device */
+-      PM_USB_DEV,         /* USB device */
+-      PM_SCSI_DEV,        /* SCSI device */
+-      PM_ISA_DEV,         /* ISA device */
+-      PM_MTD_DEV,         /* Memory Technology Device */
 -};
 -
--#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 */
--
---- linux-2.4.21/include/linux/jffs2_fs_sb.h~mtd-cvs
-+++ linux-2.4.21/include/linux/jffs2_fs_sb.h
-@@ -1,18 +1,23 @@
--/* $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
-+#include <linux/rwsem.h>
+ typedef int pm_dev_t;
  
- #define JFFS2_SB_FLAG_RO 1
--#define JFFS2_SB_FLAG_MOUNTING 2
-+#define JFFS2_SB_FLAG_SCANNING 2 /* Flash scanning is in progress */
-+#define JFFS2_SB_FLAG_BUILDING 4 /* File system building is in progress */
+ /*
+--- /dev/null
++++ linux-2.4.21/include/linux/rbtree-24.h
+@@ -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.
 +
-+struct jffs2_inodirty;
- /* A struct for the overall file system control.  Pointers to
-    jffs2_sb_info structs are named `c' in the source code.  
-@@ -21,36 +26,44 @@
- struct jffs2_sb_info {
-       struct mtd_info *mtd;
--      __u32 highest_ino;
-+      uint32_t highest_ino;
-+      uint32_t checked_ino;
++  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.
 +
-       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;
-+                                         out-of-order writing of nodes. And GC. */
-+      uint32_t cleanmarker_size;      /* Size of an _inline_ CLEANMARKER
-+                                       (i.e. zero for OOB CLEANMARKER */
--      __u32 nr_free_blocks;
--      __u32 nr_erasing_blocks;
-+      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;
--      __u32 nr_blocks;
-+      uint32_t nr_free_blocks;
-+      uint32_t nr_erasing_blocks;
++  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
 +
-+      /* 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 */
++  linux/include/linux/rbtree.h
 +
-+      uint32_t nospc_dirty_size;
++  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...
 +
-+      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,35 @@
-       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];
++  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.
 +
-+      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;
++-----------------------------------------------------------------------
++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;
 +
-+#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
-+      /* 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;
++      while (n)
++      {
++              page = rb_entry(n, struct page, rb_page_cache);
 +
-+      struct rw_semaphore wbuf_sem;   /* Protects the write buffer */
++              if (offset < page->offset)
++                      n = n->rb_left;
++              else if (offset > page->offset)
++                      n = n->rb_right;
++              else
++                      return page;
++      }
++      return NULL;
++}
 +
-+      /* 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 */
---- /dev/null
-+++ linux-2.4.21/include/linux/mtd/blktrans.h
-@@ -0,0 +1,72 @@
-+/*
-+ * $Id$
-+ *
-+ * (C) 2003 David Woodhouse <dwmw2@infradead.org>
-+ *
-+ * Interface to Linux block layer for MTD 'translation layers'.
-+ *
-+ */
++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;
 +
-+#ifndef __MTD_TRANS_H__
-+#define __MTD_TRANS_H__
++      while (*p)
++      {
++              parent = *p;
++              page = rb_entry(parent, struct page, rb_page_cache);
 +
-+#include <asm/semaphore.h>
++              if (offset < page->offset)
++                      p = &(*p)->rb_left;
++              else if (offset > page->offset)
++                      p = &(*p)->rb_right;
++              else
++                      return page;
++      }
 +
-+struct hd_geometry;
-+struct mtd_info;
-+struct mtd_blktrans_ops;
-+struct file;
-+struct inode;
++      rb_link_node(node, parent, p);
 +
-+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 */
-+};
++      return NULL;
++}
 +
-+struct blkcore_priv; /* Differs for 2.4 and 2.5 kernels; private */
++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;
++}
++-----------------------------------------------------------------------
++*/
 +
-+struct mtd_blktrans_ops {
-+      char *name;
-+      int major;
-+      int part_bits;
++#ifndef       _LINUX_RBTREE_H
++#define       _LINUX_RBTREE_H
 +
-+      /* 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);
++#include <linux/kernel.h>
++#include <linux/stddef.h>
 +
-+      /* Block layer ioctls */
-+      int (*getgeo)(struct mtd_blktrans_dev *dev, struct hd_geometry *geo);
-+      int (*flush)(struct mtd_blktrans_dev *dev);
++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;
 +
-+      /* 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);
++typedef struct rb_root_s
++{
++      struct rb_node_s * rb_node;
++}
++rb_root_t;
 +
-+      /* 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);
++#define RB_ROOT       (rb_root_t) { NULL, }
++#define       rb_entry(ptr, type, member)                                     \
++      ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
 +
-+      struct list_head devs;
-+      struct list_head list;
-+      struct module *owner;
++extern void rb_insert_color(rb_node_t *, rb_root_t *);
++extern void rb_erase(rb_node_t *, rb_root_t *);
 +
-+      struct mtd_blkcore_priv *blkcore_priv;
-+};
++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;
 +
-+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);
-+                               
++      *rb_link = node;
++}
 +
-+#endif /* __MTD_TRANS_H__ */
---- linux-2.4.21/include/linux/mtd/cfi.h~mtd-cvs
-+++ linux-2.4.21/include/linux/mtd/cfi.h
-@@ -1,211 +1,86 @@
- /* 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>
- #include <linux/mtd/flashchip.h>
-+#include <linux/mtd/map.h>
- #include <linux/mtd/cfi_endian.h>
--/*
-- * You can optimize the code size and performance by defining only 
-- * the geometry(ies) available on your hardware.
-- * CFIDEV_INTERLEAVE_n, where  represents the interleave (number of chips to fill the bus width)
-- * CFIDEV_BUSWIDTH_n, where n is the bus width in bytes (1, 2, 4 or 8 bytes)
-- *
-- * By default, all (known) geometries are supported.
-- */
++#endif        /* _LINUX_RBTREE_H */
+--- linux-2.4.21/include/linux/rbtree.h~mtd-cvs
++++ linux-2.4.21/include/linux/rbtree.h
+@@ -1,133 +1,25 @@
+ /*
+-  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.
 -
--#ifndef CONFIG_MTD_CFI_GEOMETRY
+-  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.
 -
--/* The default case - support all but 64-bit, which has
--   a performance penalty */
+-  You should have received a copy of the GNU General Public License
+-  along with this program; if not, write to the Free Software
+-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 -
--#define CFIDEV_INTERLEAVE_1 (1)
--#define CFIDEV_INTERLEAVE_2 (2)
--#define CFIDEV_INTERLEAVE_4 (4)
+-  linux/include/linux/rbtree.h
 -
--#define CFIDEV_BUSWIDTH_1 (1)
--#define CFIDEV_BUSWIDTH_2 (2)
--#define CFIDEV_BUSWIDTH_4 (4)
+-  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...
 -
--typedef __u32 cfi_word;
+-  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.
 -
--#else
+------------------------------------------------------------------------
+-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;
 -
--/* Explicitly configured buswidth/interleave support */
+-      while (n)
+-      {
+-              page = rb_entry(n, struct page, rb_page_cache);
 -
- #ifdef CONFIG_MTD_CFI_I1
--#define CFIDEV_INTERLEAVE_1 (1)
--#endif
--#ifdef CONFIG_MTD_CFI_I2
--#define CFIDEV_INTERLEAVE_2 (2)
--#endif
--#ifdef CONFIG_MTD_CFI_I4
--#define CFIDEV_INTERLEAVE_4 (4)
--#endif
--#ifdef CONFIG_MTD_CFI_I8
--#define CFIDEV_INTERLEAVE_8 (8)
--#endif
+-              if (offset < page->offset)
+-                      n = n->rb_left;
+-              else if (offset > page->offset)
+-                      n = n->rb_right;
+-              else
+-                      return page;
+-      }
+-      return NULL;
+-}
 -
--#ifdef CONFIG_MTD_CFI_B1
--#define CFIDEV_BUSWIDTH_1 (1)
--#endif
--#ifdef CONFIG_MTD_CFI_B2
--#define CFIDEV_BUSWIDTH_2 (2)
--#endif
--#ifdef CONFIG_MTD_CFI_B4
--#define CFIDEV_BUSWIDTH_4 (4)
--#endif
--#ifdef CONFIG_MTD_CFI_B8
--#define CFIDEV_BUSWIDTH_8 (8)
--#endif
+-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;
 -
--/* pick the largest necessary */
--#ifdef CONFIG_MTD_CFI_B8
--typedef __u64 cfi_word;
+-      while (*p)
+-      {
+-              parent = *p;
+-              page = rb_entry(parent, struct page, rb_page_cache);
 -
--/* This only works if asm/io.h is included first */
--#ifndef __raw_readll
--#define __raw_readll(addr)    (*(volatile __u64 *)(addr))
--#endif
--#ifndef __raw_writell
--#define __raw_writell(v, addr)        (*(volatile __u64 *)(addr) = (v))
--#endif
--#define CFI_WORD_64
--#else  /* CONFIG_MTD_CFI_B8 */
--/* All others can use 32-bits. It's probably more efficient than
--   the smaller types anyway */
--typedef __u32 cfi_word;
--#endif /* CONFIG_MTD_CFI_B8 */
+-              if (offset < page->offset)
+-                      p = &(*p)->rb_left;
+-              else if (offset > page->offset)
+-                      p = &(*p)->rb_right;
+-              else
+-                      return page;
+-      }
 -
--#endif
+-      rb_link_node(node, parent, p);
 -
--/*
-- * The following macros are used to select the code to execute:
-- *   cfi_buswidth_is_*()
-- *   cfi_interleave_is_*()
-- *   [where * is either 1, 2, 4, or 8]
-- * Those macros should be used with 'if' statements.  If only one of few
-- * geometry arrangements are selected, they expand to constants thus allowing
-- * the compiler (most of them being 0) to optimize away all the unneeded code,
-- * while still validating the syntax (which is not possible with embedded 
-- * #if ... #endif constructs).
-- * The exception to this is the 64-bit versions, which need an extension
-- * to the cfi_word type, and cause compiler warnings about shifts being
-- * out of range.
-- */
+-      return NULL;
+-}
 -
--#ifdef CFIDEV_INTERLEAVE_1
--# ifdef CFIDEV_INTERLEAVE
--#  undef CFIDEV_INTERLEAVE
--#  define CFIDEV_INTERLEAVE (cfi->interleave)
--# else
--#  define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_1
--# endif
--# define cfi_interleave_is_1() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_1)
-+#define cfi_interleave(cfi) 1
-+#define cfi_interleave_is_1(cfi) (cfi_interleave(cfi) == 1)
- #else
--# define cfi_interleave_is_1() (0)
-+#define cfi_interleave_is_1(cfi) (0)
- #endif
--#ifdef CFIDEV_INTERLEAVE_2
--# ifdef CFIDEV_INTERLEAVE
--#  undef CFIDEV_INTERLEAVE
--#  define CFIDEV_INTERLEAVE (cfi->interleave)
-+#ifdef CONFIG_MTD_CFI_I2
-+# ifdef cfi_interleave
-+#  undef cfi_interleave
-+#  define cfi_interleave(cfi) ((cfi)->interleave)
- # else
--#  define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_2
-+#  define cfi_interleave(cfi) 2
- # endif
--# define cfi_interleave_is_2() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_2)
-+#define cfi_interleave_is_2(cfi) (cfi_interleave(cfi) == 2)
- #else
--# define cfi_interleave_is_2() (0)
-+#define cfi_interleave_is_2(cfi) (0)
- #endif
--#ifdef CFIDEV_INTERLEAVE_4
--# ifdef CFIDEV_INTERLEAVE
--#  undef CFIDEV_INTERLEAVE
--#  define CFIDEV_INTERLEAVE (cfi->interleave)
-+#ifdef CONFIG_MTD_CFI_I4
-+# ifdef cfi_interleave
-+#  undef cfi_interleave
-+#  define cfi_interleave(cfi) ((cfi)->interleave)
- # else
--#  define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_4
-+#  define cfi_interleave(cfi) 4
- # endif
--# define cfi_interleave_is_4() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_4)
-+#define cfi_interleave_is_4(cfi) (cfi_interleave(cfi) == 4)
- #else
--# define cfi_interleave_is_4() (0)
-+#define cfi_interleave_is_4(cfi) (0)
- #endif
--#ifdef CFIDEV_INTERLEAVE_8
--# ifdef CFIDEV_INTERLEAVE
--#  undef CFIDEV_INTERLEAVE
--#  define CFIDEV_INTERLEAVE (cfi->interleave)
-+#ifdef CONFIG_MTD_CFI_I8
-+# ifdef cfi_interleave
-+#  undef cfi_interleave
-+#  define cfi_interleave(cfi) ((cfi)->interleave)
- # else
--#  define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_8
-+#  define cfi_interleave(cfi) 8
- # endif
--# define cfi_interleave_is_8() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_8)
-+#define cfi_interleave_is_8(cfi) (cfi_interleave(cfi) == 8)
- #else
--# define cfi_interleave_is_8() (0)
-+#define cfi_interleave_is_8(cfi) (0)
- #endif
--#ifndef CFIDEV_INTERLEAVE
--#error You must define at least one interleave to support!
-+static inline int cfi_interleave_supported(int i)
-+{
-+      switch (i) {
-+#ifdef CONFIG_MTD_CFI_I1
-+      case 1:
- #endif
+-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;
+-}
+------------------------------------------------------------------------
+-*/
 -
--#ifdef CFIDEV_BUSWIDTH_1
--# ifdef CFIDEV_BUSWIDTH
--#  undef CFIDEV_BUSWIDTH
--#  define CFIDEV_BUSWIDTH (map->buswidth)
--# else
--#  define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_1
--# endif
--# define cfi_buswidth_is_1() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_1)
--#else
--# define cfi_buswidth_is_1() (0)
-+#ifdef CONFIG_MTD_CFI_I2
-+      case 2:
- #endif
+-#ifndef       _LINUX_RBTREE_H
+-#define       _LINUX_RBTREE_H
 -
--#ifdef CFIDEV_BUSWIDTH_2
--# ifdef CFIDEV_BUSWIDTH
--#  undef CFIDEV_BUSWIDTH
--#  define CFIDEV_BUSWIDTH (map->buswidth)
--# else
--#  define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_2
--# endif
--# define cfi_buswidth_is_2() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_2)
--#else
--# define cfi_buswidth_is_2() (0)
-+#ifdef CONFIG_MTD_CFI_I4
-+      case 4:
- #endif
+-#include <linux/kernel.h>
+-#include <linux/stddef.h>
 -
--#ifdef CFIDEV_BUSWIDTH_4
--# ifdef CFIDEV_BUSWIDTH
--#  undef CFIDEV_BUSWIDTH
--#  define CFIDEV_BUSWIDTH (map->buswidth)
--# else
--#  define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_4
--# endif
--# define cfi_buswidth_is_4() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_4)
--#else
--# define cfi_buswidth_is_4() (0)
-+#ifdef CONFIG_MTD_CFI_I8
-+      case 8:
- #endif
-+              return 1;
--#ifdef CFIDEV_BUSWIDTH_8
--# ifdef CFIDEV_BUSWIDTH
--#  undef CFIDEV_BUSWIDTH
--#  define CFIDEV_BUSWIDTH (map->buswidth)
--# else
--#  define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_8
--# endif
--# define cfi_buswidth_is_8() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_8)
--#else
--# define cfi_buswidth_is_8() (0)
--#endif
-+      default:
-+              return 0;
-+      }
-+}
--#ifndef CFIDEV_BUSWIDTH
--#error You must define at least one bus width to support!
--#endif
+-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;
++ * 2.5 compatibility
++ * $Id$
++ */
  
- /* NB: these values must represents the number of bytes needed to meet the 
-  *     device type (x8, x16, x32).  Eg. a 32 bit device is 4 x 8 bytes. 
-@@ -222,88 +97,138 @@
+-typedef struct rb_root_s
+-{
+-      struct rb_node_s * rb_node;
+-}
+-rb_root_t;
++#ifndef __MTD_COMPAT_RBTREE_H__
++#define __MTD_COMPAT_RBTREE_H__
  
- /* Basic Query Structure */
- struct cfi_ident {
--  __u8  qry[3];
--  __u16 P_ID;
--  __u16 P_ADR;
--  __u16 A_ID;
--  __u16 A_ADR;
--  __u8  VccMin;
--  __u8  VccMax;
--  __u8  VppMin;
--  __u8  VppMax;
--  __u8  WordWriteTimeoutTyp;
--  __u8  BufWriteTimeoutTyp;
--  __u8  BlockEraseTimeoutTyp;
--  __u8  ChipEraseTimeoutTyp;
--  __u8  WordWriteTimeoutMax;
--  __u8  BufWriteTimeoutMax;
--  __u8  BlockEraseTimeoutMax;
--  __u8  ChipEraseTimeoutMax;
--  __u8  DevSize;
--  __u16 InterfaceDesc;
--  __u16 MaxBufWriteSize;
--  __u8  NumEraseRegions;
--  __u32 EraseRegionInfo[0]; /* Not host ordered */
-+      uint8_t  qry[3];
-+      uint16_t P_ID;
-+      uint16_t P_ADR;
-+      uint16_t A_ID;
-+      uint16_t A_ADR;
-+      uint8_t  VccMin;
-+      uint8_t  VccMax;
-+      uint8_t  VppMin;
-+      uint8_t  VppMax;
-+      uint8_t  WordWriteTimeoutTyp;
-+      uint8_t  BufWriteTimeoutTyp;
-+      uint8_t  BlockEraseTimeoutTyp;
-+      uint8_t  ChipEraseTimeoutTyp;
-+      uint8_t  WordWriteTimeoutMax;
-+      uint8_t  BufWriteTimeoutMax;
-+      uint8_t  BlockEraseTimeoutMax;
-+      uint8_t  ChipEraseTimeoutMax;
-+      uint8_t  DevSize;
-+      uint16_t InterfaceDesc;
-+      uint16_t MaxBufWriteSize;
-+      uint8_t  NumEraseRegions;
-+      uint32_t EraseRegionInfo[0]; /* Not host ordered */
- } __attribute__((packed));
+-#define RB_ROOT       (rb_root_t) { NULL, }
+-#define       rb_entry(ptr, type, member)                                     \
+-      ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
++#include <linux/version.h>
  
- /* Extended Query Structure for both PRI and ALT */
+-extern void rb_insert_color(rb_node_t *, rb_root_t *);
+-extern void rb_erase(rb_node_t *, rb_root_t *);
++#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
  
- struct cfi_extquery {
--  __u8  pri[3];
--  __u8  MajorVersion;
--  __u8  MinorVersion;
-+      uint8_t  pri[3];
-+      uint8_t  MajorVersion;
-+      uint8_t  MinorVersion;
- } __attribute__((packed));
+-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;
++#include <linux/rbtree-24.h>
  
- /* Vendor-Specific PRI for Intel/Sharp Extended Command Set (0x0001) */
+-      *rb_link = node;
+-}
++/* 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
  
- struct cfi_pri_intelext {
--  __u8  pri[3];
--  __u8  MajorVersion;
--  __u8  MinorVersion;
--  __u32 FeatureSupport;
--  __u8  SuspendCmdSupport;
--  __u16 BlkStatusRegMask;
--  __u8  VccOptimal;
--  __u8  VppOptimal;
--  __u8  NumProtectionFields;
--  __u16 ProtRegAddr;
--  __u8  FactProtRegSize;
--  __u8  UserProtRegSize;
-+      uint8_t  pri[3];
-+      uint8_t  MajorVersion;
-+      uint8_t  MinorVersion;
-+      uint32_t FeatureSupport; /* if bit 31 is set then an additional uint32_t feature
-+                                  block follows - FIXME - not currently supported */
-+      uint8_t  SuspendCmdSupport;
-+      uint16_t BlkStatusRegMask;
-+      uint8_t  VccOptimal;
-+      uint8_t  VppOptimal;
-+      uint8_t  NumProtectionFields;
-+      uint16_t ProtRegAddr;
-+      uint8_t  FactProtRegSize;
-+      uint8_t  UserProtRegSize;
-+      uint8_t  extra[0];
-+} __attribute__((packed));
+-#endif        /* _LINUX_RBTREE_H */
++#endif /*  __MTD_COMPAT_RBTREE_H__ */
+--- /dev/null
++++ linux-2.4.21/include/linux/rslib.h
+@@ -0,0 +1,105 @@
++/* 
++ * include/linux/rslib.h
++ *
++ * Overview:
++ *   Generic Reed Solomon encoder / decoder library
++ *   
++ * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
++ *
++ * RS code lifted from reed solomon library written by Phil Karn
++ * Copyright 2002 Phil Karn, KA9Q
++ *
++ * $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
++ * published by the Free Software Foundation.
++ */
 +
-+struct cfi_intelext_otpinfo {
-+      uint32_t ProtRegAddr;
-+      uint16_t FactGroups;
-+      uint8_t  FactProtRegSize;
-+      uint16_t UserGroups;
-+      uint8_t  UserProtRegSize;
-+} __attribute__((packed));
++#ifndef _RSLIB_H_
++#define _RSLIB_H_
 +
-+struct cfi_intelext_blockinfo {
-+      uint16_t NumIdentBlocks;
-+      uint16_t BlockSize;
-+      uint16_t MinBlockEraseCycles;
-+      uint8_t  BitsPerCell;
-+      uint8_t  BlockCap;
-+} __attribute__((packed));
++#include <linux/list.h>
 +
-+struct cfi_intelext_regioninfo {
-+      uint16_t NumIdentPartitions;
-+      uint8_t  NumOpAllowed;
-+      uint8_t  NumOpAllowedSimProgMode;
-+      uint8_t  NumOpAllowedSimEraMode;
-+      uint8_t  NumBlockTypes;
-+      struct cfi_intelext_blockinfo BlockTypes[1];
-+} __attribute__((packed));
++/** 
++ * struct rs_control - rs control structure
++ * 
++ * @mm:               Bits per symbol
++ * @nn:               Symbols per block (= (1<<mm)-1)
++ * @alpha_to: log lookup table
++ * @index_of: Antilog lookup table
++ * @genpoly:  Generator polynomial 
++ * @nroots:   Number of generator roots = number of parity symbols
++ * @fcr:      First consecutive root, index form
++ * @prim:     Primitive element, index form 
++ * @iprim:    prim-th root of 1, index form 
++ * @gfpoly:   The primitive generator polynominal 
++ * @users:    Users of this structure 
++ * @list:     List entry for the rs control list
++*/
++struct rs_control {
++      int             mm;
++      int             nn;
++      uint16_t        *alpha_to;
++      uint16_t        *index_of;
++      uint16_t        *genpoly;
++      int             nroots;
++      int             fcr;
++      int             prim;
++      int             iprim;
++      int             gfpoly;
++      int             users;
++      struct list_head list;
++};
 +
-+/* Vendor-Specific PRI for AMD/Fujitsu Extended Command Set (0x0002) */
++/* General purpose RS codec, 8-bit data width, symbol width 1-15 bit  */
++#ifdef CONFIG_REED_SOLOMON_ENC8
++int encode_rs8(struct rs_control *rs, uint8_t *data, int len, uint16_t *par,
++             uint16_t invmsk);
++#endif
++#ifdef CONFIG_REED_SOLOMON_DEC8
++int decode_rs8(struct rs_control *rs, uint8_t *data, uint16_t *par, int len, 
++              uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk,
++             uint16_t *corr);
++#endif
 +
-+struct cfi_pri_amdstd {
-+      uint8_t  pri[3];
-+      uint8_t  MajorVersion;
-+      uint8_t  MinorVersion;
-+      uint8_t  SiliconRevision; /* bits 1-0: Address Sensitive Unlock */
-+      uint8_t  EraseSuspend;
-+      uint8_t  BlkProt;
-+      uint8_t  TmpBlkUnprotect;
-+      uint8_t  BlkProtUnprot;
-+      uint8_t  SimultaneousOps;
-+      uint8_t  BurstMode;
-+      uint8_t  PageMode;
-+      uint8_t  VppMin;
-+      uint8_t  VppMax;
-+      uint8_t  TopBottom;
- } __attribute__((packed));
++/* General purpose RS codec, 16-bit data width, symbol width 1-15 bit  */
++#ifdef CONFIG_REED_SOLOMON_ENC16
++int encode_rs16(struct rs_control *rs, uint16_t *data, int len, uint16_t *par,
++              uint16_t invmsk);
++#endif
++#ifdef CONFIG_REED_SOLOMON_DEC16
++int decode_rs16(struct rs_control *rs, uint16_t *data, uint16_t *par, int len,
++              uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk,
++              uint16_t *corr);
++#endif
++
++/* Create or get a matching rs control structure */
++struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim, 
++                         int nroots);
++
++/* Release a rs control structure */
++void free_rs(struct rs_control *rs);
++
++/** modulo replacement for galois field arithmetics
++ *
++ *  @rs:      the rs control structure
++ *  @x:               the value to reduce
++ *
++ *  where
++ *  rs->mm = number of bits per symbol        
++ *  rs->nn = (2^rs->mm) - 1
++ *  
++ *  Simple arithmetic modulo would return a wrong result for values
++ *  >= 3 * rs->nn
++*/
++static inline int rs_modnn(struct rs_control *rs, int x)
++{
++      while (x >= rs->nn) {
++              x -= rs->nn;
++              x = (x >> rs->mm) + (x & rs->nn);
++      }
++      return x;
++}
++
++#endif
+--- linux-2.4.21/include/linux/soundcard.h~ucb1x00
++++ linux-2.4.21/include/linux/soundcard.h
+@@ -811,6 +811,7 @@
+ #define SOUND_MIXER_STEREODEVS        0xfb    /* Mixer channels supporting stereo */
+ #define SOUND_MIXER_OUTSRC    0xfa    /* Arg contains a bit for each input source to output */
+ #define SOUND_MIXER_OUTMASK   0xf9    /* Arg contains a bit for each supported input source to output */
++#define SOUND_MIXER_AC97      0xf8    /* directly access ac97 registers */
  
- struct cfi_pri_query {
--  __u8  NumFields;
--  __u32 ProtField[1]; /* Not host ordered */
-+      uint8_t  NumFields;
-+      uint32_t ProtField[1]; /* Not host ordered */
- } __attribute__((packed));
+ /*    Device mask bits        */
  
- struct cfi_bri_query {
--  __u8  PageModeReadCap;
--  __u8  NumFields;
--  __u32 ConfField[1]; /* Not host ordered */
-+      uint8_t  PageModeReadCap;
-+      uint8_t  NumFields;
-+      uint32_t ConfField[1]; /* Not host ordered */
- } __attribute__((packed));
+@@ -874,6 +875,7 @@
+ #define SOUND_MIXER_READ_RECMASK      MIXER_READ(SOUND_MIXER_RECMASK)
+ #define SOUND_MIXER_READ_STEREODEVS   MIXER_READ(SOUND_MIXER_STEREODEVS)
+ #define SOUND_MIXER_READ_CAPS         MIXER_READ(SOUND_MIXER_CAPS)
++#define SOUND_MIXER_READ_AC97         MIXER_READ(SOUND_MIXER_AC97)
  
--#define P_ID_NONE 0
--#define P_ID_INTEL_EXT 1
--#define P_ID_AMD_STD 2
--#define P_ID_INTEL_STD 3
--#define P_ID_AMD_EXT 4
--#define P_ID_MITSUBISHI_STD 256
--#define P_ID_MITSUBISHI_EXT 257
--#define P_ID_RESERVED 65535
-+#define P_ID_NONE               0x0000
-+#define P_ID_INTEL_EXT          0x0001
-+#define P_ID_AMD_STD            0x0002
-+#define P_ID_INTEL_STD          0x0003
-+#define P_ID_AMD_EXT            0x0004
-+#define P_ID_WINBOND            0x0006
-+#define P_ID_ST_ADV             0x0020
-+#define P_ID_MITSUBISHI_STD     0x0100
-+#define P_ID_MITSUBISHI_EXT     0x0101
-+#define P_ID_SST_PAGE           0x0102
-+#define P_ID_INTEL_PERFORMANCE  0x0200
-+#define P_ID_INTEL_DATA         0x0210
-+#define P_ID_RESERVED           0xffff
+ #define MIXER_WRITE(dev)              _SIOWR('M', dev, int)
+ #define SOUND_MIXER_WRITE_VOLUME      MIXER_WRITE(SOUND_MIXER_VOLUME)
+@@ -900,6 +902,7 @@
+ #define SOUND_MIXER_WRITE_LOUD                MIXER_WRITE(SOUND_MIXER_LOUD)
  
+ #define SOUND_MIXER_WRITE_RECSRC      MIXER_WRITE(SOUND_MIXER_RECSRC)
++#define SOUND_MIXER_WRITE_AC97                MIXER_WRITE(SOUND_MIXER_AC97)
  
- #define CFI_MODE_CFI  1
- #define CFI_MODE_JEDEC        0
+ typedef struct mixer_info
+ {
+--- /dev/null
++++ linux-2.4.21/include/linux/suspend.h
+@@ -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__ */
+--- linux-2.4.21/include/linux/tty.h~ramses-lcd
++++ linux-2.4.21/include/linux/tty.h
+@@ -10,8 +10,8 @@
+  * resizing).
+  */
+ #define MIN_NR_CONSOLES 1       /* must be at least 1 */
+-#define MAX_NR_CONSOLES       63      /* serial lines start at 64 */
+-#define MAX_NR_USER_CONSOLES 63       /* must be root to allocate above this */
++#define MAX_NR_CONSOLES       3       /* serial lines start at 64 */
++#define MAX_NR_USER_CONSOLES 3        /* must be root to allocate above this */
+               /* Note: the ioctl VT_GETSTATE does not work for
+                  consoles 16 and higher (since it returns a short) */
  
- struct cfi_private {
--      __u16 cmdset;
-+      uint16_t cmdset;
-       void *cmdset_priv;
-       int interleave;
-       int device_type;
-       int cfi_mode;           /* Are we a JEDEC device pretending to be CFI? */
-       int addr_unlock1;
-       int addr_unlock2;
--      int fast_prog;
-       struct mtd_info *(*cmdset_setup)(struct map_info *);
-       struct cfi_ident *cfiq; /* For now only one. We insist that all devs
-                                 must be of the same type. */
-@@ -314,107 +239,81 @@
-       struct flchip chips[0];  /* per-chip data structure for each chip */
- };
+--- /dev/null
++++ linux-2.4.21/include/linux/uinput.h
+@@ -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_ */
+--- linux-2.4.21/include/linux/usb.h~ramses-usb
++++ linux-2.4.21/include/linux/usb.h
+@@ -1079,7 +1079,7 @@
+ void usb_show_string(struct usb_device *dev, char *id, int index);
  
--#define MAX_CFI_CHIPS 8 /* Entirely arbitrary to avoid realloc() */
--
+ #ifdef DEBUG
+-#define dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg)
++#define dbg(format, arg...) printk(__FILE__ ": " format "\n" , ## arg)
+ #else
+ #define dbg(format, arg...) do {} while (0)
+ #endif
+--- linux-2.4.21/include/linux/wireless.h~linux-iw241_we16-6
++++ linux-2.4.21/include/linux/wireless.h
+@@ -1,7 +1,7 @@
  /*
-  * Returns the command address according to the given geometry.
-  */
--static inline __u32 cfi_build_cmd_addr(__u32 cmd_ofs, int interleave, int type)
-+static inline uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs, int interleave, int type)
- {
-       return (cmd_ofs * type) * interleave;
- }
+  * This file define a set of standard wireless extensions
+  *
+- * Version :  15      12.7.02
++ * Version :  16      2.4.03
+  *
+  * Authors :  Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+  * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved.
+@@ -69,6 +69,8 @@
  
- /*
-- * Transforms the CFI command for the given geometry (bus width & interleave.
-+ * Transforms the CFI command for the given geometry (bus width & interleave).
-+ * It looks too long to be inline, but in the common case it should almost all
-+ * get optimised away. 
+ /***************************** INCLUDES *****************************/
++/* To minimise problems in user space, I might remove those headers
++ * at some point. Jean II */
+ #include <linux/types.h>              /* for "caddr_t" et al          */
+ #include <linux/socket.h>             /* for "struct sockaddr" et al  */
+ #include <linux/if.h>                 /* for IFNAMSIZ and co... */
+@@ -80,7 +82,7 @@
+  * (there is some stuff that will be added in the future...)
+  * I just plan to increment with each new version.
   */
--static inline cfi_word cfi_build_cmd(u_char cmd, struct map_info *map, struct cfi_private *cfi)
-+static inline map_word cfi_build_cmd(u_long cmd, struct map_info *map, struct cfi_private *cfi)
- {
--      cfi_word val = 0;
-+      map_word val = { {0} };
-+      int wordwidth, words_per_bus, chip_mode, chips_per_word;
-+      unsigned long onecmd;
-+      int i;
+-#define WIRELESS_EXT  15
++#define WIRELESS_EXT  16
  
--      if (cfi_buswidth_is_1()) {
--              /* 1 x8 device */
--              val = cmd;
--      } else if (cfi_buswidth_is_2()) {
--              if (cfi_interleave_is_1()) {
--                      /* 1 x16 device in x16 mode */
--                      val = cpu_to_cfi16(cmd);
--              } else if (cfi_interleave_is_2()) {
--                      /* 2 (x8, x16 or x32) devices in x8 mode */
--                      val = cpu_to_cfi16((cmd << 8) | cmd);
--              }
--      } else if (cfi_buswidth_is_4()) {
--              if (cfi_interleave_is_1()) {
--                      /* 1 x32 device in x32 mode */
--                      val = cpu_to_cfi32(cmd);
--              } else if (cfi_interleave_is_2()) {
--                      /* 2 x16 device in x16 mode */
--                      val = cpu_to_cfi32((cmd << 16) | cmd);
--              } else if (cfi_interleave_is_4()) {
--                      /* 4 (x8, x16 or x32) devices in x8 mode */
--                      val = (cmd << 16) | cmd;
--                      val = cpu_to_cfi32((val << 8) | val);
--              }
--#ifdef CFI_WORD_64
--      } else if (cfi_buswidth_is_8()) {
--              if (cfi_interleave_is_1()) {
--                      /* 1 x64 device in x64 mode */
--                      val = cpu_to_cfi64(cmd);
--              } else if (cfi_interleave_is_2()) {
--                      /* 2 x32 device in x32 mode */
--                      val = cmd;
--                      val = cpu_to_cfi64((val << 32) | val);
--              } else if (cfi_interleave_is_4()) {
--                      /* 4 (x16, x32 or x64) devices in x16 mode */
--                      val = (cmd << 16) | cmd;
--                      val = cpu_to_cfi64((val << 32) | val);
--              } else if (cfi_interleave_is_8()) {
--                      /* 8 (x8, x16 or x32) devices in x8 mode */
--                      val = (cmd << 8) | cmd;
--                      val = (val << 16) | val;
--                      val = (val << 32) | val;
--                      val = cpu_to_cfi64(val);
--              }
--#endif /* CFI_WORD_64 */
-+      /* We do it this way to give the compiler a fighting chance 
-+         of optimising away all the crap for 'bankwidth' larger than
-+         an unsigned long, in the common case where that support is
-+         disabled */
-+      if (map_bankwidth_is_large(map)) {
-+              wordwidth = sizeof(unsigned long);
-+              words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1
-+      } else {
-+              wordwidth = map_bankwidth(map);
-+              words_per_bus = 1;
-       }
--      return val;
--}
--#define CMD(x)  cfi_build_cmd((x), map, cfi)
+ /*
+  * Changes :
+@@ -163,6 +165,16 @@
+  *    - Add IW_TXPOW_RANGE for range of Tx Powers
+  *    - Add IWEVREGISTERED & IWEVEXPIRED events for Access Points
+  *    - Add IW_MODE_MONITOR for passive monitor
++ *
++ * V15 to V16
++ * ----------
++ *    - Increase the number of bitrates in iw_range to 32 (for 802.11g)
++ *    - Increase the number of frequencies in iw_range to 32 (for 802.11b+a)
++ *    - Reshuffle struct iw_range for increases, add filler
++ *    - Increase IW_MAX_AP to 64 for driver returning a lot of addresses
++ *    - Remove IW_MAX_GET_SPY because conflict with enhanced spy support
++ *    - Add SIOCSIWTHRSPY/SIOCGIWTHRSPY and "struct iw_thrspy"
++ *    - Add IW_ENCODE_TEMP and iw_range->encoding_login_index
+  */
  
--/*
-- * Read a value according to the bus width.
-- */
-+      chip_mode = map_bankwidth(map) / cfi_interleave(cfi);
-+      chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map);
+ /**************************** CONSTANTS ****************************/
+@@ -196,9 +208,11 @@
+ /* SIOCGIWSTATS is strictly used between user space and the kernel, and
+  * is never passed to the driver (i.e. the driver will never see it). */
  
--static inline cfi_word cfi_read(struct map_info *map, __u32 addr)
--{
--      if (cfi_buswidth_is_1()) {
--              return map->read8(map, addr);
--      } else if (cfi_buswidth_is_2()) {
--              return map->read16(map, addr);
--      } else if (cfi_buswidth_is_4()) {
--              return map->read32(map, addr);
--      } else if (cfi_buswidth_is_8()) {
--              return map->read64(map, addr);
--      } else {
--              return 0;
-+      /* First, determine what the bit-pattern should be for a single
-+         device, according to chip mode and endianness... */
-+      switch (chip_mode) {
-+      default: BUG();
-+      case 1:
-+              onecmd = cmd;
-+              break;
-+      case 2:
-+              onecmd = cpu_to_cfi16(cmd);
-+              break;
-+      case 4:
-+              onecmd = cpu_to_cfi32(cmd);
-+              break;
-       }
--}
+-/* Mobile IP support (statistics per MAC address) */
++/* Spy support (statistics per MAC address - used for Mobile IP support) */
+ #define SIOCSIWSPY    0x8B10          /* set spy addresses */
+ #define SIOCGIWSPY    0x8B11          /* get spy info (quality of link) */
++#define SIOCSIWTHRSPY 0x8B12          /* set spy threshold (spy event) */
++#define SIOCGIWTHRSPY 0x8B13          /* get spy threshold */
  
--/*
-- * Write a value according to the bus width.
-- */
-+      /* Now replicate it across the size of an unsigned long, or 
-+         just to the bus width as appropriate */
-+      switch (chips_per_word) {
-+      default: BUG();
-+#if BITS_PER_LONG >= 64
-+      case 8:
-+              onecmd |= (onecmd << (chip_mode * 32));
-+#endif
-+      case 4:
-+              onecmd |= (onecmd << (chip_mode * 16));
-+      case 2:
-+              onecmd |= (onecmd << (chip_mode * 8));
-+      case 1:
-+              ;
-+      }
+ /* Access Point manipulation */
+ #define SIOCSIWAP     0x8B14          /* set access point MAC addresses */
+@@ -294,7 +308,7 @@
+ #define IW_PRIV_TYPE_FLOAT    0x5000  /* struct iw_freq */
+ #define IW_PRIV_TYPE_ADDR     0x6000  /* struct sockaddr */
  
--static inline void cfi_write(struct map_info *map, cfi_word val, __u32 addr)
--{
--      if (cfi_buswidth_is_1()) {
--              map->write8(map, val, addr);
--      } else if (cfi_buswidth_is_2()) {
--              map->write16(map, val, addr);
--      } else if (cfi_buswidth_is_4()) {
--              map->write32(map, val, addr);
--      } else if (cfi_buswidth_is_8()) {
--              map->write64(map, val, addr);
-+      /* And finally, for the multi-word case, replicate it 
-+         in all words in the structure */
-+      for (i=0; i < words_per_bus; i++) {
-+              val.x[i] = onecmd;
-       }
-+
-+      return val;
- }
-+#define CMD(x)  cfi_build_cmd((x), map, cfi)
+-#define IW_PRIV_SIZE_FIXED    0x0800  /* Variable or fixed nuber of args */
++#define IW_PRIV_SIZE_FIXED    0x0800  /* Variable or fixed number of args */
+ #define IW_PRIV_SIZE_MASK     0x07FF  /* Max number of those args */
+@@ -306,13 +320,13 @@
+ /* ----------------------- OTHER CONSTANTS ----------------------- */
+ /* Maximum frequencies in the range struct */
+-#define IW_MAX_FREQUENCIES    16
++#define IW_MAX_FREQUENCIES    32
+ /* Note : if you have something like 80 frequencies,
+  * don't increase this constant and don't fill the frequency list.
+  * The user will be able to set by channel anyway... */
+ /* Maximum bit rates in the range struct */
+-#define IW_MAX_BITRATES               8
++#define IW_MAX_BITRATES               32
+ /* Maximum tx powers in the range struct */
+ #define IW_MAX_TXPOWER                8
+@@ -320,8 +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       /* set */
+-#define IW_MAX_GET_SPY                64      /* get */
++#define IW_MAX_SPY            8
+ /* Maximum of address that you may get in the
+    list of access points in range */
+@@ -354,7 +367,8 @@
+ #define IW_ENCODE_ENABLED     0x0000  /* Encoding enabled */
+ #define IW_ENCODE_RESTRICTED  0x4000  /* Refuse non-encoded packets */
+ #define IW_ENCODE_OPEN                0x2000  /* Accept non-encoded packets */
+-#define IW_ENCODE_NOKEY         0x0800  /* Key is write only, so not present */
++#define IW_ENCODE_NOKEY               0x0800  /* Key is write only, so not present */
++#define IW_ENCODE_TEMP                0x0400  /* Temporary key */
+ /* Power management flags available (along with the value, if any) */
+ #define IW_POWER_ON           0x0000  /* No details... */
+@@ -482,6 +496,17 @@
+       __u32           beacon;         /* Missed beacons/superframe */
+ };
  
++/*
++ *    Quality range (for spy threshold)
++ */
++struct        iw_thrspy
++{
++      struct sockaddr         addr;           /* Source address (hw/mac) */
++      struct iw_quality       qual;           /* Quality of the link */
++      struct iw_quality       low;            /* Low threshold */
++      struct iw_quality       high;           /* High threshold */
++};
++
+ /* ------------------------ WIRELESS STATS ------------------------ */
  /*
-  * Sends a CFI command to a bank of flash for the given geometry.
-@@ -423,50 +322,47 @@
-  * If prev_val is non-null, it will be set to the value at the command address,
-  * before the command was written.
-  */
--static inline __u32 cfi_send_gen_cmd(u_char cmd, __u32 cmd_addr, __u32 base,
-+static inline uint32_t cfi_send_gen_cmd(u_char cmd, uint32_t cmd_addr, uint32_t base,
-                               struct map_info *map, struct cfi_private *cfi,
--                              int type, cfi_word *prev_val)
-+                              int type, map_word *prev_val)
- {
--      cfi_word val;
--      __u32 addr = base + cfi_build_cmd_addr(cmd_addr, CFIDEV_INTERLEAVE, type);
-+      map_word val;
-+      uint32_t addr = base + cfi_build_cmd_addr(cmd_addr, cfi_interleave(cfi), type);
+  * Wireless statistics (used for /proc/net/wireless)
+@@ -534,7 +559,7 @@
+       struct iw_quality qual;         /* Quality part of statistics */
  
-       val = cfi_build_cmd(cmd, map, cfi);
+       struct sockaddr ap_addr;        /* Access point address */
+-      struct sockaddr addr;           /* Destination address (hw) */
++      struct sockaddr addr;           /* Destination address (hw/mac) */
  
-       if (prev_val)
--              *prev_val = cfi_read(map, addr);
-+              *prev_val = map_read(map, addr);
+       struct iw_param param;          /* Other small parameters */
+       struct iw_point data;           /* Other large parameters */
+@@ -582,17 +607,31 @@
+       __u32           min_nwid;       /* Minimal NWID we are able to set */
+       __u32           max_nwid;       /* Maximal NWID we are able to set */
  
--      cfi_write(map, val, addr);
-+      map_write(map, val, addr);
+-      /* Frequency */
+-      __u16           num_channels;   /* Number of channels [0; num - 1] */
+-      __u8            num_frequency;  /* Number of entry in the list */
+-      struct iw_freq  freq[IW_MAX_FREQUENCIES];       /* list */
+-      /* Note : this frequency list doesn't need to fit channel numbers */
++      /* Old Frequency (backward compat - moved lower ) */
++      __u16           old_num_channels;
++      __u8            old_num_frequency;
++      /* Filler to keep "version" at the same offset */
++      __s32           old_freq[6];
  
-       return addr - base;
- }
+       /* signal level threshold range */
+       __s32   sensitivity;
  
--static inline __u8 cfi_read_query(struct map_info *map, __u32 addr)
-+static inline uint8_t cfi_read_query(struct map_info *map, uint32_t addr)
- {
--      if (cfi_buswidth_is_1()) {
--              return map->read8(map, addr);
--      } else if (cfi_buswidth_is_2()) {
--              return cfi16_to_cpu(map->read16(map, addr));
--      } else if (cfi_buswidth_is_4()) {
--              return cfi32_to_cpu(map->read32(map, addr));
--      } else if (cfi_buswidth_is_8()) {
--              return cfi64_to_cpu(map->read64(map, addr));
-+      map_word val = map_read(map, addr);
+       /* Quality of link & SNR stuff */
++      /* Quality range (link, level, noise)
++       * If the quality is absolute, it will be in the range [0 ; max_qual],
++       * if the quality is dBm, it will be in the range [max_qual ; 0].
++       * Don't forget that we use 8 bit arithmetics... */
+       struct iw_quality       max_qual;       /* Quality of the link */
++      /* This should contain the average/typical values of the quality
++       * indicator. This should be the threshold between a "good" and
++       * a "bad" link (example : monitor going from green to orange).
++       * Currently, user space apps like quality monitors don't have any
++       * way to calibrate the measurement. With this, they can split
++       * the range between 0 and max_qual in different quality level
++       * (using a geometric subdivision centered on the average).
++       * I expect that people doing the user space apps will feedback
++       * us on which value we need to put in each driver... */
++      struct iw_quality       avg_qual;       /* Quality of the link */
+       /* Rates */
+       __u8            num_bitrates;   /* Number of entries in the list */
+@@ -619,6 +658,8 @@
+       __u16   encoding_size[IW_MAX_ENCODING_SIZES];   /* Different token sizes */
+       __u8    num_encoding_sizes;     /* Number of entry in the list */
+       __u8    max_encoding_tokens;    /* Max number of tokens */
++      /* For drivers that need a "login/passwd" form */
++      __u8    encoding_login_index;   /* token index for login token */
+       /* Transmit power */
+       __u16           txpower_capa;   /* What options are supported */
+@@ -638,18 +679,12 @@
+       __s32           min_r_time;     /* Minimal retry lifetime */
+       __s32           max_r_time;     /* Maximal retry lifetime */
+-      /* Average quality of link & SNR */
+-      struct iw_quality       avg_qual;       /* Quality of the link */
+-      /* This should contain the average/typical values of the quality
+-       * indicator. This should be the threshold between a "good" and
+-       * a "bad" link (example : monitor going from green to orange).
+-       * Currently, user space apps like quality monitors don't have any
+-       * way to calibrate the measurement. With this, they can split
+-       * the range between 0 and max_qual in different quality level
+-       * (using a geometric subdivision centered on the average).
+-       * I expect that people doing the user space apps will feedback
+-       * us on which value we need to put in each driver...
+-       */
++      /* Frequency */
++      __u16           num_channels;   /* Number of channels [0; num - 1] */
++      __u8            num_frequency;  /* Number of entry in the list */
++      struct iw_freq  freq[IW_MAX_FREQUENCIES];       /* list */
++      /* Note : this frequency list doesn't need to fit channel numbers,
++       * because each entry contain its channel index */
+ };
+ /*
+--- /dev/null
++++ linux-2.4.21/include/linux/workqueue.h
+@@ -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__ */
+--- /dev/null
++++ linux-2.4.21/include/mtd/inftl-user.h
+@@ -0,0 +1,91 @@
++/*
++ * $Id$
++ *
++ * Parts of INFTL headers shared with userspace 
++ *
++ */
++
++#ifndef __MTD_INFTL_USER_H__
++#define __MTD_INFTL_USER_H__
++
++#define       OSAK_VERSION    0x5120
++#define       PERCENTUSED     98
++
++#define       SECTORSIZE      512
++
++/* Block Control Information */
++
++struct inftl_bci {
++      uint8_t ECCsig[6];
++      uint8_t Status;
++      uint8_t Status1;
++} __attribute__((packed));
++
++struct inftl_unithead1 {
++      uint16_t virtualUnitNo;
++      uint16_t prevUnitNo;
++      uint8_t ANAC;
++      uint8_t NACs;
++      uint8_t parityPerField;
++      uint8_t discarded;
++} __attribute__((packed));
++
++struct inftl_unithead2 {
++      uint8_t parityPerField;
++      uint8_t ANAC;
++      uint16_t prevUnitNo;
++      uint16_t virtualUnitNo;
++      uint8_t NACs;
++      uint8_t discarded;
++} __attribute__((packed));
++
++struct inftl_unittail {
++      uint8_t Reserved[4];
++      uint16_t EraseMark;
++      uint16_t 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
++
++#endif /* __MTD_INFTL_USER_H__ */
++
++
+--- /dev/null
++++ linux-2.4.21/include/mtd/jffs2-user.h
+@@ -0,0 +1,35 @@
++/*
++ * $Id$
++ *
++ * JFFS2 definitions for use in user space only
++ */
++
++#ifndef __JFFS2_USER_H__
++#define __JFFS2_USER_H__
++
++/* This file is blessed for inclusion by userspace */
++#include <linux/jffs2.h>
++#include <endian.h>
++#include <byteswap.h>
++
++#undef cpu_to_je16
++#undef cpu_to_je32
++#undef cpu_to_jemode
++#undef je16_to_cpu
++#undef je32_to_cpu
++#undef jemode_to_cpu
++
++extern int target_endian;
++
++#define t16(x) ({ uint16_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_16(__b); })
++#define t32(x) ({ uint32_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_32(__b); })
++
++#define cpu_to_je16(x) ((jint16_t){t16(x)})
++#define cpu_to_je32(x) ((jint32_t){t32(x)})
++#define cpu_to_jemode(x) ((jmode_t){t32(x)})
++
++#define je16_to_cpu(x) (t16((x).v16))
++#define je32_to_cpu(x) (t32((x).v32))
++#define jemode_to_cpu(x) (t32((x).m))
++
++#endif /* __JFFS2_USER_H__ */
+--- /dev/null
++++ linux-2.4.21/include/mtd/mtd-abi.h
+@@ -0,0 +1,119 @@
++/*
++ * $Id$
++ *
++ * Portions of MTD ABI definition which are shared by kernel and user space 
++ */
++
++#ifndef __MTD_ABI_H__
++#define __MTD_ABI_H__
++
++#ifndef __KERNEL__ /* Urgh. The whole point of splitting this out into
++                  separate files was to avoid #ifdef __KERNEL__ */
++#define __user
++#endif
++
++struct erase_info_user {
++      uint32_t start;
++      uint32_t length;
++};
++
++struct mtd_oob_buf {
++      uint32_t start;
++      uint32_t length;
++      unsigned char __user *ptr;
++};
++
++#define MTD_ABSENT            0
++#define MTD_RAM                       1
++#define MTD_ROM                       2
++#define MTD_NORFLASH          3
++#define MTD_NANDFLASH         4
++#define MTD_PEROM             5
++#define MTD_DATAFLASH         6
++#define MTD_OTHER             14
++#define MTD_UNKNOWN           15
++
++#define MTD_CLEAR_BITS                1       // Bits can be cleared (flash)
++#define MTD_SET_BITS          2       // Bits can be set
++#define MTD_ERASEABLE         4       // Has an erase function
++#define MTD_WRITEB_WRITEABLE  8       // Direct IO is possible
++#define MTD_VOLATILE          16      // Set for RAMs
++#define MTD_XIP                       32      // eXecute-In-Place possible
++#define MTD_OOB                       64      // Out-of-band data (NAND flash)
++#define MTD_ECC                       128     // Device capable of automatic ECC
++#define MTD_NO_VIRTBLOCKS     256     // Virtual blocks not allowed
++
++// Some common devices / combinations of capabilities
++#define MTD_CAP_ROM           0
++#define MTD_CAP_RAM           (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE)
++#define MTD_CAP_NORFLASH        (MTD_CLEAR_BITS|MTD_ERASEABLE)
++#define MTD_CAP_NANDFLASH       (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB)
++#define MTD_WRITEABLE         (MTD_CLEAR_BITS|MTD_SET_BITS)
++
++
++// Types of automatic ECC/Checksum available
++#define MTD_ECC_NONE          0       // No automatic ECC available
++#define MTD_ECC_RS_DiskOnChip 1       // Automatic ECC on DiskOnChip
++#define MTD_ECC_SW            2       // SW ECC for Toshiba & Samsung devices
++
++/* ECC byte placement */
++#define MTD_NANDECC_OFF               0       // Switch off ECC (Not recommended)
++#define MTD_NANDECC_PLACE     1       // Use the given placement in the structure (YAFFS1 legacy mode)
++#define MTD_NANDECC_AUTOPLACE 2       // Use the default placement scheme
++#define MTD_NANDECC_PLACEONLY 3       // Use the given placement in the structure (Do not store ecc result on read)
++
++/* OTP mode selection */
++#define MTD_OTP_OFF           0
++#define MTD_OTP_FACTORY               1
++#define MTD_OTP_USER          2
++
++struct mtd_info_user {
++      uint8_t type;
++      uint32_t flags;
++      uint32_t size;   // Total size of the MTD
++      uint32_t erasesize;
++      uint32_t oobblock;  // Size of OOB blocks (e.g. 512)
++      uint32_t oobsize;   // Amount of OOB data per block (e.g. 16)
++      uint32_t ecctype;
++      uint32_t eccsize;
++};
++
++struct region_info_user {
++      uint32_t offset;                /* At which this region starts, 
++                                       * from the beginning of the MTD */
++      uint32_t erasesize;             /* For this region */
++      uint32_t numblocks;             /* Number of blocks in this region */
++      uint32_t regionindex;
++};
++
++struct otp_info {
++      uint32_t start;
++      uint32_t length;
++      uint32_t locked;
++};
 +
-+      if (map_bankwidth_is_1(map)) {
-+              return val.x[0];
-+      } else if (map_bankwidth_is_2(map)) {
-+              return cfi16_to_cpu(val.x[0]);
-       } else {
--              return 0;
-+              /* No point in a 64-bit byteswap since that would just be
-+                 swapping the responses from different chips, and we are
-+                 only interested in one chip (a representative sample) */
-+              return cfi32_to_cpu(val.x[0]);
-       }
- }
- static inline void cfi_udelay(int us)
- {
--#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
--      unsigned long t = us * HZ / 1000000;
--      if (t) {
--              set_current_state(TASK_UNINTERRUPTIBLE);
--              schedule_timeout(t);
--              return;
--      }
--#endif
-+      if (us >= 1000) {
-+              msleep((us+999)/1000);
-+      } else {
-       udelay(us);
-       cond_resched();
-+      }
- }
- static inline void cfi_spin_lock(spinlock_t *mutex)
-@@ -479,5 +375,28 @@
-       spin_unlock_bh(mutex);
- }
-+struct cfi_extquery *cfi_read_pri(struct map_info *map, uint16_t adr, uint16_t size,
-+                           const char* name);
-+struct cfi_fixup {
-+      uint16_t mfr;
-+      uint16_t id;
-+      void (*fixup)(struct mtd_info *mtd, void* param);
-+      void* param;
++#define MEMGETINFO              _IOR('M', 1, struct mtd_info_user)
++#define MEMERASE                _IOW('M', 2, struct erase_info_user)
++#define MEMWRITEOOB             _IOWR('M', 3, struct mtd_oob_buf)
++#define MEMREADOOB              _IOWR('M', 4, struct mtd_oob_buf)
++#define MEMLOCK                 _IOW('M', 5, struct erase_info_user)
++#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 MEMSETOOBSEL          _IOW('M', 9, struct nand_oobinfo)
++#define MEMGETOOBSEL          _IOR('M', 10, struct nand_oobinfo)
++#define MEMGETBADBLOCK                _IOW('M', 11, loff_t)
++#define MEMSETBADBLOCK                _IOW('M', 12, loff_t)
++#define OTPSELECT             _IOR('M', 13, int)
++#define OTPGETREGIONCOUNT     _IOW('M', 14, int)
++#define OTPGETREGIONINFO      _IOW('M', 15, struct otp_info)
++#define OTPLOCK               _IOR('M', 16, struct otp_info)
++
++struct nand_oobinfo {
++      uint32_t useecc;
++      uint32_t eccbytes;
++      uint32_t oobfree[8][2];
++      uint32_t eccpos[32];
 +};
 +
-+#define CFI_MFR_ANY 0xffff
-+#define CFI_ID_ANY  0xffff
++#endif /* __MTD_ABI_H__ */
+--- /dev/null
++++ linux-2.4.21/include/mtd/mtd-user.h
+@@ -0,0 +1,20 @@
++/*
++ * $Id$
++ *
++ * MTD ABI header for use by user space only.
++ */
 +
-+#define CFI_MFR_AMD 0x0001
-+#define CFI_MFR_ST  0x0020    /* STMicroelectronics */
++#ifndef __MTD_USER_H__
++#define __MTD_USER_H__
 +
-+void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup* fixups);
++#include <stdint.h>
 +
-+typedef int (*varsize_frob_t)(struct map_info *map, struct flchip *chip,
-+                            unsigned long adr, int len, void *thunk);
++/* This file is blessed for inclusion by userspace */
++#include <mtd/mtd-abi.h>
 +
-+int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
-+      loff_t ofs, size_t len, void *thunk);
++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;
 +
- #endif /* __MTD_CFI_H__ */
---- linux-2.4.21/include/linux/mtd/compatmac.h~mtd-cvs
-+++ linux-2.4.21/include/linux/mtd/compatmac.h
-@@ -1,583 +1,223 @@
--
- /*
-- * mtd/include/compatmac.h
-- *
-- * $Id$
++#endif /* __MTD_USER_H__ */
+--- /dev/null
++++ linux-2.4.21/include/mtd/nftl-user.h
+@@ -0,0 +1,76 @@
++/*
 + * $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 */
--#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__
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10)
-+#error "This kernel is too old: not supported by this file"
- #endif
--/* 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
-+      /* O(1) scheduler stuff. */
--/* changed the prototype of read/write */
-+#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 ()
--#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
-+#define set_user_nice(tsk, n) do { (tsk)->nice = n; } while(0)
- #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
--
--
--/* 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
-+      daemonize();
--#ifndef THIS_MODULE
--#ifdef MODULE
--#define THIS_MODULE (&__this_module)
--#else
--#define THIS_MODULE (NULL)
--#endif
--#endif
-+      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)
++ *
++ * Parts of NFTL headers shared with userspace 
++ *
++ */
++
++#ifndef __MTD_NFTL_USER_H__
++#define __MTD_NFTL_USER_H__
++
++/* Block Control Information */
++
++struct nftl_bci {
++      unsigned char ECCSig[6];
++      uint8_t Status;
++      uint8_t Status1;
++}__attribute__((packed));
++
++/* Unit Control Information */
++
++struct nftl_uci0 {
++      uint16_t VirtUnitNum;
++      uint16_t ReplUnitNum;
++      uint16_t SpareVirtUnitNum;
++      uint16_t SpareReplUnitNum;
++} __attribute__((packed));
++
++struct nftl_uci1 {
++      uint32_t WearInfo;
++      uint16_t EraseMark;
++      uint16_t EraseMark1;
++} __attribute__((packed));
++
++struct nftl_uci2 {
++        uint16_t FoldMark;
++        uint16_t FoldMark1;
++      uint32_t unused;
++} __attribute__((packed));
++
++union nftl_uci {
++      struct nftl_uci0 a;
++      struct nftl_uci1 b;
++      struct nftl_uci2 c;
++};
++
++struct nftl_oob {
++      struct nftl_bci b;
++      union nftl_uci u;
++};
++
++/* NFTL Media Header */
++
++struct NFTLMediaHeader {
++      char DataOrgID[6];
++      uint16_t NumEraseUnits;
++      uint16_t FirstPhysicalEUN;
++      uint32_t FormattedSize;
++      unsigned char UnitSizeFactor;
++} __attribute__((packed));
++
++#define MAX_ERASE_ZONES (8192 - 512)
++
++#define ERASE_MARK 0x3c69
++#define SECTOR_FREE 0xff
++#define SECTOR_USED 0x55
++#define SECTOR_IGNORE 0x11
++#define SECTOR_DELETED 0x00
++
++#define FOLD_MARK_IN_PROGRESS 0x5555
++
++#define ZONE_GOOD 0xff
++#define ZONE_BAD_ORIGINAL 0
++#define ZONE_BAD_MARKED 7
++
++
++#endif /* __MTD_NFTL_USER_H__ */
+--- linux-2.4.21/include/net/bluetooth/bluetooth.h~bluetooth
++++ linux-2.4.21/include/net/bluetooth/bluetooth.h
+@@ -51,6 +51,8 @@
+ #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
+@@ -155,7 +157,7 @@
+ 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_w4_connect(struct sock *sk, int flags);
++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);
+--- linux-2.4.21/include/net/bluetooth/hci.h~bluetooth
++++ linux-2.4.21/include/net/bluetooth/hci.h
+@@ -50,6 +50,11 @@
+ #define HCI_RS232     4
+ #define HCI_PCI               5
++/* HCI device quirks */
++enum {
++      HCI_QUIRK_RESET_ON_INIT
++};
++
+ /* HCI device flags */
+ enum {
+       HCI_UP,
+@@ -160,6 +165,7 @@
+ #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 */
+@@ -333,6 +339,8 @@
+ } __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 {
+@@ -459,6 +467,17 @@
+ } __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;
+--- linux-2.4.21/include/net/bluetooth/hci_core.h~bluetooth
++++ linux-2.4.21/include/net/bluetooth/hci_core.h
+@@ -72,7 +72,9 @@
+       __u16           pkt_type;
+       __u16           link_policy;
+       __u16           link_mode;
+-      
++
++      unsigned long   quirks;
++
+       atomic_t        cmd_cnt;
+       unsigned int    acl_cnt;
+       unsigned int    sco_cnt;
+@@ -167,6 +169,12 @@
+       c->list = NULL;
+ }
  
--#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
-+static inline int dequeue_signal_lock(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
++static inline int inquiry_cache_empty(struct hci_dev *hdev)
 +{
-+      unsigned long flags;
-+      unsigned long ret;
--#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)
--#define set_current_state(state_value)                        \
--        do { current->state = (state_value); } while (0)
--#endif
-+      spin_lock_irqsave(&current->sigmask_lock, flags);
-+      ret = dequeue_signal(mask, info);
-+      spin_unlock_irqrestore(&current->sigmask_lock, flags);
--#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0) 
--static inline int invalidate_device(kdev_t dev, int do_sync) {
-+      return ret;
++      struct inquiry_cache *c = &hdev->inq_cache;
++      return (c->list == NULL);
 +}
--      if (do_sync)
--              fsync_dev(dev);
-+static inline int allow_signal(int sig)
-+{
-+      if (sig < 1 || sig > _NSIG)
-+              return -EINVAL;
-       
--      invalidate_buffers(dev);
-+        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;
- }
--#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);
-+static inline int disallow_signal(int sig)
-+{
-+      if (sig < 1 || sig > _NSIG)
-+              return -EINVAL;
-       
--      if (sb)
--              res = invalidate_inodes(sb);
-+      spin_lock_irq(&current->sigmask_lock);
-+      sigaddset(&current->blocked, sig);
-+      recalc_sigpending();
--      invalidate_buffers(dev);
--      return res;
-+      current->sig->action[sig-1].sa.sa_handler = SIG_DFL;
-+      spin_unlock_irq(&current->sigmask_lock);
-+      return 0;
- }
 +
-+#define PF_FREEZE 0
-+#define refrigerator(x) do { ; } while(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; })
-+      /* Module bits */
--#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; })
-+#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,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);
-+      /* 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,4,9)
--/* This came later */
--#define complete_and_exit(c, r) do { complete(c); do_exit(r); } while(0)
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,12)
-+#define PageUptodate(x) Page_Uptodate(x)
- #endif
--#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9) || \
--    (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) && !defined(__rh_config_h__))
--
--#include <linux/genhd.h>
--
--static inline void add_gendisk(struct gendisk *gp)
--{
--      gp->next = gendisk_head;
--      gendisk_head = gp;
--}
--
--static inline void del_gendisk(struct gendisk *gp)
--{
--      struct gendisk *gd, **gdp;
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48)
-+#define get_seconds() CURRENT_TIME
-+#endif
--      for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
--              if (*gdp == gp) {
--                      gd = *gdp; *gdp = gd->next;
--                      break;
--              }
--}
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,53)
-+#define generic_file_readonly_mmap generic_file_mmap
- #endif
--#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) && defined(MODULE)
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,70)
--#define module_init(func)             \
--mod_init_t init_module(void) {                \
--      return func();                  \
--}
-+#include <linux/kmod.h>
-+#include <linux/string.h>
--#define module_exit(func)             \
--mod_exit_t cleanup_module(void) {     \
--      return func();                  \
-+static inline char *strlcpy(char *dest, const char *src, int len)
-+{
-+      dest[len-1] = 0;
-+      return strncpy(dest, src, len-1);
- }
--#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)
-+static inline int do_old_request_module(const char *mod)
+ static inline long inquiry_cache_age(struct hci_dev *hdev)
  {
--      recalc_sigpending(current);
-+      return request_module(mod);
+       struct inquiry_cache *c = &hdev->inq_cache;
+@@ -282,10 +290,12 @@
+ static inline void hci_conn_put(struct hci_conn *conn)
+ {
+       if (atomic_dec_and_test(&conn->refcnt)) {
+-              if (conn->type == SCO_LINK)
++              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);
+-              else if (conn->out)
+-                      hci_conn_set_timer(conn, HCI_DISCONN_TIMEOUT);
+       }
  }
--#undef recalc_sigpending
--#define recalc_sigpending() __recalc_sigpending ()
--#endif
--
--#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5)
--#define parent_ino(d) ((d)->d_parent->d_inode->i_ino)
--#endif
-+#undef request_module
-+#define request_module(fmt, ...) \
-+ ({ char modname[32]; snprintf(modname, 31, fmt ,##__VA_ARGS__); do_old_request_module(modname); })
  
--#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
-+#endif /* 2.5.70 */
+--- linux-2.4.21/include/net/bluetooth/l2cap.h~bluetooth
++++ linux-2.4.21/include/net/bluetooth/l2cap.h
+@@ -60,6 +60,7 @@
+ #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 {
+@@ -189,6 +190,14 @@
+ } __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;
+@@ -229,6 +238,7 @@
+       __u32           link_mode;
+       __u8            conf_state;
++      __u8            conf_retry;
+       __u16           conf_mtu;
+       __u8            ident;
+@@ -238,8 +248,11 @@
+       struct sock             *prev_c;
+ };
  
--#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
-+#ifndef container_of
-+#define container_of(ptr, type, member) ({                 \
-+      const typeof( ((type *)0)->member ) *__mptr = (ptr); \
-+      (type *)( (char *)__mptr - offsetof(type,member) );})
- #endif
+-#define CONF_REQ_SENT    0x01
+-#define CONF_INPUT_DONE  0x02
+-#define CONF_OUTPUT_DONE 0x04
++#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);
  
--#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)
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,7)
-+#define kvec iovec
-+#define __user 
- #endif
+ #endif /* __L2CAP_H */
+--- linux-2.4.21/include/net/bluetooth/rfcomm.h~bluetooth
++++ linux-2.4.21/include/net/bluetooth/rfcomm.h
+@@ -167,8 +167,8 @@
+       int              initiator;
  
--#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21)
--#define rq_data_dir(x)        ((x)->cmd)
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,28)
-+#define msleep(x) \
-+      set_current_state(TASK_UNINTERRUPTIBLE); \
-+      schedule_timeout((x)*(HZ/1000));
- #endif
+       /* Default DLC parameters */
++      int    cfc;
+       uint   mtu;
+-      uint   credits;
  
--#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)) 
--
-+#ifndef __iomem
-+#define __iomem
- #endif
+       struct list_head dlcs;
+ };
+@@ -185,11 +185,12 @@
+       atomic_t      refcnt;
+       u8            dlci;
+       u8            addr;
+-
+-      uint          mtu;
++      u8            priority;
+       u8            v24_sig;
++      u8            mscex;
+-      uint          credits;
++      uint          mtu;
++      uint          cfc;
+       uint          rx_credits;
+       uint          tx_credits;
+@@ -213,6 +214,16 @@
+ #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;
  
--/* Removed cos it broke stuff. Where is this required anyway? 
-- * #ifndef QUEUE_EMPTY
-- * #define QUEUE_EMPTY  (!CURRENT)
-- * #endif
-+#ifndef list_for_each_entry_safe
-+/**
-+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
-+ * @pos:      the type * to use as a loop counter.
-+ * @n:                another type * to use as temporary storage
-+ * @head:     the head for your list.
-+ * @member:   the name of the list_struct within the struct.
+--- linux-2.4.21/include/net/iw_handler.h~linux-iw241_we16-6
++++ linux-2.4.21/include/net/iw_handler.h
+@@ -1,7 +1,7 @@
+ /*
+  * This file define the new driver API for Wireless Extensions
+  *
+- * Version :  4       21.6.02
++ * Version :  5       4.12.02
+  *
+  * Authors :  Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+  * Copyright (c) 2001-2002 Jean Tourrilhes, All Rights Reserved.
+@@ -206,7 +206,7 @@
+  * will be needed...
+  * I just plan to increment with each new version.
   */
--#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
-+#define list_for_each_entry_safe(pos, n, head, member)                        \
-+      for (pos = list_entry((head)->next, typeof(*pos), member),      \
-+              n = list_entry(pos->member.next, typeof(*pos), member); \
-+           &pos->member != (head);                                    \
-+           pos = n, n = list_entry(n->member.next, typeof(*n), member))
--#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,12)
--#define PageUptodate(x) Page_Uptodate(x)
- #endif
--#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48)
--#define get_seconds() CURRENT_TIME
-+#ifndef DEFINE_SPINLOCK
-+#define DEFINE_SPINLOCK(x) spinlock_t x = SPIN_LOCK_UNLOCKED
- #endif
--
--#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,53)
--#define generic_file_readonly_mmap generic_file_mmap
-+#ifndef DEFINE_RWLOCK
-+#define DEFINE_RWLOCK(x) rwlock_t x = RW_LOCK_UNLOCKED
- #endif
+-#define IW_HANDLER_VERSION    4
++#define IW_HANDLER_VERSION    5
  
- #endif /* __LINUX_MTD_COMPATMAC_H__ */
---- linux-2.4.21/include/linux/mtd/doc2000.h~mtd-cvs
-+++ linux-2.4.21/include/linux/mtd/doc2000.h
-@@ -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$ 
+ /*
+  * Changes :
+@@ -220,10 +220,18 @@
+  * V3 to V4
+  * --------
+  *    - Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes
 + *
-+ * 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,22 +46,51 @@
- #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 
++ * V4 to V5
++ * --------
++ *    - Add new spy support : struct iw_spy_data & prototypes
   */
- #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))
--#define WriteDOC_(d, adr, reg)  writeb(d, ((unsigned long)adr) + (reg))
-+#define ReadDOC_(adr, reg)      readb((void __iomem *)(adr) + (reg))
-+#define WriteDOC_(d, adr, reg)  writeb(d, (void __iomem *)(adr) + (reg))
- #define DOC_IOREMAP_LEN 0x2000
- #endif
-@@ -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
+ /**************************** CONSTANTS ****************************/
++/* Enable enhanced spy support. Disable to reduce footprint */
++#define IW_WIRELESS_SPY
++#define IW_WIRELESS_THRSPY
 +
- #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
+ /* Special error message for the driver to indicate that we
+  * should do a commit after return from the iw_handler */
+ #define EIWCOMMIT     EINPROGRESS
+@@ -315,6 +323,9 @@
+        * We will automatically export that to user space... */
+       struct iw_priv_args *   private_args;
  
-+#define DOC_FLASH_CE          0x80
-+#define DOC_FLASH_WP          0x40
-+#define DOC_FLASH_BANK                0x02
++      /* Driver enhanced spy support */
++      long                    spy_offset;     /* Spy data offset */
 +
- /* 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,18 +156,21 @@
- #define MAX_FLOORS 4
- #define MAX_CHIPS 4
+       /* In the long term, get_wireless_stats will move from
+        * 'struct net_device' to here, to minimise bloat. */
+ };
+@@ -350,6 +361,33 @@
  
--#define MAX_FLOORS_MIL 4
-+#define MAX_FLOORS_MIL 1
- #define MAX_CHIPS_MIL 1
+ /* Need to think of short header translation table. Later. */
  
-+#define MAX_FLOORS_MPLUS 2
-+#define MAX_CHIPS_MPLUS 1
++/* --------------------- ENHANCED SPY SUPPORT --------------------- */
++/*
++ * In the old days, the driver was handling spy support all by itself.
++ * Now, the driver can delegate this task to Wireless Extensions.
++ * It needs to include this struct in its private part and use the
++ * standard spy iw_handler.
++ */
 +
- #define ADDR_COLUMN 1
- #define ADDR_PAGE 2
- #define ADDR_COLUMN_PAGE 3
++/*
++ * Instance specific spy data, i.e. addresses spied and quality for them.
++ */
++struct iw_spy_data
++{
++#ifdef IW_WIRELESS_SPY
++      /* --- Standard spy support --- */
++      int                     spy_number;
++      u_char                  spy_address[IW_MAX_SPY][ETH_ALEN];
++      struct iw_quality       spy_stat[IW_MAX_SPY];
++#ifdef IW_WIRELESS_THRSPY
++      /* --- Enhanced spy support (event) */
++      struct iw_quality       spy_thr_low;    /* Low threshold */
++      struct iw_quality       spy_thr_high;   /* High threshold */
++      u_char                  spy_thr_under[IW_MAX_SPY];
++#endif /* IW_WIRELESS_THRSPY */
++#endif /* IW_WIRELESS_SPY */
++};
++
+ /**************************** PROTOTYPES ****************************/
+ /*
+  * Functions part of the Wireless Extensions (defined in net/core/wireless.c).
+@@ -376,6 +414,31 @@
+ /* We may need a function to send a stream of events to user space.
+  * More on that later... */
  
- struct DiskOnChip {
-       unsigned long physadr;
--      unsigned long virtadr;
-+      void __iomem *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;
---- linux-2.4.21/include/linux/mtd/flashchip.h~mtd-cvs
-+++ linux-2.4.21/include/linux/mtd/flashchip.h
-@@ -6,7 +6,7 @@
-  *
-  * (C) 2000 Red Hat. GPLd.
-  *
-- * $Id$
-+ * $Id$
-  *
-  */
++/* Standard handler for SIOCSIWSPY */
++extern int iw_handler_set_spy(struct net_device *     dev,
++                            struct iw_request_info *  info,
++                            union iwreq_data *        wrqu,
++                            char *                    extra);
++/* Standard handler for SIOCGIWSPY */
++extern int iw_handler_get_spy(struct net_device *     dev,
++                            struct iw_request_info *  info,
++                            union iwreq_data *        wrqu,
++                            char *                    extra);
++/* Standard handler for SIOCSIWTHRSPY */
++extern int iw_handler_set_thrspy(struct net_device *  dev,
++                               struct iw_request_info *info,
++                               union iwreq_data *     wrqu,
++                               char *                 extra);
++/* Standard handler for SIOCGIWTHRSPY */
++extern int iw_handler_get_thrspy(struct net_device *  dev,
++                               struct iw_request_info *info,
++                               union iwreq_data *     wrqu,
++                               char *                 extra);
++/* Driver call to update spy records */
++extern void wireless_spy_update(struct net_device *   dev,
++                              unsigned char *         address,
++                              struct iw_quality *     wstats);
++
+ /************************* INLINE FUNTIONS *************************/
+ /*
+  * Function that are so simple that it's more efficient inlining them
+--- linux-2.4.21/init/do_mounts.c~small-nocramdisk
++++ linux-2.4.21/init/do_mounts.c
+@@ -16,8 +16,6 @@
+ #include <linux/ext2_fs.h>
+ #include <linux/romfs_fs.h>
  
-@@ -29,6 +29,7 @@
-       FL_ERASE_SUSPENDED,
-       FL_WRITING,
-       FL_WRITING_TO_BUFFER,
-+      FL_OTP_WRITE,
-       FL_WRITE_SUSPENDING,
-       FL_WRITE_SUSPENDED,
-       FL_PM_SUSPENDED,
-@@ -37,13 +38,16 @@
-       FL_LOCKING,
-       FL_UNLOCKING,
-       FL_POINT,
-+      FL_XIP_WHILE_ERASING,
-+      FL_XIP_WHILE_WRITING,
-       FL_UNKNOWN
- } flstate_t;
+-#define BUILD_CRAMDISK
+-
+ extern int get_filesystem_list(char * buf);
  
+ extern asmlinkage long sys_mount(char *dev_name, char *dir_name, char *type,
+--- linux-2.4.21/kernel/ksyms.c~bluetooth
++++ linux-2.4.21/kernel/ksyms.c
+@@ -48,6 +48,7 @@
+ #include <linux/completion.h>
+ #include <linux/seq_file.h>
+ #include <linux/dnotify.h>
++#include <linux/firmware.h>
+ #include <asm/checksum.h>
+ #if defined(CONFIG_PROC_FS)
+@@ -564,6 +565,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);
+@@ -585,6 +593,11 @@
  
+ EXPORT_SYMBOL(tasklist_lock);
+ EXPORT_SYMBOL(pidhash);
++#ifdef CONFIG_ARCH_RAMSES
++#include <asm/arch/ramses.h>
++EXPORT_SYMBOL(ramses_control_shadow);
++EXPORT_SYMBOL(ramses_flags);
++#endif
  
- /* NOTE: confusingly, this can be used to refer to more than one chip at a time, 
--   if they're interleaved. */
-+   if they're interleaved.  This can even refer to individual partitions on
-+   the same physical chip when present. */
+ /* debug */
+ EXPORT_SYMBOL(dump_stack);
+--- linux-2.4.21/kernel/pm.c~pm
++++ linux-2.4.21/kernel/pm.c
+@@ -234,7 +234,7 @@
+       struct list_head *entry;
+       
+       down(&pm_devs_lock);
+-      entry = pm_devs.next;
++      entry = (rqst==PM_RESUME) ? pm_devs.prev : pm_devs.next;
+       while (entry != &pm_devs) {
+               struct pm_dev *dev = list_entry(entry, struct pm_dev, entry);
+               if (dev->callback) {
+@@ -249,7 +249,7 @@
+                               return status;
+                       }
+               }
+-              entry = entry->next;
++              entry = (rqst==PM_RESUME) ? entry->prev : entry->next;
+       }
+       up(&pm_devs_lock);
+       return 0;
+--- linux-2.4.21/lib/Config.in~mtd-cvs
++++ linux-2.4.21/lib/Config.in
+@@ -35,4 +35,25 @@
+   fi
+ fi
  
- struct flchip {
-       unsigned long start; /* Offset within the map */
-@@ -58,6 +62,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;
++if [ "$CONFIG_MTD_DOCPROBE" = "y" -o \
++     "$CONFIG_MTD_NAND_RTC_FROM4" = "y" -o \
++     "$CONFIG_MTD_NAND_DISKONCHIP" = "y" ]; then
++   define_tristate CONFIG_REED_SOLOMON y
++   define_tristate CONFIG_REED_SOLOMON_DEC16 y
++else
++  if [ "$CONFIG_MTD_DOCPROBE" = "m" -o \
++     "$CONFIG_MTD_NAND_RTC_FROM4" = "m" -o \
++     "$CONFIG_MTD_NAND_DISKONCHIP" = "m" ]; then
++     define_tristate CONFIG_REED_SOLOMON m
++     define_tristate CONFIG_REED_SOLOMON_DEC16 y
++  else
++     define_tristate CONFIG_REED_SOLOMON n
++  fi
++fi
 +
-       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
-@@ -65,8 +74,17 @@
-       int word_write_time;
-       int buffer_write_time;
-       int erase_time;
++if [ "$CONFIG_EXPERIMENTAL" = "y" -a \
++     "$CONFIG_HOTPLUG" = "y" ]; then
++   tristate 'Hotplug firmware loading support (EXPERIMENTAL)' CONFIG_FW_LOADER
++fi
 +
-+      void *priv;
- };
+ endmenu
+--- linux-2.4.21/lib/Makefile~mtd-cvs
++++ linux-2.4.21/lib/Makefile
+@@ -8,11 +8,13 @@
  
-+/* This is used to handle contention on write/erase operations
-+   between partitions of the same physical chip. */
-+struct flchip_shared {
-+      spinlock_t lock;
-+      struct flchip *writing;
-+      struct flchip *erasing;
-+};
+ L_TARGET := lib.a
  
+-export-objs := cmdline.o dec_and_lock.o rwsem-spinlock.o rwsem.o rbtree.o
++export-objs := cmdline.o dec_and_lock.o rwsem-spinlock.o rwsem.o \
++             rbtree.o firmware_class.o
  
- #endif /* __MTD_FLASHCHIP_H__ */
---- linux-2.4.21/include/linux/mtd/gen_probe.h~mtd-cvs
-+++ linux-2.4.21/include/linux/mtd/gen_probe.h
-@@ -1,7 +1,7 @@
- /*
-  * (C) 2001, 2001 Red Hat, Inc.
-  * GPL'd
-- * $Id$
-+ * $Id$
-  */
+ obj-y := errno.o ctype.o string.o vsprintf.o brlock.o cmdline.o \
+        bust_spinlocks.o rbtree.o dump_stack.o
  
- #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 <linux/bitops.h>
++obj-$(CONFIG_FW_LOADER) += firmware_class.o
+ obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
+ obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
  
- 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);
- };
+@@ -22,6 +24,9 @@
  
- struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp);
+ subdir-$(CONFIG_ZLIB_INFLATE) += zlib_inflate
+ subdir-$(CONFIG_ZLIB_DEFLATE) += zlib_deflate
++subdir-$(CONFIG_REED_SOLOMON) += reed_solomon
++
++include $(TOPDIR)/drivers/bluetooth/Makefile.lib
+ # Include the subdirs, if necessary.
+ obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o))
 --- /dev/null
-+++ linux-2.4.21/include/linux/mtd/inftl.h
-@@ -0,0 +1,57 @@
++++ linux-2.4.21/lib/firmware_class.c
+@@ -0,0 +1,573 @@
 +/*
-+ *    inftl.h -- defines to support the Inverse NAND Flash Translation Layer
++ * firmware_class.c - Multi purpose firmware loading support
 + *
-+ *    (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
++ * Copyright (c) 2003 Manuel Estrada Sainz <ranty@debian.org>
 + *
-+ *    $Id$
++ * Please see Documentation/firmware_class/ for more information.
++ *
++ */
++/*
++ * Based on kernel/kmod.c and drivers/usb/usb.c
 + */
++/*
++        kernel/kmod.c
++        Kirk Petersen
 +
-+#ifndef __MTD_INFTL_H__
-+#define __MTD_INFTL_H__
++        Reorganized not to be a daemon by Adam Richter, with guidance
++        from Greg Zornetzer.
 +
-+#ifndef __KERNEL__
-+#error This is a kernel header. Perhaps include nftl-user.h instead?
-+#endif
++        Modified to avoid chroot and file sharing problems.
++        Mikael Pettersson
 +
-+#include <linux/mtd/blktrans.h>
-+#include <linux/mtd/mtd.h>
-+#include <linux/mtd/nftl.h>
++        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
 +
-+#include <mtd/inftl-user.h>
++        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)
++ */
 +
-+#ifndef INFTL_MAJOR
-+#define INFTL_MAJOR 94
-+#endif
-+#define INFTL_PARTN_BITS 4
++#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>
 +
-+#ifdef __KERNEL__
++#include "linux/firmware.h"
 +
-+struct INFTLrecord {
-+      struct mtd_blktrans_dev mbd;
-+      __u16 MediaUnit;
-+      __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;
-+        struct nand_oobinfo oobinfo;
-+};
++MODULE_AUTHOR("Manuel Estrada Sainz <ranty@debian.org>");
++MODULE_DESCRIPTION("Multi purpose firmware loading support");
++MODULE_LICENSE("GPL");
 +
-+int INFTL_mount(struct INFTLrecord *s);
-+int INFTL_formatblock(struct INFTLrecord *s, int block);
++#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)
 +
-+#endif /* __KERNEL__ */
++static int loading_timeout = 10;      /* In seconds */
++static struct proc_dir_entry *proc_dir_timeout;
++static struct proc_dir_entry *proc_dir;
 +
-+#endif /* __MTD_INFTL_H__ */
---- linux-2.4.21/include/linux/mtd/jedec.h~mtd-cvs
-+++ linux-2.4.21/include/linux/mtd/jedec.h
-@@ -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
---- linux-2.4.21/include/linux/mtd/map.h~mtd-cvs
-+++ linux-2.4.21/include/linux/mtd/map.h
-@@ -1,23 +1,176 @@
- /* 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 <linux/mtd/compatmac.h>
-+#include <asm/unaligned.h>
-+#include <asm/system.h>
-+#include <asm/io.h>
-+#include <asm/bug.h>
++#ifdef CONFIG_HOTPLUG
 +
-+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_1
-+#define map_bankwidth(map) 1
-+#define map_bankwidth_is_1(map) (map_bankwidth(map) == 1)
-+#define map_bankwidth_is_large(map) (0)
-+#define map_words(map) (1)
-+#define MAX_MAP_BANKWIDTH 1
-+#else
-+#define map_bankwidth_is_1(map) (0)
-+#endif
++static int
++call_helper(char *verb, const char *name, const char *device)
++{
++      char *argv[3], **envp, *buf, *scratch;
++      int i = 0;
 +
-+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_2
-+# ifdef map_bankwidth
-+#  undef map_bankwidth
-+#  define map_bankwidth(map) ((map)->bankwidth)
-+# else
-+#  define map_bankwidth(map) 2
-+#  define map_bankwidth_is_large(map) (0)
-+#  define map_words(map) (1)
-+# endif
-+#define map_bankwidth_is_2(map) (map_bankwidth(map) == 2)
-+#undef MAX_MAP_BANKWIDTH
-+#define MAX_MAP_BANKWIDTH 2
-+#else
-+#define map_bankwidth_is_2(map) (0)
-+#endif
++      int retval = 0;
 +
-+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_4
-+# ifdef map_bankwidth
-+#  undef map_bankwidth
-+#  define map_bankwidth(map) ((map)->bankwidth)
-+# else
-+#  define map_bankwidth(map) 4
-+#  define map_bankwidth_is_large(map) (0)
-+#  define map_words(map) (1)
-+# endif
-+#define map_bankwidth_is_4(map) (map_bankwidth(map) == 4)
-+#undef MAX_MAP_BANKWIDTH
-+#define MAX_MAP_BANKWIDTH 4
-+#else
-+#define map_bankwidth_is_4(map) (0)
-+#endif
++      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;
++      }
 +
-+/* ensure we never evaluate anything shorted than an unsigned long
-+ * to zero, and ensure we'll never miss the end of an comparison (bjd) */
++      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;
++      }
 +
-+#define map_calc_words(map) ((map_bankwidth(map) + (sizeof(unsigned long)-1))/ sizeof(unsigned long))
++      /* only one standardized param to hotplug command: type */
++      argv[0] = hotplug_path;
++      argv[1] = "firmware";
++      argv[2] = 0;
 +
-+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_8
-+# ifdef map_bankwidth
-+#  undef map_bankwidth
-+#  define map_bankwidth(map) ((map)->bankwidth)
-+#  if BITS_PER_LONG < 64
-+#   undef map_bankwidth_is_large
-+#   define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
-+#   undef map_words
-+#   define map_words(map) map_calc_words(map)
-+#  endif
-+# else
-+#  define map_bankwidth(map) 8
-+#  define map_bankwidth_is_large(map) (BITS_PER_LONG < 64)
-+#  define map_words(map) map_calc_words(map)
-+# endif
-+#define map_bankwidth_is_8(map) (map_bankwidth(map) == 8)
-+#undef MAX_MAP_BANKWIDTH
-+#define MAX_MAP_BANKWIDTH 8
-+#else
-+#define map_bankwidth_is_8(map) (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;
 +
-+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_16
-+# ifdef map_bankwidth
-+#  undef map_bankwidth
-+#  define map_bankwidth(map) ((map)->bankwidth)
-+#  undef map_bankwidth_is_large
-+#  define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
-+#  undef map_words
-+#  define map_words(map) map_calc_words(map)
-+# else
-+#  define map_bankwidth(map) 16
-+#  define map_bankwidth_is_large(map) (1)
-+#  define map_words(map) map_calc_words(map)
-+# endif
-+#define map_bankwidth_is_16(map) (map_bankwidth(map) == 16)
-+#undef MAX_MAP_BANKWIDTH
-+#define MAX_MAP_BANKWIDTH 16
-+#else
-+#define map_bankwidth_is_16(map) (0)
++      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
 +
-+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_32
-+# ifdef map_bankwidth
-+#  undef map_bankwidth
-+#  define map_bankwidth(map) ((map)->bankwidth)
-+#  undef map_bankwidth_is_large
-+#  define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
-+#  undef map_words
-+#  define map_words(map) map_calc_words(map)
-+# else
-+#  define map_bankwidth(map) 32
-+#  define map_bankwidth_is_large(map) (1)
-+#  define map_words(map) map_calc_words(map)
-+# endif
-+#define map_bankwidth_is_32(map) (map_bankwidth(map) == 32)
-+#undef MAX_MAP_BANKWIDTH
-+#define MAX_MAP_BANKWIDTH 32
++      retval = call_usermodehelper(argv[0], argv, envp);
++      if (retval) {
++              printk("call_usermodehelper return %d\n", retval);
++      }
++
++      kfree(buf);
++      kfree(envp);
++      return retval;
++}
 +#else
-+#define map_bankwidth_is_32(map) (0)
-+#endif
 +
-+#ifndef map_bankwidth
-+#error "No bus width supported. What's the point?"
-+#endif
++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);
++}
 +
-+static inline int map_bankwidth_supported(int w)
++/**
++ * 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)
 +{
-+      switch (w) {
-+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_1
++      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:
-+#endif
-+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_2
-+      case 2:
-+#endif
-+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_4
-+      case 4:
-+#endif
-+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_8
-+      case 8:
-+#endif
-+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_16
-+      case 16:
-+#endif
-+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_32
-+      case 32:
-+#endif
-+              return 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;
++      }
 +
-+      default:
++      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;
 +}
 +
-+#define MAX_MAP_LONGS ( ((MAX_MAP_BANKWIDTH*8) + BITS_PER_LONG - 1) / BITS_PER_LONG )
++/**
++ * 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;
 +
-+typedef union {
-+      unsigned long x[MAX_MAP_LONGS];
-+} map_word;
- /* 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
-    paging etc. correctly if your device needs it. Then you pass it off
--   to a chip driver which deals with a mapped device - generally either
--   do_cfi_probe() or do_ram_probe(), either of which will return a 
--   struct mtd_info if they liked what they saw. At which point, you
--   fill in the mtd->module with your own module address, and register 
--   it.
-+   to a chip probe routine -- either JEDEC or CFI probe or both -- via
-+   do_map_probe(). If a chip is recognised, the probe code will invoke the
-+   appropriate chip driver (if present) and return a struct mtd_info.
-+   At which point, you fill in the mtd->module with your own module 
-+   address, and register it with the MTD core code. Or you could partition
-+   it and register the partitions instead, or keep it for your own private
-+   use; whatever.
-    
-    The mtd->priv field will point to the struct map_info, and any further
-    private data required by the chip driver is linked from the 
-@@ -29,39 +182,45 @@
- struct map_info {
-       char *name;
-       unsigned long size;
--      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);  
--      /* If it returned a 'long' I'd call it readl.
--       * It doesn't.
--       * I won't.
--       * dwmw2 */
-+      unsigned long phys;
-+#define NO_XIP (-1UL)
-       
-+      void __iomem *virt;
-+      void *cached;
++      retval = fw_realloc_buffer(fw_priv, offset + count);
++      if (retval) {
++              printk("%s: retval:%d\n", __FUNCTION__, retval);
++              return retval;
++      }
 +
-+      int bankwidth; /* in octets. This isn't necessarily the width
-+                     of actual bus cycles -- it's the repeat interval
-+                    in bytes, before you are talking to the first chip again.
-+                    */
++      memcpy(fw->data + offset, buffer, count);
 +
-+#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
-+      map_word (*read)(struct map_info *, unsigned long);
-       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);
++      fw->size = max_t(size_t, offset + count, fw->size);
++      file->f_pos += count;
++      return count;
++}
 +
-+      void (*write)(struct map_info *, const map_word, 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
-+      /* It's possible for the map driver to use cached memory in its
-+         copy_from implementation (and _only_ with copy_from).  However,
-+         when the chip driver knows some flash area has changed contents,
-+         it will signal it to the map driver through this routine to let
-+         the map driver invalidate the corresponding cache as needed.
-+         If there is no cache to care about this can be set to NULL. */
-+      void (*inval_cache)(struct map_info *, unsigned long, ssize_t);
-+      /* 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 */
++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));
 +
-       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 +233,193 @@
- 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);
++      init_completion(&fw_priv->completion);
 +
-+#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)
-+#define INVALIDATE_CACHED_RANGE(map, from, size) \
-+      do { if(map->inval_cache) map->inval_cache(map, from, size); } while(0)
--/*
-- * 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)
++      fw_priv->timeout.function = firmware_class_timeout;
++      fw_priv->timeout.data = (u_long) fw_priv;
++      init_timer(&fw_priv->timeout);
 +
-+static inline int map_word_equal(struct map_info *map, map_word val1, map_word val2)
- {
--      struct map_info *map = mtd->priv;
-+      int i;
-+      for (i=0; i<map_words(map); i++) {
-+              if (val1.x[i] != val2.x[i])
-+                      return 0;
++      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;
 +      }
-+      return 1;
++      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;
 +}
--      if (map->fldrv->destroy)
--              map->fldrv->destroy(mtd);
--#ifdef CONFIG_MODULES
--      if (map->fldrv->module)
--              __MOD_DEC_USE_COUNT(map->fldrv->module);
-+static inline map_word map_word_and(struct map_info *map, map_word val1, map_word val2)
++static void
++fw_remove_class_device(struct firmware_priv *fw_priv)
 +{
-+      map_word r;
-+      int i;
++      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);
++}
 +
-+      for (i=0; i<map_words(map); i++) {
-+              r.x[i] = val1.x[i] & val2.x[i];
++/** 
++ * 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;
 +      }
-+      return r;
++      *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;
 +}
 +
-+static inline map_word map_word_clr(struct map_info *map, map_word val1, map_word val2)
++void
++release_firmware(const struct firmware *fw)
 +{
-+      map_word r;
-+      int i;
-+
-+      for (i=0; i<map_words(map); i++) {
-+              r.x[i] = val1.x[i] & ~val2.x[i];
++      if (fw) {
++              vfree(fw->data);
++              kfree(fw);
 +      }
-+      return r;
 +}
 +
-+static inline map_word map_word_or(struct map_info *map, map_word val1, map_word val2)
++/**
++ * 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)
 +{
-+      map_word r;
-+      int i;
++      /* This is meaningless without firmware caching, so until we
++       * decide if firmware caching is reasonable just leave it as a
++       * noop */
++}
 +
-+      for (i=0; i<map_words(map); i++) {
-+              r.x[i] = val1.x[i] | val2.x[i];
-+      }
-+      return r;
++/* 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);
 +}
 +
-+#define map_word_andequal(m, a, b, z) map_word_equal(m, z, map_word_and(m, a, b))
++/**
++ * 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;
++      }
 +
-+static inline int map_word_bitsset(struct map_info *map, map_word val1, map_word val2)
++      *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)
 +{
-+      int i;
++      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);
++}
 +
-+      for (i=0; i<map_words(map); i++) {
-+              if (val1.x[i] & val2.x[i])
-+                      return 1;
++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
+--- /dev/null
++++ linux-2.4.21/lib/reed_solomon/Makefile
+@@ -0,0 +1,12 @@
++#
++# This is a modified version of reed solomon lib, 
++#
++
++O_TARGET:= reed_solomon.o     
++
++export-objs := rslib.o
++
++obj-y := rslib.o
++obj-m := $(O_TARGET)
++
++include $(TOPDIR)/Rules.make
+--- /dev/null
++++ linux-2.4.21/lib/reed_solomon/decode_rs.c
+@@ -0,0 +1,272 @@
++/* 
++ * lib/reed_solomon/decode_rs.c
++ *
++ * Overview:
++ *   Generic Reed Solomon encoder / decoder library
++ *   
++ * Copyright 2002, Phil Karn, KA9Q
++ * May be used under the terms of the GNU General Public License (GPL)
++ *
++ * Adaption to the kernel by Thomas Gleixner (tglx@linutronix.de)
++ *
++ * $Id$
++ *
++ */
++
++/* Generic data width independent code which is included by the 
++ * wrappers.
++ */
++{ 
++      int deg_lambda, el, deg_omega;
++      int i, j, r, k, pad;
++      int nn = rs->nn;
++      int nroots = rs->nroots;
++      int fcr = rs->fcr;
++      int prim = rs->prim;
++      int iprim = rs->iprim;
++      uint16_t *alpha_to = rs->alpha_to;
++      uint16_t *index_of = rs->index_of;
++      uint16_t u, q, tmp, num1, num2, den, discr_r, syn_error;
++      /* Err+Eras Locator poly and syndrome poly The maximum value
++       * of nroots is 8. So the necessary stack size will be about
++       * 220 bytes max.
++       */
++      uint16_t lambda[nroots + 1], syn[nroots];
++      uint16_t b[nroots + 1], t[nroots + 1], omega[nroots + 1];
++      uint16_t root[nroots], reg[nroots + 1], loc[nroots];
++      int count = 0;
++      uint16_t msk = (uint16_t) rs->nn;
++
++      /* Check length parameter for validity */
++      pad = nn - nroots - len;
++      if (pad < 0 || pad >= nn)
++              return -ERANGE;
++              
++      /* Does the caller provide the syndrome ? */
++      if (s != NULL) 
++              goto decode;
++
++      /* form the syndromes; i.e., evaluate data(x) at roots of
++       * g(x) */
++      for (i = 0; i < nroots; i++)
++              syn[i] = (((uint16_t) data[0]) ^ invmsk) & msk;
++
++      for (j = 1; j < len; j++) {
++              for (i = 0; i < nroots; i++) {
++                      if (syn[i] == 0) {
++                              syn[i] = (((uint16_t) data[j]) ^ 
++                                        invmsk) & msk;
++                      } else {
++                              syn[i] = ((((uint16_t) data[j]) ^
++                                         invmsk) & msk) ^ 
++                                      alpha_to[rs_modnn(rs, index_of[syn[i]] +
++                                                     (fcr + i) * prim)];
++                      }
++              }
++      }
++
++      for (j = 0; j < nroots; j++) {
++              for (i = 0; i < nroots; i++) {
++                      if (syn[i] == 0) {
++                              syn[i] = ((uint16_t) par[j]) & msk;
++                      } else {
++                              syn[i] = (((uint16_t) par[j]) & msk) ^ 
++                                      alpha_to[rs_modnn(rs, index_of[syn[i]] +
++                                                     (fcr+i)*prim)];
++                      }
++              }
++      }
++      s = syn;
++
++      /* Convert syndromes to index form, checking for nonzero condition */
++      syn_error = 0;
++      for (i = 0; i < nroots; i++) {
++              syn_error |= s[i];
++              s[i] = index_of[s[i]];
++      }
++
++      if (!syn_error) {
++              /* if syndrome is zero, data[] is a codeword and there are no
++               * errors to correct. So return data[] unmodified
++               */
++              count = 0;
++              goto finish;
++      }
++
++ decode:
++      memset(&lambda[1], 0, nroots * sizeof(lambda[0]));
++      lambda[0] = 1;
++
++      if (no_eras > 0) {
++              /* Init lambda to be the erasure locator polynomial */
++              lambda[1] = alpha_to[rs_modnn(rs, 
++                                            prim * (nn - 1 - eras_pos[0]))];
++              for (i = 1; i < no_eras; i++) {
++                      u = rs_modnn(rs, prim * (nn - 1 - eras_pos[i]));
++                      for (j = i + 1; j > 0; j--) {
++                              tmp = index_of[lambda[j - 1]];
++                              if (tmp != nn) {
++                                      lambda[j] ^= 
++                                              alpha_to[rs_modnn(rs, u + tmp)];
++                              }
++                      }
++              }
++      }
++
++      for (i = 0; i < nroots + 1; i++)
++              b[i] = index_of[lambda[i]];
++
++      /*
++       * Begin Berlekamp-Massey algorithm to determine error+erasure
++       * locator polynomial
++       */
++      r = no_eras;
++      el = no_eras;
++      while (++r <= nroots) { /* r is the step number */
++              /* Compute discrepancy at the r-th step in poly-form */
++              discr_r = 0;
++              for (i = 0; i < r; i++) {
++                      if ((lambda[i] != 0) && (s[r - i - 1] != nn)) {
++                              discr_r ^= 
++                                      alpha_to[rs_modnn(rs, 
++                                                        index_of[lambda[i]] +
++                                                        s[r - i - 1])];
++                      }
++              }
++              discr_r = index_of[discr_r];    /* Index form */
++              if (discr_r == nn) {
++                      /* 2 lines below: B(x) <-- x*B(x) */
++                      memmove (&b[1], b, nroots * sizeof (b[0]));
++                      b[0] = nn;
++              } else {
++                      /* 7 lines below: T(x) <-- lambda(x)-discr_r*x*b(x) */
++                      t[0] = lambda[0];
++                      for (i = 0; i < nroots; i++) {
++                              if (b[i] != nn) {
++                                      t[i + 1] = lambda[i + 1] ^ 
++                                              alpha_to[rs_modnn(rs, discr_r +
++                                                                b[i])];
++                              } else
++                                      t[i + 1] = lambda[i + 1];
++                      }
++                      if (2 * el <= r + no_eras - 1) {
++                              el = r + no_eras - el;
++                              /*
++                               * 2 lines below: B(x) <-- inv(discr_r) *
++                               * lambda(x)
++                               */
++                              for (i = 0; i <= nroots; i++) {
++                                      b[i] = (lambda[i] == 0) ? nn :
++                                              rs_modnn(rs, index_of[lambda[i]]
++                                                       - discr_r + nn);
++                              }
++                      } else {
++                              /* 2 lines below: B(x) <-- x*B(x) */
++                              memmove(&b[1], b, nroots * sizeof(b[0]));
++                              b[0] = nn;
++                      }
++                      memcpy(lambda, t, (nroots + 1) * sizeof(t[0]));
++              }
++      }
++
++      /* Convert lambda to index form and compute deg(lambda(x)) */
++      deg_lambda = 0;
++      for (i = 0; i < nroots + 1; i++) {
++              lambda[i] = index_of[lambda[i]];
++              if (lambda[i] != nn)
++                      deg_lambda = i;
++      }
++      /* Find roots of error+erasure locator polynomial by Chien search */
++      memcpy(&reg[1], &lambda[1], nroots * sizeof(reg[0]));
++      count = 0;              /* Number of roots of lambda(x) */
++      for (i = 1, k = iprim - 1; i <= nn; i++, k = rs_modnn(rs, k + iprim)) {
++              q = 1;          /* lambda[0] is always 0 */
++              for (j = deg_lambda; j > 0; j--) {
++                      if (reg[j] != nn) {
++                              reg[j] = rs_modnn(rs, reg[j] + j);
++                              q ^= alpha_to[reg[j]];
++                      }
++              }
++              if (q != 0)
++                      continue;       /* Not a root */
++              /* store root (index-form) and error location number */
++              root[count] = i;
++              loc[count] = k;
++              /* If we've already found max possible roots,
++               * abort the search to save time
++               */
++              if (++count == deg_lambda)
++                      break;
++      }
++      if (deg_lambda != count) {
++              /*
++               * deg(lambda) unequal to number of roots => uncorrectable
++               * error detected
++               */
++              count = -1;
++              goto finish;
 +      }
-+      return 0;
-+}
-+
-+static inline map_word map_word_load(struct map_info *map, const void *ptr)
-+{
-+      map_word r;
-+
-+      if (map_bankwidth_is_1(map))
-+              r.x[0] = *(unsigned char *)ptr;
-+      else if (map_bankwidth_is_2(map))
-+              r.x[0] = get_unaligned((uint16_t *)ptr);
-+      else if (map_bankwidth_is_4(map))
-+              r.x[0] = get_unaligned((uint32_t *)ptr);
-+#if BITS_PER_LONG >= 64
-+      else if (map_bankwidth_is_8(map))
-+              r.x[0] = get_unaligned((uint64_t *)ptr);
- #endif
--      kfree(mtd);
-+      else if (map_bankwidth_is_large(map))
-+              memcpy(r.x, ptr, map->bankwidth);
-+
-+      return r;
- }
--#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 map_word map_word_load_partial(struct map_info *map, map_word orig, const unsigned char *buf, int start, int len)
-+{
-+      int i;
-+
-+      if (map_bankwidth_is_large(map)) {
-+              char *dest = (char *)&orig;
-+              memcpy(dest+start, buf, len);
-+      } else {
-+              for (i=start; i < start+len; i++) {
-+                      int bitpos;
-+#ifdef __LITTLE_ENDIAN
-+                      bitpos = i*8;
-+#else /* __BIG_ENDIAN */
-+                      bitpos = (map_bankwidth(map)-1-i)*8;
-+#endif
-+                      orig.x[0] &= ~(0xff << bitpos);
-+                      orig.x[0] |= buf[i-start] << bitpos;
++      /*
++       * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo
++       * x**nroots). in index form. Also find deg(omega).
++       */
++      deg_omega = deg_lambda - 1;
++      for (i = 0; i <= deg_omega; i++) {
++              tmp = 0;
++              for (j = i; j >= 0; j--) {
++                      if ((s[i - j] != nn) && (lambda[j] != nn))
++                              tmp ^=
++                                  alpha_to[rs_modnn(rs, s[i - j] + lambda[j])];
 +              }
++              omega[i] = index_of[tmp];
 +      }
-+      return orig;
-+}
 +
-+static inline map_word map_word_ff(struct map_info *map)
-+{
-+      map_word r;
-+      int i;
++      /*
++       * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 =
++       * inv(X(l))**(fcr-1) and den = lambda_pr(inv(X(l))) all in poly-form
++       */
++      for (j = count - 1; j >= 0; j--) {
++              num1 = 0;
++              for (i = deg_omega; i >= 0; i--) {
++                      if (omega[i] != nn)
++                              num1 ^= alpha_to[rs_modnn(rs, omega[i] + 
++                                                      i * root[j])];
++              }
++              num2 = alpha_to[rs_modnn(rs, root[j] * (fcr - 1) + nn)];
++              den = 0;
 +
-+      for (i=0; i<map_words(map); i++) {
-+              r.x[i] = ~0UL;
++              /* lambda[i+1] for i even is the formal derivative
++               * lambda_pr of lambda[i] */
++              for (i = min(deg_lambda, nroots - 1) & ~1; i >= 0; i -= 2) {
++                      if (lambda[i + 1] != nn) {
++                              den ^= alpha_to[rs_modnn(rs, lambda[i + 1] + 
++                                                     i * root[j])];
++                      }
++              }
++              /* Apply error to data */
++              if (num1 != 0 && loc[j] >= pad) {
++                      uint16_t cor = alpha_to[rs_modnn(rs,index_of[num1] + 
++                                                     index_of[num2] +
++                                                     nn - index_of[den])];
++                      /* Store the error correction pattern, if a
++                       * correction buffer is available */
++                      if (corr) {
++                              corr[j] = cor;
++                      } else {
++                              /* If a data buffer is given and the
++                               * error is inside the message,
++                               * correct it */
++                              if (data && (loc[j] < (nn - nroots)))
++                                      data[loc[j] - pad] ^= cor;
++                      }
++              }
 +      }
-+      return r;
-+}
-+
-+static inline map_word inline_map_read(struct map_info *map, unsigned long ofs)
-+{
-+      map_word r;
 +
-+      if (map_bankwidth_is_1(map))
-+              r.x[0] = __raw_readb(map->virt + ofs);
-+      else if (map_bankwidth_is_2(map))
-+              r.x[0] = __raw_readw(map->virt + ofs);
-+      else if (map_bankwidth_is_4(map))
-+              r.x[0] = __raw_readl(map->virt + ofs);
-+#if BITS_PER_LONG >= 64
-+      else if (map_bankwidth_is_8(map))
-+              r.x[0] = __raw_readq(map->virt + ofs);
-+#endif
-+      else if (map_bankwidth_is_large(map))
-+              memcpy_fromio(r.x, map->virt+ofs, map->bankwidth);
++finish:
++      if (eras_pos != NULL) {
++              for (i = 0; i < count; i++)
++                      eras_pos[i] = loc[i] - pad;
++      }
++      return count;
 +
-+      return r;
 +}
+--- /dev/null
++++ linux-2.4.21/lib/reed_solomon/encode_rs.c
+@@ -0,0 +1,54 @@
++/* 
++ * lib/reed_solomon/encode_rs.c
++ *
++ * Overview:
++ *   Generic Reed Solomon encoder / decoder library
++ *   
++ * Copyright 2002, Phil Karn, KA9Q
++ * May be used under the terms of the GNU General Public License (GPL)
++ *
++ * Adaption to the kernel by Thomas Gleixner (tglx@linutronix.de)
++ *
++ * $Id$
++ *
++ */
 +
-+static inline void inline_map_write(struct map_info *map, const map_word datum, unsigned long ofs)
++/* Generic data width independent code which is included by the 
++ * wrappers.
++ * int encode_rsX (struct rs_control *rs, uintX_t *data, int len, uintY_t *par)
++ */
 +{
-+      if (map_bankwidth_is_1(map))
-+              __raw_writeb(datum.x[0], map->virt + ofs);
-+      else if (map_bankwidth_is_2(map))
-+              __raw_writew(datum.x[0], map->virt + ofs);
-+      else if (map_bankwidth_is_4(map))
-+              __raw_writel(datum.x[0], map->virt + ofs);
-+#if BITS_PER_LONG >= 64
-+      else if (map_bankwidth_is_8(map))
-+              __raw_writeq(datum.x[0], map->virt + ofs);
-+#endif
-+      else if (map_bankwidth_is_large(map))
-+              memcpy_toio(map->virt+ofs, datum.x, map->bankwidth);
-+      mb();
-+}
++      int i, j, pad;
++      int nn = rs->nn;
++      int nroots = rs->nroots;
++      uint16_t *alpha_to = rs->alpha_to;
++      uint16_t *index_of = rs->index_of;
++      uint16_t *genpoly = rs->genpoly;
++      uint16_t fb;
++      uint16_t msk = (uint16_t) rs->nn;
 +
-+static inline void inline_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-+{
-+      if (map->cached)
-+              memcpy(to, (char *)map->cached + from, len);
-+      else
-+              memcpy_fromio(to, map->virt + from, len);
-+}
++      /* Check length parameter for validity */
++      pad = nn - nroots - len;
++      if (pad < 0 || pad >= nn)
++              return -ERANGE;
 +
-+static inline void inline_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-+{
-+      memcpy_toio(map->virt + to, from, len);
++      for (i = 0; i < len; i++) {
++              fb = index_of[((((uint16_t) data[i])^invmsk) & msk) ^ par[0]];
++              /* feedback term is non-zero */
++              if (fb != nn) { 
++                      for (j = 1; j < nroots; j++) {
++                              par[j] ^= alpha_to[rs_modnn(rs, fb + 
++                                                       genpoly[nroots - j])];
++                      }
++              }
++              /* Shift */
++              memmove(&par[0], &par[1], sizeof(uint16_t) * (nroots - 1));
++              if (fb != nn) {
++                      par[nroots - 1] = alpha_to[rs_modnn(rs, 
++                                                          fb + genpoly[0])];
++              } else {
++                      par[nroots - 1] = 0;
++              }
++      }
++      return 0;
 +}
-+
-+#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
-+#define map_read(map, ofs) (map)->read(map, ofs)
-+#define map_copy_from(map, to, from, len) (map)->copy_from(map, to, from, len)
-+#define map_write(map, datum, ofs) (map)->write(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)
-+
-+#else
-+#define map_read(map, ofs) inline_map_read(map, ofs)
-+#define map_copy_from(map, to, from, len) inline_map_copy_from(map, to, from, len)
-+#define map_write(map, datum, ofs) inline_map_write(map, datum, ofs)
-+#define map_copy_to(map, to, from, len) inline_map_copy_to(map, to, from, len)
-+
-+
-+#define simple_map_init(map) BUG_ON(!map_bankwidth_supported((map)->bankwidth))
-+#define map_is_linear(map) ({ (void)(map); 1; })
-+
-+#endif /* !CONFIG_MTD_COMPLEX_MAPPINGS */
- #endif /* __LINUX_MTD_MAP_H__ */
---- linux-2.4.21/include/linux/mtd/mtd.h~mtd-cvs
-+++ linux-2.4.21/include/linux/mtd/mtd.h
-@@ -1,123 +1,45 @@
--
--/* $Id$ */
+--- /dev/null
++++ linux-2.4.21/lib/reed_solomon/rslib.c
+@@ -0,0 +1,335 @@
 +/* 
++ * lib/reed_solomon/rslib.c
++ *
++ * Overview:
++ *   Generic Reed Solomon encoder / decoder library
++ *   
++ * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
++ *
++ * Reed Solomon code lifted from reed solomon library written by Phil Karn
++ * Copyright 2002 Phil Karn, KA9Q
++ *
 + * $Id$
 + *
-+ * Copyright (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> et al.
++ * 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.
++ *
++ * Description:
++ *    
++ * The generic Reed Solomon library provides runtime configurable
++ * encoding / decoding of RS codes.
++ * Each user must call init_rs to get a pointer to a rs_control
++ * structure for the given rs parameters. This structure is either
++ * generated or a already available matching control structure is used.
++ * If a structure is generated then the polynomial arrays for
++ * fast encoding / decoding are built. This can take some time so
++ * make sure not to call this function from a time critical path.
++ * Usually a module / driver should initialize the necessary 
++ * rs_control structure on module / driver init and release it
++ * on exit.
++ * The encoding puts the calculated syndrome into a given syndrome 
++ * buffer. 
++ * The decoding is a two step process. The first step calculates
++ * the syndrome over the received (data + syndrome) and calls the
++ * second stage, which does the decoding / error correction itself.
++ * Many hw encoders provide a syndrome calculation over the received
++ * data + syndrome and can call the second stage directly.
 + *
-+ * Released under GPL
 + */
- #ifndef __MTD_MTD_H__
- #define __MTD_MTD_H__
--#ifdef __KERNEL__
-+#ifndef __KERNEL__
-+#error This is a kernel header. Perhaps include mtd-user.h instead?
-+#endif
- #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>
--#endif /* __KERNEL__ */
--
--struct erase_info_user {
--      u_int32_t start;
--      u_int32_t length;
--};
--
--struct mtd_oob_buf {
--      u_int32_t start;
--      u_int32_t length;
--      unsigned char *ptr;
--};
--
-+#include <linux/mtd/compatmac.h>
-+#include <mtd/mtd-abi.h>
- #define MTD_CHAR_MAJOR 90
- #define MTD_BLOCK_MAJOR 31
- #define MAX_MTD_DEVICES 16
--
--
--#define MTD_ABSENT            0
--#define MTD_RAM                       1
--#define MTD_ROM                       2
--#define MTD_NORFLASH          3
--#define MTD_NANDFLASH         4
--#define MTD_PEROM             5
--#define MTD_OTHER             14
--#define MTD_UNKNOWN           15
--
--
--
--#define MTD_CLEAR_BITS                1       // Bits can be cleared (flash)
--#define MTD_SET_BITS          2       // Bits can be set
--#define MTD_ERASEABLE         4       // Has an erase function
--#define MTD_WRITEB_WRITEABLE  8       // Direct IO is possible
--#define MTD_VOLATILE          16      // Set for RAMs
--#define MTD_XIP                       32      // eXecute-In-Place possible
--#define MTD_OOB                       64      // Out-of-band data (NAND flash)
--#define MTD_ECC                       128     // Device capable of automatic ECC
--
--// Some common devices / combinations of capabilities
--#define MTD_CAP_ROM           0
--#define MTD_CAP_RAM           (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE)
--#define MTD_CAP_NORFLASH        (MTD_CLEAR_BITS|MTD_ERASEABLE)
--#define MTD_CAP_NANDFLASH       (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB)
--#define MTD_WRITEABLE         (MTD_CLEAR_BITS|MTD_SET_BITS)
--
--
--// Types of automatic ECC/Checksum available
--#define MTD_ECC_NONE          0       // No automatic ECC available
--#define MTD_ECC_RS_DiskOnChip 1       // Automatic ECC on DiskOnChip
--#define MTD_ECC_SW            2       // SW ECC for Toshiba & Samsung devices
--
--struct mtd_info_user {
--      u_char type;
--      u_int32_t flags;
--      u_int32_t size;  // Total size of the MTD
--      u_int32_t erasesize;
--      u_int32_t oobblock;  // Size of OOB blocks (e.g. 512)
--      u_int32_t oobsize;   // Amount of OOB data per block (e.g. 16)
--      u_int32_t ecctype;
--      u_int32_t eccsize;
--};
--
--struct region_info_user {
--      u_int32_t offset;               /* At which this region starts, 
--                                       * from the beginning of the MTD */
--      u_int32_t erasesize;            /* For this region */
--      u_int32_t numblocks;            /* Number of blocks in this region */
--      u_int32_t regionindex;
--};
--
--#define MEMGETINFO              _IOR('M', 1, struct mtd_info_user)
--#define MEMERASE                _IOW('M', 2, struct erase_info_user)
--#define MEMWRITEOOB             _IOWR('M', 3, struct mtd_oob_buf)
--#define MEMREADOOB              _IOWR('M', 4, struct mtd_oob_buf)
--#define MEMLOCK                 _IOW('M', 5, struct erase_info_user)
--#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)
--
--#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;
--
--      /* User-space ioctl definitions */
--
--
--#else /* __KERNEL__ */
--
--
- #define MTD_ERASE_PENDING             0x01
- #define MTD_ERASING           0x02
- #define MTD_ERASE_SUSPEND     0x04
- #define MTD_ERASE_DONE          0x08
- #define MTD_ERASE_FAILED        0x10
-+/* If the erase fails, fail_addr might indicate exactly which block failed.  If
-+   fail_addr = 0xffffffff, the failure was not at the device level or was not
-+   specific to any particular block. */
- struct erase_info {
-       struct mtd_info *mtd;
-       u_int32_t addr;
-       u_int32_t len;
-+      u_int32_t fail_addr;
-       u_long time;
-       u_long retries;
-       u_int dev;
-@@ -147,13 +69,18 @@
-       u_int32_t oobblock;  // Size of OOB blocks (e.g. 512)
-       u_int32_t oobsize;   // Amount of OOB data per block (e.g. 16)
-+      u_int32_t oobavail;  // Number of bytes in OOB area available for fs 
-       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;
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/rslib.h>
++#include <linux/slab.h>
++#include <asm/semaphore.h>
 +
-       /* Data for variable erase regions. If numeraseregions is zero,
-        * it means that the whole device has erasesize as given above. 
-        */
-@@ -163,7 +90,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 +102,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);
-@@ -187,24 +113,24 @@
-        * flash devices. The user data is one time programmable but the
-        * factory data is read only. 
-        */
--      int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
--
-+      int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
-       int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
--
--      /* This function is not yet implemented */
-+      int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
-+      int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
-       int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
-+      int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
--      /* iovec-based read/write methods. We need these especially for NAND flash,
-+      /* kvec-based read/write methods. We need these especially for NAND flash,
-          with its limited number of write cycles per erase.
-          NB: The 'count' parameter is the number of _vectors_, each of 
-          which contains an (ofs, len) tuple.
-       */
--      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);
--      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);
-+      int (*readv) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, size_t *retlen);
-+      int (*readv_ecc) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, 
-+              size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
-+      int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
-+      int (*writev_ecc) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, 
-+              size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
-       /* Sync */
-       void (*sync) (struct mtd_info *mtd);
-@@ -217,7 +143,14 @@
-       int (*suspend) (struct mtd_info *mtd);
-       void (*resume) (struct mtd_info *mtd);
-+      /* Bad block management functions */
-+      int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
-+      int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
++/* This list holds all currently allocated rs control structures */
++static LIST_HEAD (rslist);
++/* Protection for the list */
++static DECLARE_MUTEX(rslistlock);
++
++/** 
++ * rs_init - Initialize a Reed-Solomon codec
++ *
++ * @symsize:  symbol size, bits (1-8)
++ * @gfpoly:   Field generator polynomial coefficients
++ * @fcr:      first root of RS code generator polynomial, index form
++ * @prim:     primitive element to generate polynomial roots
++ * @nroots:   RS code generator polynomial degree (number of roots)
++ *
++ * Allocate a control structure and the polynom arrays for faster
++ * en/decoding. Fill the arrays according to the given parameters
++ */
++static struct rs_control *rs_init(int symsize, int gfpoly, int fcr, 
++                                 int prim, int nroots)
++{
++      struct rs_control *rs;
++      int i, j, sr, root, iprim;
 +
-       void *priv;
++      /* Allocate the control structure */
++      rs = kmalloc(sizeof (struct rs_control), GFP_KERNEL);
++      if (rs == NULL)
++              return NULL;
 +
-+      struct module *owner;
-+      int usecount;
- };
-@@ -226,44 +159,27 @@
- 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;
--      
--      ret = __get_mtd_device(mtd, num);
--
--      if (ret && ret->module && !try_inc_mod_count(ret->module))
--              return NULL;
--
--      return ret;
--}
-+extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num);
--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;
- };
- extern void register_mtd_user (struct mtd_notifier *new);
- extern int unregister_mtd_user (struct mtd_notifier *old);
--int default_mtd_writev(struct mtd_info *mtd, const struct iovec *vecs,
-+int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
-                      unsigned long count, loff_t to, size_t *retlen);
--int default_mtd_readv(struct mtd_info *mtd, struct iovec *vecs,
-+int default_mtd_readv(struct mtd_info *mtd, struct kvec *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 +192,17 @@
- #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 */
++      INIT_LIST_HEAD(&rs->list);
 +
++      rs->mm = symsize;
++      rs->nn = (1 << symsize) - 1;
++      rs->fcr = fcr;
++      rs->prim = prim;
++      rs->nroots = nroots;
++      rs->gfpoly = gfpoly;
 +
-+#ifdef CONFIG_MTD_PARTITIONS
-+void mtd_erase_callback(struct erase_info *instr);
-+#else
-+static inline void mtd_erase_callback(struct erase_info *instr)
-+{
-+      if (instr->callback)
-+              instr->callback(instr);
-+}
-+#endif
- /*
-  * Debugging macro and defines
-@@ -288,13 +214,13 @@
- #ifdef CONFIG_MTD_DEBUG
- #define DEBUG(n, args...)                     \
--      if (n <=  CONFIG_MTD_DEBUG_VERBOSE) {   \
-+      do {                                            \
-+              if (n <= CONFIG_MTD_DEBUG_VERBOSE)      \
-               printk(KERN_INFO args); \
--      }
-+      } while(0)
- #else /* CONFIG_MTD_DEBUG */
--#define DEBUG(n, args...)
--#endif /* CONFIG_MTD_DEBUG */
-+#define DEBUG(n, args...) do { } while(0)
--#endif /* __KERNEL__ */
-+#endif /* CONFIG_MTD_DEBUG */
- #endif /* __MTD_MTD_H__ */
---- linux-2.4.21/include/linux/mtd/nand.h~mtd-cvs
-+++ linux-2.4.21/include/linux/mtd/nand.h
-@@ -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
-@@ -44,27 +44,61 @@
-  *                    NAND_YAFFS_OOB
-  *  11-25-2002 tglx   Added Manufacturer code FUJITSU, NATIONAL
-  *                    Split manufacturer and device ID structures 
-+ *
-+ *  02-08-2004 tglx   added option field to nand structure for chip anomalities
-+ *  05-25-2004 tglx   added bad block table support, ST-MICRO manufacturer id
-+ *                    update of nand_chip structure description
-+ *  01-17-2005 dmarlin        added extended commands for AG-AND device and added option 
-+ *                    for BBT_AUTO_REFRESH.
-+ *  01-20-2005 dmarlin        added optional pointer to hardware specific callback for 
-+ *                    extra error status checks.
-  */
- #ifndef __LINUX_MTD_NAND_H
- #define __LINUX_MTD_NAND_H
- #include <linux/config.h>
--#include <linux/sched.h>
-+#include <linux/wait.h>
-+#include <linux/spinlock.h>
-+#include <linux/mtd/mtd.h>
--/*
-- * Searches for a NAND device
-+struct mtd_info;
-+/* Scan and identify a NAND device */
-+extern int nand_scan (struct mtd_info *mtd, int max_chips);
-+/* Free resources held by the NAND device */
-+extern void nand_release (struct mtd_info *mtd);
++      /* Allocate the arrays */
++      rs->alpha_to = kmalloc(sizeof(uint16_t) * (rs->nn + 1), GFP_KERNEL);
++      if (rs->alpha_to == NULL)
++              goto errrs;
 +
-+/* Read raw data from the device without ECC */
-+extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen);
++      rs->index_of = kmalloc(sizeof(uint16_t) * (rs->nn + 1), GFP_KERNEL);
++      if (rs->index_of == NULL)
++              goto erralp;
 +
++      rs->genpoly = kmalloc(sizeof(uint16_t) * (rs->nroots + 1), GFP_KERNEL);
++      if(rs->genpoly == NULL)
++              goto erridx;
 +
-+/* The maximum number of NAND chips in an array */
-+#define NAND_MAX_CHIPS                8
++      /* Generate Galois field lookup tables */
++      rs->index_of[0] = rs->nn;       /* log(zero) = -inf */
++      rs->alpha_to[rs->nn] = 0;       /* alpha**-inf = 0 */
++      sr = 1;
++      for (i = 0; i < rs->nn; i++) {
++              rs->index_of[sr] = i;
++              rs->alpha_to[i] = sr;
++              sr <<= 1;
++              if (sr & (1 << symsize))
++                      sr ^= gfpoly;
++              sr &= rs->nn;
++      }
++      /* If it's not primitive, exit */
++      if(sr != 1)
++              goto errpol;
 +
-+/* This constant declares the max. oobsize / page, which
-+ * is supported now. If you add a chip with bigger oobsize/page
-+ * adjust this accordingly.
-  */
--extern int nand_scan (struct mtd_info *mtd);
-+#define NAND_MAX_OOBSIZE      64
- /*
-  * Constants for hardware specific CLE/ALE/NCE function
- */
-+/* Select the chip by setting nCE to low */
- #define NAND_CTL_SETNCE       1
-+/* Deselect the chip by setting nCE to high */
- #define NAND_CTL_CLRNCE               2
-+/* Select the command latch by setting CLE to high */
- #define NAND_CTL_SETCLE               3
-+/* Deselect the command latch by setting CLE to low */
- #define NAND_CTL_CLRCLE               4
-+/* Select the address latch by setting ALE to high */
- #define NAND_CTL_SETALE               5
-+/* Deselect the address latch by setting ALE to low */
- #define NAND_CTL_CLRALE               6
-+/* Set write protection by setting WP to high. Not used! */
-+#define NAND_CTL_SETWP                7
-+/* Clear write protection by setting WP to low. Not used! */
-+#define NAND_CTL_CLRWP                8
- /*
-  * Standard NAND flash commands
-@@ -75,35 +109,132 @@
- #define NAND_CMD_READOOB      0x50
- #define NAND_CMD_ERASE1               0x60
- #define NAND_CMD_STATUS               0x70
-+#define NAND_CMD_STATUS_MULTI 0x71
- #define NAND_CMD_SEQIN                0x80
- #define NAND_CMD_READID               0x90
- #define NAND_CMD_ERASE2               0xd0
- #define NAND_CMD_RESET                0xff
-+/* Extended commands for large page devices */
-+#define NAND_CMD_READSTART    0x30
-+#define NAND_CMD_CACHEDPROG   0x15
++      /* Find prim-th root of 1, used in decoding */
++      for(iprim = 1; (iprim % prim) != 0; iprim += rs->nn);
++      /* prim-th root of 1, index form */
++      rs->iprim = iprim / prim;
 +
-+/* Extended commands for AG-AND device */
-+/* 
-+ * Note: the command for NAND_CMD_DEPLETE1 is really 0x00 but 
-+ *       there is no way to distinguish that from NAND_CMD_READ0
-+ *       until the remaining sequence of commands has been completed
-+ *       so add a high order bit and mask it off in the command.
-+ */
-+#define NAND_CMD_DEPLETE1     0x100
-+#define NAND_CMD_DEPLETE2     0x38
-+#define NAND_CMD_STATUS_MULTI 0x71
-+#define NAND_CMD_STATUS_ERROR 0x72
-+/* multi-bank error status (banks 0-3) */
-+#define NAND_CMD_STATUS_ERROR0        0x73
-+#define NAND_CMD_STATUS_ERROR1        0x74
-+#define NAND_CMD_STATUS_ERROR2        0x75
-+#define NAND_CMD_STATUS_ERROR3        0x76
-+#define NAND_CMD_STATUS_RESET 0x7f
-+#define NAND_CMD_STATUS_CLEAR 0xff
++      /* Form RS code generator polynomial from its roots */
++      rs->genpoly[0] = 1;
++      for (i = 0, root = fcr * prim; i < nroots; i++, root += prim) {
++              rs->genpoly[i + 1] = 1;
++              /* Multiply rs->genpoly[] by  @**(root + x) */
++              for (j = i; j > 0; j--) {
++                      if (rs->genpoly[j] != 0) {
++                              rs->genpoly[j] = rs->genpoly[j -1] ^ 
++                                      rs->alpha_to[rs_modnn(rs, 
++                                      rs->index_of[rs->genpoly[j]] + root)];
++                      } else
++                              rs->genpoly[j] = rs->genpoly[j - 1];
++              }
++              /* rs->genpoly[0] can never be zero */
++              rs->genpoly[0] = 
++                      rs->alpha_to[rs_modnn(rs, 
++                              rs->index_of[rs->genpoly[0]] + root)];
++      }
++      /* convert rs->genpoly[] to index form for quicker encoding */
++      for (i = 0; i <= nroots; i++)
++              rs->genpoly[i] = rs->index_of[rs->genpoly[i]];
++      return rs;
 +
-+/* Status bits */
-+#define NAND_STATUS_FAIL      0x01
-+#define NAND_STATUS_FAIL_N1   0x02
-+#define NAND_STATUS_TRUE_READY        0x20
-+#define NAND_STATUS_READY     0x40
-+#define NAND_STATUS_WP                0x80
++      /* Error exit */
++errpol:
++      kfree(rs->genpoly);
++erridx:
++      kfree(rs->index_of);
++erralp:
++      kfree(rs->alpha_to);
++errrs:
++      kfree(rs);
++      return NULL;
++}
 +
- /* 
-  * Constants for ECC_MODES
-- *
-- * NONE:      No ECC
-- * SOFT:      Software ECC 3 byte ECC per 256 Byte data
-- * HW3_256:   Hardware ECC 3 byte ECC per 256 Byte data
-- * HW3_512:   Hardware ECC 3 byte ECC per 512 Byte data
-- *
-- *
--*/
++
++/** 
++ *  free_rs - Free the rs control structure, if its not longer used
++ *
++ *  @rs:      the control structure which is not longer used by the
++ *            caller
 + */
++void free_rs(struct rs_control *rs)
++{
++      down(&rslistlock);
++      rs->users--;
++      if(!rs->users) {
++              list_del(&rs->list);
++              kfree(rs->alpha_to);
++              kfree(rs->index_of);
++              kfree(rs->genpoly);
++              kfree(rs);
++      }
++      up(&rslistlock);
++}
 +
-+/* No ECC. Usage is not recommended ! */
- #define NAND_ECC_NONE         0
-+/* Software ECC 3 byte ECC per 256 Byte data */
- #define NAND_ECC_SOFT         1
-+/* Hardware ECC 3 byte ECC per 256 Byte data */
- #define NAND_ECC_HW3_256      2
-+/* Hardware ECC 3 byte ECC per 512 Byte data */
- #define NAND_ECC_HW3_512      3
-+/* Hardware ECC 3 byte ECC per 512 Byte data */
- #define NAND_ECC_HW6_512      4
--#define NAND_ECC_DISKONCHIP   5
-+/* Hardware ECC 8 byte ECC per 512 Byte data */
-+#define NAND_ECC_HW8_512      6
-+/* Hardware ECC 12 byte ECC per 2048 Byte data */
-+#define NAND_ECC_HW12_2048    7
- /*
-  * Constants for Hardware ECC
--*/
++/** 
++ * init_rs - Find a matching or allocate a new rs control structure
++ *
++ *  @symsize: the symbol size (number of bits)
++ *  @gfpoly:  the extended Galois field generator polynomial coefficients,
++ *            with the 0th coefficient in the low order bit. The polynomial
++ *            must be primitive;
++ *  @fcr:     the first consecutive root of the rs code generator polynomial 
++ *            in index form
++ *  @prim:    primitive element to generate polynomial roots
++ *  @nroots:  RS code generator polynomial degree (number of roots)
 + */
-+/* Reset Hardware ECC for read */
- #define NAND_ECC_READ         0
-+/* Reset Hardware ECC for write */
- #define NAND_ECC_WRITE                1
-+/* Enable Hardware ECC before syndrom is read back from flash */
-+#define NAND_ECC_READSYN      2
++struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim, 
++                         int nroots)
++{
++      struct list_head        *tmp;
++      struct rs_control       *rs;
 +
-+/* Bit mask for flags passed to do_nand_read_ecc */
-+#define NAND_GET_DEVICE               0x80
++      /* Sanity checks */
++      if (symsize < 1)
++              return NULL;
++      if (fcr < 0 || fcr >= (1<<symsize))
++              return NULL;
++      if (prim <= 0 || prim >= (1<<symsize))
++              return NULL;
++      if (nroots < 0 || nroots >= (1<<symsize) || nroots > 8)
++              return NULL;
++      
++      down(&rslistlock);
 +
++      /* Walk through the list and look for a matching entry */
++      list_for_each(tmp, &rslist) {
++              rs = list_entry(tmp, struct rs_control, list);
++              if (symsize != rs->mm)
++                      continue;
++              if (gfpoly != rs->gfpoly)
++                      continue;
++              if (fcr != rs->fcr)
++                      continue;       
++              if (prim != rs->prim)
++                      continue;       
++              if (nroots != rs->nroots)
++                      continue;
++              /* We have a matching one already */
++              rs->users++;
++              goto out;
++      }
 +
-+/* Option constants for bizarre disfunctionality and real
-+*  features
-+*/
-+/* Chip can not auto increment pages */
-+#define NAND_NO_AUTOINCR      0x00000001
-+/* Buswitdh is 16 bit */
-+#define NAND_BUSWIDTH_16      0x00000002
-+/* Device supports partial programming without padding */
-+#define NAND_NO_PADDING               0x00000004
-+/* Chip has cache program function */
-+#define NAND_CACHEPRG         0x00000008
-+/* Chip has copy back function */
-+#define NAND_COPYBACK         0x00000010
-+/* AND Chip which has 4 banks and a confusing page / block 
-+ * assignment. See Renesas datasheet for further information */
-+#define NAND_IS_AND           0x00000020
-+/* Chip has a array of 4 pages which can be read without
-+ * additional ready /busy waits */
-+#define NAND_4PAGE_ARRAY      0x00000040 
-+/* Chip requires that BBT is periodically rewritten to prevent
-+ * bits from adjacent blocks from 'leaking' in altering data.
-+ * This happens with the Renesas AG-AND chips, possibly others.  */
-+#define BBT_AUTO_REFRESH      0x00000080
++      /* Create a new one */
++      rs = rs_init(symsize, gfpoly, fcr, prim, nroots);
++      if (rs) {
++              rs->users = 1;
++              list_add(&rs->list, &rslist);
++      }
++out:  
++      up(&rslistlock);
++      return rs;
++}
 +
-+/* Options valid for Samsung large page devices */
-+#define NAND_SAMSUNG_LP_OPTIONS \
-+      (NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK)
++#ifdef CONFIG_REED_SOLOMON_ENC8
++/** 
++ *  encode_rs8 - Calculate the parity for data values (8bit data width)
++ *
++ *  @rs:      the rs control structure
++ *  @data:    data field of a given type
++ *  @len:     data length 
++ *  @par:     parity data, must be initialized by caller (usually all 0)
++ *  @invmsk:  invert data mask (will be xored on data)
++ *
++ *  The parity uses a uint16_t data type to enable
++ *  symbol size > 8. The calling code must take care of encoding of the
++ *  syndrome result for storage itself.
++ */
++int encode_rs8(struct rs_control *rs, uint8_t *data, int len, uint16_t *par, 
++             uint16_t invmsk)
++{
++#include "encode_rs.c"
++}
++EXPORT_SYMBOL_GPL(encode_rs8);
++#endif
 +
-+/* Macros to identify the above */
-+#define NAND_CANAUTOINCR(chip) (!(chip->options & NAND_NO_AUTOINCR))
-+#define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING))
-+#define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG))
-+#define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK))
++#ifdef CONFIG_REED_SOLOMON_DEC8
++/** 
++ *  decode_rs8 - Decode codeword (8bit data width)
++ *
++ *  @rs:      the rs control structure
++ *  @data:    data field of a given type
++ *  @par:     received parity data field
++ *  @len:     data length
++ *  @s:               syndrome data field (if NULL, syndrome is calculated)
++ *  @no_eras: number of erasures
++ *  @eras_pos:        position of erasures, can be NULL
++ *  @invmsk:  invert data mask (will be xored on data, not on parity!)
++ *  @corr:    buffer to store correction bitmask on eras_pos
++ *
++ *  The syndrome and parity uses a uint16_t data type to enable
++ *  symbol size > 8. The calling code must take care of decoding of the
++ *  syndrome result and the received parity before calling this code.
++ */
++int decode_rs8(struct rs_control *rs, uint8_t *data, uint16_t *par, int len,
++             uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk, 
++             uint16_t *corr)
++{
++#include "decode_rs.c"
++}
++EXPORT_SYMBOL_GPL(decode_rs8);
++#endif
++
++#ifdef CONFIG_REED_SOLOMON_ENC16
++/**
++ *  encode_rs16 - Calculate the parity for data values (16bit data width)
++ *
++ *  @rs:      the rs control structure
++ *  @data:    data field of a given type
++ *  @len:     data length 
++ *  @par:     parity data, must be initialized by caller (usually all 0)
++ *  @invmsk:  invert data mask (will be xored on data, not on parity!)
++ *
++ *  Each field in the data array contains up to symbol size bits of valid data.
++ */
++int encode_rs16(struct rs_control *rs, uint16_t *data, int len, uint16_t *par, 
++      uint16_t invmsk)
++{
++#include "encode_rs.c"
++}
++EXPORT_SYMBOL_GPL(encode_rs16);
++#endif
++
++#ifdef CONFIG_REED_SOLOMON_DEC16
++/** 
++ *  decode_rs16 - Decode codeword (16bit data width)
++ *
++ *  @rs:      the rs control structure
++ *  @data:    data field of a given type
++ *  @par:     received parity data field
++ *  @len:     data length
++ *  @s:               syndrome data field (if NULL, syndrome is calculated)
++ *  @no_eras: number of erasures
++ *  @eras_pos:        position of erasures, can be NULL
++ *  @invmsk:  invert data mask (will be xored on data, not on parity!) 
++ *  @corr:    buffer to store correction bitmask on eras_pos
++ *
++ *  Each field in the data array contains up to symbol size bits of valid data.
++ */
++int decode_rs16(struct rs_control *rs, uint16_t *data, uint16_t *par, int len,
++              uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk, 
++              uint16_t *corr)
++{
++#include "decode_rs.c"
++}
++EXPORT_SYMBOL_GPL(decode_rs16);
++#endif
 +
-+/* Mask to zero out the chip options, which come from the id table */
-+#define NAND_CHIPOPTIONS_MSK  (0x0000ffff & ~NAND_NO_AUTOINCR)
++EXPORT_SYMBOL_GPL(init_rs);
++EXPORT_SYMBOL_GPL(free_rs);
 +
-+/* Non chip related options */
-+/* Use a flash based bad block table. This option is passed to the
-+ * default bad block table function. */
-+#define NAND_USE_FLASH_BBT    0x00010000
-+/* The hw ecc generator provides a syndrome instead a ecc value on read 
-+ * This can only work if we have the ecc bytes directly behind the 
-+ * data bytes. Applies for DOC and AG-AND Renesas HW Reed Solomon generators */
-+#define NAND_HWECC_SYNDROME   0x00020000
-+/* This option skips the bbt scan during initialization. */
-+#define NAND_SKIP_BBTSCAN     0x00040000
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Reed Solomon encoder/decoder");
++MODULE_AUTHOR("Phil Karn, Thomas Gleixner");
 +
-+/* Options set by nand scan */
-+/* Nand scan has allocated oob_buf */
-+#define NAND_OOBBUF_ALLOC     0x40000000
-+/* Nand scan has allocated data_buf */
-+#define NAND_DATABUF_ALLOC    0x80000000
+--- linux-2.4.21/mm/swap.c~swap-performance
++++ linux-2.4.21/mm/swap.c
+@@ -28,7 +28,7 @@
+ int page_cluster;
+ pager_daemon_t pager_daemon = {
+-      512,    /* base number for calculating the number of tries */
++      128,    /* base number for calculating the number of tries */
+       SWAP_CLUSTER_MAX,       /* minimum number of tries */
+       8,      /* do swap I/O in clusters of this size */
+ };
+--- linux-2.4.21/mm/vmalloc.c~vmalloc
++++ linux-2.4.21/mm/vmalloc.c
+@@ -183,6 +183,9 @@
+               return NULL;
+       size += PAGE_SIZE;
++#ifdef VMALLOC_ALIGN
++      size = (size + VMALLOC_ALIGN - 1) & ~(VMALLOC_ALIGN - 1);
++#endif
+       if (!size) {
+               kfree (area);
+               return NULL;
+--- linux-2.4.21/net/bluetooth/Config.in~bluetooth
++++ linux-2.4.21/net/bluetooth/Config.in
+@@ -13,6 +13,8 @@
+       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
+--- linux-2.4.21/net/bluetooth/Makefile~bluetooth
++++ linux-2.4.21/net/bluetooth/Makefile
+@@ -5,7 +5,7 @@
+ O_TARGET    := bluetooth.o
+ list-multi  := bluez.o
+-export-objs := syms.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
+@@ -15,6 +15,8 @@
+ 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
+@@ -24,6 +26,14 @@
+ obj-y += bnep/bnep.o
+ endif
++ifeq ($(CONFIG_BLUEZ_CMTP),y)
++obj-y += cmtp/cmtp.o
++endif
 +
-       
- /*
-+ * nand_state_t - chip states
-  * Enumeration for NAND flash chip state
++ifeq ($(CONFIG_BLUEZ_HIDP),y)
++obj-y += hidp/hidp.o
++endif
++
+ include $(TOPDIR)/Rules.make
+ bluez.o: $(bluez-objs)
+--- linux-2.4.21/net/bluetooth/af_bluetooth.c~bluetooth
++++ linux-2.4.21/net/bluetooth/af_bluetooth.c
+@@ -27,7 +27,7 @@
+  *
+  * $Id$
   */
- typedef enum {
-@@ -111,73 +242,137 @@
-       FL_READING,
-       FL_WRITING,
-       FL_ERASING,
--      FL_SYNCING
-+      FL_SYNCING,
-+      FL_CACHEDPRG,
- } nand_state_t;
+-#define VERSION "2.2"
++#define VERSION "2.4"
  
-+/* Keep gcc happy */
-+struct nand_chip;
+ #include <linux/config.h>
+ #include <linux/module.h>
+@@ -57,7 +57,7 @@
+ #endif
  
--/*
-- * NAND Private Flash Chip Data
-- *
-- * Structure overview:
-- *
-- *  IO_ADDR_R - address to read the 8 I/O lines of the flash device 
-- *
-- *  IO_ADDR_W - address to write the 8 I/O lines of the flash device 
-- *
-- *  hwcontrol - hardwarespecific function for accesing control-lines
-- *
-- *  dev_ready - hardwarespecific function for accesing device ready/busy line
-- *
-- *  waitfunc - hardwarespecific function for wait on ready
-- *
-- *  calculate_ecc - function for ecc calculation or readback from ecc hardware
-- *
-- *  correct_data - function for ecc correction, matching to ecc generator (sw/hw)
-- *
-- *  enable_hwecc - function to enable (reset) hardware ecc generator
-- *
-- *  eccmod - mode of ecc: see constants
-- *
-- *  eccsize - databytes used per ecc-calculation
-- *
-- *  chip_delay - chip dependent delay for transfering data from array to read regs (tR)
-- *
-- *  chip_lock - spinlock used to protect access to this structure
-- *
-- *  wq - wait queue to sleep on if a NAND operation is in progress
-- *
-- *  state - give the current state of the NAND device
-- *
-- *  page_shift - number of address bits in a page (column address bits)
-- *
-- *  data_buf - data buffer passed to/from MTD user modules
-- *
-- *  data_cache - data cache for redundant page access and shadow for
-- *             ECC failure
-- *
-- *  cache_page - number of last valid page in page_cache 
-+/**
-+ * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independend devices
-+ * @lock:               protection lock  
-+ * @active:           the mtd device which holds the controller currently
-  */
-+struct nand_hw_control {
-+      spinlock_t       lock;
-+      struct nand_chip *active;
-+};
+ /* Bluetooth sockets */
+-#define BLUEZ_MAX_PROTO       5
++#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)
+@@ -221,12 +221,11 @@
+ unsigned int bluez_sock_poll(struct file * file, struct socket *sock, poll_table *wait)
+ {
+       struct sock *sk = sock->sk;
+-      unsigned int mask;
++      unsigned int mask = 0;
+       BT_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;
+@@ -242,9 +241,11 @@
+       if (sk->state == BT_CLOSED)
+               mask |= POLLHUP;
+-      if (sk->state == BT_CONNECT || sk->state == BT_CONNECT2)
++      if (sk->state == BT_CONNECT ||
++                      sk->state == BT_CONNECT2 ||
++                      sk->state == BT_CONFIG)
+               return mask;
+-      
 +
-+/**
-+ * struct nand_chip - NAND Private Flash Chip Data
-+ * @IO_ADDR_R:                [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device 
-+ * @IO_ADDR_W:                [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device 
-+ * @read_byte:                [REPLACEABLE] read one byte from the chip
-+ * @write_byte:               [REPLACEABLE] write one byte to the chip
-+ * @read_word:                [REPLACEABLE] read one word from the chip
-+ * @write_word:               [REPLACEABLE] write one word to the chip
-+ * @write_buf:                [REPLACEABLE] write data from the buffer to the chip
-+ * @read_buf:         [REPLACEABLE] read data from the chip into the buffer
-+ * @verify_buf:               [REPLACEABLE] verify buffer contents against the chip data
-+ * @select_chip:      [REPLACEABLE] select chip nr
-+ * @block_bad:                [REPLACEABLE] check, if the block is bad
-+ * @block_markbad:    [REPLACEABLE] mark the block bad
-+ * @hwcontrol:                [BOARDSPECIFIC] hardwarespecific function for accesing control-lines
-+ * @dev_ready:                [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line
-+ *                    If set to NULL no access to ready/busy is available and the ready/busy information
-+ *                    is read from the chip status register
-+ * @cmdfunc:          [REPLACEABLE] hardwarespecific function for writing commands to the chip
-+ * @waitfunc:         [REPLACEABLE] hardwarespecific function for wait on ready
-+ * @calculate_ecc:    [REPLACEABLE] function for ecc calculation or readback from ecc hardware
-+ * @correct_data:     [REPLACEABLE] function for ecc correction, matching to ecc generator (sw/hw)
-+ * @enable_hwecc:     [BOARDSPECIFIC] function to enable (reset) hardware ecc generator. Must only
-+ *                    be provided if a hardware ECC is available
-+ * @erase_cmd:                [INTERN] erase command write function, selectable due to AND support
-+ * @scan_bbt:         [REPLACEABLE] function to scan bad block table
-+ * @eccmode:          [BOARDSPECIFIC] mode of ecc, see defines 
-+ * @eccsize:          [INTERN] databytes used per ecc-calculation
-+ * @eccbytes:                 [INTERN] number of ecc bytes per ecc-calculation step
-+ * @eccsteps:         [INTERN] number of ecc calculation steps per page
-+ * @chip_delay:               [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR)
-+ * @chip_lock:                [INTERN] spinlock used to protect access to this structure and the chip
-+ * @wq:                       [INTERN] wait queue to sleep on if a NAND operation is in progress
-+ * @state:            [INTERN] the current state of the NAND device
-+ * @page_shift:               [INTERN] number of address bits in a page (column address bits)
-+ * @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock
-+ * @bbt_erase_shift:  [INTERN] number of address bits in a bbt entry
-+ * @chip_shift:               [INTERN] number of address bits in one chip
-+ * @data_buf:         [INTERN] internal buffer for one page + oob 
-+ * @oob_buf:          [INTERN] oob buffer for one eraseblock
-+ * @oobdirty:         [INTERN] indicates that oob_buf must be reinitialized
-+ * @data_poi:         [INTERN] pointer to a data buffer
-+ * @options:          [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about
-+ *                    special functionality. See the defines for further explanation
-+ * @badblockpos:      [INTERN] position of the bad block marker in the oob area
-+ * @numchips:         [INTERN] number of physical chips
-+ * @chipsize:         [INTERN] the size of one chip for multichip arrays
-+ * @pagemask:         [INTERN] page number mask = number of (pages / chip) - 1
-+ * @pagebuf:          [INTERN] holds the pagenumber which is currently in data_buf
-+ * @autooob:          [REPLACEABLE] the default (auto)placement scheme
-+ * @bbt:              [INTERN] bad block table pointer
-+ * @bbt_td:           [REPLACEABLE] bad block table descriptor for flash lookup
-+ * @bbt_md:           [REPLACEABLE] bad block table mirror descriptor
-+ * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial bad block scan 
-+ * @controller:               [OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices
-+ * @priv:             [OPTIONAL] pointer to private chip date
-+ * @errstat:          [OPTIONAL] hardware specific function to perform additional error status checks 
-+ *                    (determine if errors are correctable)
-+ */
-+ 
- struct nand_chip {
--      unsigned long   IO_ADDR_R;
--      unsigned long   IO_ADDR_W;
--      void            (*hwcontrol)(int cmd);
--      int             (*dev_ready)(void);
-+      void  __iomem   *IO_ADDR_R;
-+      void  __iomem   *IO_ADDR_W;
-+      
-+      u_char          (*read_byte)(struct mtd_info *mtd);
-+      void            (*write_byte)(struct mtd_info *mtd, u_char byte);
-+      u16             (*read_word)(struct mtd_info *mtd);
-+      void            (*write_word)(struct mtd_info *mtd, u16 word);
-+      
-+      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, loff_t ofs, int getchip);
-+      int             (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
-+      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);
-+      int             (*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);
-+      void            (*erase_cmd)(struct mtd_info *mtd, int page);
-+      int             (*scan_bbt)(struct mtd_info *mtd);
-       int             eccmode;
-       int             eccsize;
-+      int             eccbytes;
-+      int             eccsteps;
-       int             chip_delay;
-       spinlock_t      chip_lock;
-       wait_queue_head_t wq;
-       nand_state_t    state;
-       int             page_shift;
-+      int             phys_erase_shift;
-+      int             bbt_erase_shift;
-+      int             chip_shift;
-       u_char          *data_buf;
-+      u_char          *oob_buf;
-+      int             oobdirty;
-       u_char          *data_poi;
--      u_char          *data_cache;
--      int             cache_page;
-+      unsigned int    options;
-+      int             badblockpos;
-+      int             numchips;
-+      unsigned long   chipsize;
-+      int             pagemask;
-+      int             pagebuf;
-+      struct nand_oobinfo     *autooob;
-+      uint8_t         *bbt;
-+      struct nand_bbt_descr   *bbt_td;
-+      struct nand_bbt_descr   *bbt_md;
-+      struct nand_bbt_descr   *badblock_pattern;
-+      struct nand_hw_control  *controller;
-+      void            *priv;
-+      int             (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
- };
+       if (sock_writeable(sk))
+               mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+       else
+@@ -253,39 +254,35 @@
+       return mask;
+ }
  
- /*
-@@ -187,46 +382,35 @@
- #define NAND_MFR_SAMSUNG      0xec
- #define NAND_MFR_FUJITSU      0x04
- #define NAND_MFR_NATIONAL     0x8f
-+#define NAND_MFR_RENESAS      0x07
-+#define NAND_MFR_STMICRO      0x20
+-int bluez_sock_w4_connect(struct sock *sk, int flags)
++int bluez_sock_wait_state(struct sock *sk, int state, unsigned long timeo)
+ {
+       DECLARE_WAITQUEUE(wait, current);
+-      long timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
+       int err = 0;
  
--/*
-- * NAND Flash Device ID Structure
-- *
-- * Structure overview:
-- *
-- *  name - Identify the device type
-- *
-- *  id -  device ID code
-- *
-- *  chipshift - total number of address bits for the device which
-- *              is used to calculate address offsets and the total
-- *              number of bytes the device is capable of.
-- *
-- *  page256 - denotes if flash device has 256 byte pages or not.
-- *
-- *  pageadrlen - number of bytes minus one needed to hold the
-- *               complete address into the flash array. Keep in
-- *               mind that when a read or write is done to a
-- *               specific address, the address is input serially
-- *               8 bits at a time. This structure member is used
-- *               by the read/write routines as a loop index for
-- *               shifting the address out 8 bits at a time.
-+/**
-+ * struct nand_flash_dev - NAND Flash Device ID Structure
-  *
-- *  erasesize - size of an erase block in the flash device.
-+ * @name:     Identify the device type
-+ * @id:       device ID code
-+ * @pagesize:         Pagesize in bytes. Either 256 or 512 or 0
-+ *            If the pagesize is 0, then the real pagesize 
-+ *            and the eraseize are determined from the
-+ *            extended id bytes in the chip
-+ * @erasesize:        Size of an erase block in the flash device.
-+ * @chipsize:         Total chipsize in Mega Bytes
-+ * @options:  Bitfield to store chip relevant options
-  */
- struct nand_flash_dev {
--      char * name;
-+      char *name;
-       int id;
--      int chipshift;
-+      unsigned long pagesize;
-+      unsigned long chipsize;
-       unsigned long erasesize;
--      char page256;
-+      unsigned long options;
- };
+       BT_DBG("sk %p", sk);
  
--/*
-- * NAND Flash Manufacturer ID Structure
-- *
-- *  name - Manufacturer name
-- *
-- *  id - manufacturer ID code of device.
-+/**
-+ * struct nand_manufacturers - NAND Flash Manufacturer ID Structure
-+ * @name:     Manufacturer name
-+ * @id:       manufacturer ID code of device.
- */
- struct nand_manufacturers {
-       int id;
-@@ -236,39 +420,88 @@
- extern struct nand_flash_dev nand_flash_ids[];
- extern struct nand_manufacturers nand_manuf_ids[];
+       add_wait_queue(sk->sleep, &wait);
+-      while (sk->state != BT_CONNECTED) {
++      while (sk->state != state) {
+               set_current_state(TASK_INTERRUPTIBLE);
++
+               if (!timeo) {
+                       err = -EAGAIN;
+                       break;
+               }
  
--/*
--* Constants for oob configuration
--*/
--#define NAND_BADBLOCK_POS             5
-+/** 
-+ * struct nand_bbt_descr - bad block table descriptor
-+ * @options:  options for this descriptor
-+ * @pages:    the page(s) where we find the bbt, used with option BBT_ABSPAGE
-+ *            when bbt is searched, then we store the found bbts pages here.
-+ *            Its an array and supports up to 8 chips now
-+ * @offs:     offset of the pattern in the oob area of the page
-+ * @veroffs:  offset of the bbt version counter in the oob are of the page
-+ * @version:  version read from the bbt page during scan
-+ * @len:      length of the pattern, if 0 no pattern check is performed
-+ * @maxblocks:        maximum number of blocks to search for a bbt. This number of
-+ *            blocks is reserved at the end of the device where the tables are 
-+ *            written.
-+ * @reserved_block_code: if non-0, this pattern denotes a reserved (rather than
-+ *              bad) block in the stored bbt
-+ * @pattern:  pattern to identify bad block table or factory marked good / 
-+ *            bad blocks, can be NULL, if len = 0
-+ *
-+ * Descriptor for the bad block table marker and the descriptor for the 
-+ * pattern which identifies good and bad blocks. The assumption is made
-+ * that the pattern and the version count are always located in the oob area
-+ * of the first block.
-+ */
-+struct nand_bbt_descr {
-+      int     options;
-+      int     pages[NAND_MAX_CHIPS];
-+      int     offs;
-+      int     veroffs;
-+      uint8_t version[NAND_MAX_CHIPS];
-+      int     len;
-+      int     maxblocks;
-+      int     reserved_block_code;
-+      uint8_t *pattern;
-+};
++              if (signal_pending(current)) {
++                      err = sock_intr_errno(timeo);
++                      break;
++              }
++
+               release_sock(sk);
+               timeo = schedule_timeout(timeo);
+               lock_sock(sk);
  
--#define NAND_NONE_OOB                 0
--#define NAND_JFFS2_OOB                        1
--#define NAND_YAFFS_OOB                        2
-+/* Options for the bad block table descriptors */
+-              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;
+-              }
+       }
+       set_current_state(TASK_RUNNING);
+       remove_wait_queue(sk->sleep, &wait);
+--- linux-2.4.21/net/bluetooth/bnep/core.c~bluetooth
++++ linux-2.4.21/net/bluetooth/bnep/core.c
+@@ -63,7 +63,7 @@
+ #define BT_DBG(D...)
+ #endif
  
--#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
-+/* The number of bits used per block in the bbt on the device */
-+#define NAND_BBT_NRBITS_MSK   0x0000000F
-+#define NAND_BBT_1BIT         0x00000001
-+#define NAND_BBT_2BIT         0x00000002
-+#define NAND_BBT_4BIT         0x00000004
-+#define NAND_BBT_8BIT         0x00000008
-+/* The bad block table is in the last good block of the device */
-+#define       NAND_BBT_LASTBLOCK      0x00000010
-+/* The bbt is at the given page, else we must scan for the bbt */
-+#define NAND_BBT_ABSPAGE      0x00000020
-+/* The bbt is at the given page, else we must scan for the bbt */
-+#define NAND_BBT_SEARCH               0x00000040
-+/* bbt is stored per chip on multichip devices */
-+#define NAND_BBT_PERCHIP      0x00000080
-+/* bbt has a version counter at offset veroffs */
-+#define NAND_BBT_VERSION      0x00000100
-+/* Create a bbt if none axists */
-+#define NAND_BBT_CREATE               0x00000200
-+/* Search good / bad pattern through all pages of a block */
-+#define NAND_BBT_SCANALLPAGES 0x00000400
-+/* Scan block empty during good / bad block scan */
-+#define NAND_BBT_SCANEMPTY    0x00000800
-+/* Write bbt if neccecary */
-+#define NAND_BBT_WRITE                0x00001000
-+/* Read and write back block contents when writing bbt */
-+#define NAND_BBT_SAVECONTENT  0x00002000
-+/* Search good / bad pattern on the first and the second page */
-+#define NAND_BBT_SCAN2NDPAGE  0x00004000
+-#define VERSION "1.1"
++#define VERSION "1.2"
  
--#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
-+/* The maximum number of blocks to scan for a bbt */
-+#define NAND_BBT_SCAN_MAXBLOCKS       4
+ static LIST_HEAD(bnep_session_list);
+ static DECLARE_RWSEM(bnep_session_sem);
+@@ -113,13 +113,28 @@
+       return bnep_send(s, &rsp, sizeof(rsp));
+ }
  
--#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
-+extern int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd);
-+extern int nand_update_bbt (struct mtd_info *mtd, loff_t offs);
-+extern int nand_default_bbt (struct mtd_info *mtd);
-+extern int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt);
-+extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt);
-+extern int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
-+                             size_t * retlen, u_char * buf, u_char * oob_buf,
-+                             struct nand_oobinfo *oobsel, int flags);
++#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;
  
--#define NAND_JFFS2_OOB8_FSDAPOS               6
--#define NAND_JFFS2_OOB16_FSDAPOS      8
--#define NAND_JFFS2_OOB8_FSDALEN               2
--#define NAND_JFFS2_OOB16_FSDALEN      8
-+/*
-+* Constants for oob configuration
-+*/
-+#define NAND_SMALL_BADBLOCK_POS               5
-+#define NAND_LARGE_BADBLOCK_POS               0
+       if (len < 2)
+               return -EILSEQ;
+-      
++
+       n = ntohs(get_unaligned(data));
+       data++; len -= 2;
  
- #endif /* __LINUX_MTD_NAND_H */
---- linux-2.4.21/include/linux/mtd/nand_ecc.h~mtd-cvs
-+++ linux-2.4.21/include/linux/mtd/nand_ecc.h
-@@ -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.
-  */
+@@ -141,9 +156,13 @@
+                       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);
+@@ -160,7 +179,7 @@
+       if (len < 2)
+               return -EILSEQ;
+-      
++
+       n = ntohs(get_unaligned((u16 *) data)); 
+       data += 2; len -= 2;
+@@ -214,7 +233,7 @@
+       int err = 0;
+       data++; len--;
+-      
++
+       switch (cmd) {
+       case BNEP_CMD_NOT_UNDERSTOOD:
+       case BNEP_SETUP_CONN_REQ:
+@@ -268,13 +287,13 @@
+                       /* Unknown extension, skip it. */
+                       break;
+               }
+-      
++
+               if (!skb_pull(skb, h->len)) {
+                       err = -EILSEQ;
+                       break;
+               }
+       } while (!err && (h->type & BNEP_EXT_HEADER));
+-      
++
+       return err;
+ }
+@@ -300,7 +319,7 @@
+       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);
+@@ -326,7 +345,7 @@
+                       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);
+@@ -342,7 +361,7 @@
+       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);
+@@ -362,7 +381,7 @@
  
--/*
-- * 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__
+       memcpy(__skb_put(nskb, skb->len), skb->data, skb->len);
+       kfree_skb(skb);
+-      
 +
-+struct mtd_info;
+       s->stats.rx_packets++;
+       nskb->dev       = dev;
+       nskb->ip_summed = CHECKSUM_UNNECESSARY;
+@@ -426,9 +445,9 @@
+ 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);
+@@ -453,16 +472,16 @@
  
- /*
-  * Calculate 3 byte ECC code for 256 byte block
-  */
--void nand_calculate_ecc (const u_char *dat, u_char *ecc_code);
-+int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
+       BT_DBG("");
  
- /*
-  * 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__ */
---- linux-2.4.21/include/linux/mtd/nftl.h~mtd-cvs
-+++ linux-2.4.21/include/linux/mtd/nftl.h
-@@ -1,81 +1,16 @@
--
--/* 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>
-+ */
+-        daemonize(); reparent_to_init();
++      daemonize(); reparent_to_init();
  
- #ifndef __MTD_NFTL_H__
- #define __MTD_NFTL_H__
+-        sprintf(current->comm, "kbnepd %s", dev->name);
+-      
+-        sigfillset(&current->blocked);
++      sprintf(current->comm, "kbnepd %s", dev->name);
++
++      sigfillset(&current->blocked);
+       flush_signals(current);
  
--#ifndef __BOOT__
- #include <linux/mtd/mtd.h>
--#endif
--
--/* Block Control Information */
--
--struct nftl_bci {
--      unsigned char ECCSig[6];
--      __u8 Status;
--      __u8 Status1;
--}__attribute__((packed));
--
--/* Unit Control Information */
--
--struct nftl_uci0 {
--      __u16 VirtUnitNum;
--      __u16 ReplUnitNum;
--      __u16 SpareVirtUnitNum;
--      __u16 SpareReplUnitNum;
--} __attribute__((packed));
--
--struct nftl_uci1 {
--      __u32 WearInfo;
--      __u16 EraseMark;
--      __u16 EraseMark1;
--} __attribute__((packed));
--
--struct nftl_uci2 {
--        __u16 FoldMark;
--        __u16 FoldMark1;
--      __u32 unused;
--} __attribute__((packed));
--
--union nftl_uci {
--      struct nftl_uci0 a;
--      struct nftl_uci1 b;
--      struct nftl_uci2 c;
--};
--
--struct nftl_oob {
--      struct nftl_bci b;
--      union nftl_uci u;
--};
--
--/* NFTL Media Header */
--
--struct NFTLMediaHeader {
--      char DataOrgID[6];
--      __u16 NumEraseUnits;
--      __u16 FirstPhysicalEUN;
--      __u32 FormattedSize;
--      unsigned char UnitSizeFactor;
--} __attribute__((packed));
--
--#define MAX_ERASE_ZONES (8192 - 512)
--
--#define ERASE_MARK 0x3c69
--#define SECTOR_FREE 0xff
--#define SECTOR_USED 0x55
--#define SECTOR_IGNORE 0x11
--#define SECTOR_DELETED 0x00
--
--#define FOLD_MARK_IN_PROGRESS 0x5555
--
--#define ZONE_GOOD 0xff
--#define ZONE_BAD_ORIGINAL 0
--#define ZONE_BAD_MARKED 7
-+#include <linux/mtd/blktrans.h>
+       current->nice = -15;
  
--#ifdef __KERNEL__
-+#include <mtd/nftl-user.h>
+-        set_fs(KERNEL_DS);
++      set_fs(KERNEL_DS);
  
- /* these info are used in ReplUnitTable */
- #define BLOCK_NIL          0xffff /* last block of a chain */
-@@ -84,8 +19,7 @@
- #define BLOCK_RESERVED     0xfffc /* bios block or bad block */
+       init_waitqueue_entry(&wait, current);
+       add_wait_queue(sk->sleep, &wait);
+@@ -477,13 +496,13 @@
  
- struct NFTLrecord {
--      struct mtd_info *mtd;
--      struct semaphore mutex;
-+      struct mtd_blktrans_dev mbd;
-       __u16 MediaUnit, SpareMediaUnit;
-       __u32 EraseSize;
-       struct NFTLMediaHeader MediaHdr;
-@@ -97,13 +31,13 @@
-       __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 */
-         unsigned int nb_blocks;               /* number of physical blocks */
-         unsigned int nb_boot_blocks;  /* number of blocks used by the bios */
-         struct erase_info instr;
-+      struct nand_oobinfo oobinfo;
- };
+               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);
+@@ -547,28 +566,19 @@
+       s->sock  = sock;
+       s->role  = req->role;
+       s->state = BT_CONNECTED;
+-      
++
+       s->msg.msg_flags = MSG_NOSIGNAL;
  
- int NFTL_mount(struct NFTLrecord *s);
-@@ -114,9 +48,7 @@
+ #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 */
+-
+-      /* (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);
++      bnep_set_default_proto_filter(s);
+ #endif
+-      
++
+       dev->init = bnep_net_init;
+       dev->priv = s;
+       err = register_netdev(dev);
+@@ -577,7 +587,7 @@
+       }
  
- #define MAX_NFTLS 16
--#define MAX_SECTORS_PER_UNIT 32
-+#define MAX_SECTORS_PER_UNIT 64
- #define NFTL_PARTN_BITS 4
--#endif /* __KERNEL__ */
--
- #endif /* __MTD_NFTL_H__ */
---- linux-2.4.21/include/linux/mtd/partitions.h~mtd-cvs
-+++ linux-2.4.21/include/linux/mtd/partitions.h
-@@ -5,7 +5,7 @@
-  *
-  * This code is GPL
-  *
-- * $Id$
-+ * $Id$
-  */
+       __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. */
+@@ -680,6 +690,8 @@
  
- #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 */
- };
+ static int __init bnep_init_module(void)
+ {
++      l2cap_load();
++
+       bnep_crc32_init();
+       bnep_sock_init();
  
-@@ -49,8 +50,26 @@
- #define MTDPART_SIZ_FULL      (0)
+--- linux-2.4.21/net/bluetooth/bnep/sock.c~bluetooth
++++ linux-2.4.21/net/bluetooth/bnep/sock.c
+@@ -55,36 +55,6 @@
+ #define BT_DBG( A... )
+ #endif
  
+-static inline struct socket *socki_lookup(struct inode *inode)
+-{
+-      return &inode->u.socket_i;
+-}
+-
+-static struct socket *sockfd_lookup(int fd, int *err)
+-{
+-      struct file *file;
+-      struct inode *inode;
+-      struct socket *sock;
+-
+-      if (!(file = fget(fd))) {
+-              *err = -EBADF;
+-              return NULL;
+-      }
+-
+-      inode = file->f_dentry->d_inode;
+-      if (!inode->i_sock || !(sock = socki_lookup(inode))) {
+-              *err = -ENOTSOCK;
+-              fput(file);
+-              return NULL;
+-      }
+-
+-      if (sock->file != file) {
+-              printk(KERN_ERR "socki_lookup: socket file changed!\n");
+-              sock->file = file;
+-      }
+-      return sock;
+-}
+- 
+ static int bnep_sock_release(struct socket *sock)
+ {
+       struct sock *sk = sock->sk;
+@@ -124,8 +94,10 @@
+               if (!nsock)
+                       return err;
  
--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 *);
+-              if (nsock->sk->state != BT_CONNECTED)
++              if (nsock->sk->state != BT_CONNECTED) {
++                      fput(nsock->file);
+                       return -EBADFD;
++              }
  
-+/*
-+ * Functions dealing with the various ways of partitioning the space
-+ */
+               err = bnep_add_connection(&ca, nsock);
+               if (!err) {
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/cmtp/Config.in
+@@ -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
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/cmtp/Makefile
+@@ -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
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/cmtp/capi.c
+@@ -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);
 +
-+struct mtd_part_parser {
++      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);
++}
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/cmtp/cmtp.h
+@@ -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 module *owner;
-+      const char *name;
-+      int (*parse_fn)(struct mtd_info *, struct mtd_partition **, unsigned long);
++
++      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];
 +};
 +
-+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);
++struct cmtp_application {
++      struct list_head list;
 +
-+#define put_partition_parser(p) do { module_put((p)->owner); } while(0)
++      unsigned long state;
++      int err;
 +
- #endif
++      __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 */
 --- /dev/null
-+++ linux-2.4.21/include/linux/mtd/physmap.h
-@@ -0,0 +1,61 @@
-+/*
-+ * For boards with physically mapped flash and using 
-+ * drivers/mtd/maps/physmap.c mapping driver.
-+ *
-+ * $Id$
-+ *
-+ * 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.
-+ *
-+ */
++++ linux-2.4.21/net/bluetooth/cmtp/core.c
+@@ -0,0 +1,515 @@
++/* 
++   CMTP implementation for Linux Bluetooth stack (BlueZ).
++   Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
 +
-+#ifndef __LINUX_MTD_PHYSMAP__
++   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>
 +
-+#if defined(CONFIG_MTD_PHYSMAP) 
++#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/mtd/mtd.h>
-+#include <linux/mtd/map.h>
-+#include <linux/mtd/partitions.h>
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/l2cap.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;
++#include "cmtp.h"
 +
-+/*
-+ * Board needs to specify the exact mapping during their setup time.
-+ */
-+static inline void physmap_configure(unsigned long addr, unsigned long size, int bankwidth, void (*set_vpp)(struct map_info *, int) )
++#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)
 +{
-+      physmap_map.phys = addr;
-+      physmap_map.size = size;
-+      physmap_map.bankwidth = bankwidth;
-+      physmap_map.set_vpp = set_vpp;
++      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;
 +}
 +
-+#if defined(CONFIG_MTD_PARTITIONS)
++static void __cmtp_link_session(struct cmtp_session *session)
++{
++      MOD_INC_USE_COUNT;
++      list_add(&session->list, &cmtp_session_list);
++}
 +
-+/*
-+ * 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);
++static void __cmtp_unlink_session(struct cmtp_session *session)
++{
++      list_del(&session->list);
++      MOD_DEC_USE_COUNT;
++}
 +
-+#endif /* defined(CONFIG_MTD_PARTITIONS) */
-+#endif /* defined(CONFIG_MTD) */
++static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci)
++{
++      bacpy(&ci->bdaddr, &session->bdaddr);
 +
-+#endif /* __LINUX_MTD_PHYSMAP__ */
++      ci->flags = session->flags;
++      ci->state = session->state;
 +
---- /dev/null
-+++ linux-2.4.21/include/linux/mtd/plat-ram.h
-@@ -0,0 +1,35 @@
-+/* linux/include/mtd/plat-ram.h
-+ *
-+ * (c) 2004 Simtec Electronics
-+ *    http://www.simtec.co.uk/products/SWLINUX/
-+ *    Ben Dooks <ben@simtec.co.uk>
-+ *
-+ * Generic platform device based RAM map
-+ *
-+ * $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
-+ * published by the Free Software Foundation.
-+ *
-+ */
++      ci->num = session->num;
++}
 +
-+#ifndef __LINUX_MTD_PLATRAM_H
-+#define __LINUX_MTD_PLATRAM_H __FILE__
 +
-+#define PLATRAM_RO (0)
-+#define PLATRAM_RW (1)
++static inline int cmtp_alloc_block_id(struct cmtp_session *session)
++{
++      int i, id = -1;
 +
-+struct platdata_mtd_ram {
-+      char                    *mapname;
-+      char                   **probes;
-+      struct mtd_partition    *partitions;
-+      int                      nr_partitions;
-+      int                      bankwidth;
++      for (i = 0; i < 16; i++)
++              if (!test_and_set_bit(i, &session->blockids)) {
++                      id = i;
++                      break;
++              }
 +
-+      /* control callbacks */
++      return id;
++}
 +
-+      void    (*set_rw)(struct device *dev, int to);
-+};
++static inline void cmtp_free_block_id(struct cmtp_session *session, int id)
++{
++      clear_bit(id, &session->blockids);
++}
 +
-+#endif /* __LINUX_MTD_PLATRAM_H */
---- /dev/null
-+++ linux-2.4.21/include/linux/mtd/xip.h
-@@ -0,0 +1,107 @@
-+/*
-+ * MTD primitives for XIP support
-+ *
-+ * Author:    Nicolas Pitre
-+ * Created:   Nov 2, 2004
-+ * Copyright: (C) 2004 MontaVista Software, Inc.
-+ *
-+ * This XIP support for MTD has been loosely inspired
-+ * by an earlier patch authored by David Woodhouse.
-+ *
-+ * 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$
-+ */
++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;
 +
-+#ifndef __LINUX_MTD_XIP_H__
-+#define __LINUX_MTD_XIP_H__
++      BT_DBG("session %p buf %p count %d", session, buf, count);
 +
-+#include <linux/config.h>
++      size = (skb) ? skb->len + count : count;
 +
-+#ifdef CONFIG_MTD_XIP
++      if (!(nskb = alloc_skb(size, GFP_ATOMIC))) {
++              BT_ERR("Can't allocate memory for CAPI message");
++              return;
++      }
 +
-+/*
-+ * Function that are modifying the flash state away from array mode must
-+ * obviously not be running from flash.  The __xipram is therefore marking
-+ * those functions so they get relocated to ram.
-+ */
-+#define __xipram __attribute__ ((__section__ (".data")))
++      if (skb && (skb->len > 0))
++              memcpy(skb_put(nskb, skb->len), skb->data, skb->len);
 +
-+/*
-+ * We really don't want gcc to guess anything.
-+ * We absolutely _need_ proper inlining.
-+ */
-+#include <linux/compiler.h>
++      memcpy(skb_put(nskb, count), buf, count);
 +
-+/*
-+ * Each architecture has to provide the following macros.  They must access
-+ * the hardware directly and not rely on any other (XIP) functions since they
-+ * won't be available when used (flash not in array mode).
-+ *
-+ * xip_irqpending()
-+ *
-+ *    return non zero when any hardware interrupt is pending.
-+ *
-+ * xip_currtime()
-+ *
-+ *    return a platform specific time reference to be used with
-+ *    xip_elapsed_since().
-+ *
-+ * xip_elapsed_since(x)
-+ *
-+ *    return in usecs the elapsed timebetween now and the reference x as
-+ *    returned by xip_currtime().
-+ *
-+ *    note 1: convertion to usec can be approximated, as long as the
-+ *            returned value is <= the real elapsed time.
-+ *    note 2: this should be able to cope with a few seconds without
-+ *            overflowing.
-+ */
++      session->reassembly[id] = nskb;
 +
-+#if defined(CONFIG_ARCH_SA1100) || defined(CONFIG_ARCH_PXA)
++      if (skb)
++              kfree_skb(skb);
++}
 +
-+#include <asm/hardware.h>
-+#ifdef CONFIG_ARCH_PXA
-+#include <asm/arch/pxa-regs.h>
-+#endif
++static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb)
++{
++      __u8 hdr, hdrlen, id;
++      __u16 len;
 +
-+#define xip_irqpending()      (ICIP & ICMR)
++      BT_DBG("session %p skb %p len %d", session, skb, skb->len);
 +
-+/* we sample OSCR and convert desired delta to usec (1/4 ~= 1000000/3686400) */
-+#define xip_currtime()                (OSCR)
-+#define xip_elapsed_since(x)  (signed)((OSCR - (x)) / 4)
++      while (skb->len > 0) {
++              hdr = skb->data[0];
 +
-+#else
++              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;
++              }
 +
-+#warning "missing IRQ and timer primitives for XIP MTD support"
-+#warning "some of the XIP MTD support code will be disabled"
-+#warning "your system will therefore be unresponsive when writing or erasing flash"
++              id = (hdr & 0x3c) >> 2;
 +
-+#define xip_irqpending()      (0)
-+#define xip_currtime()                (0)
-+#define xip_elapsed_since(x)  (0)
++              BT_DBG("hdr 0x%02x hdrlen %d len %d id %d", hdr, hdrlen, len, id);
 +
-+#endif
++              if (hdrlen + len > skb->len) {
++                      BT_ERR("Wrong size or header information in CMTP frame");
++                      break;
++              }
 +
-+/*
-+ * xip_cpu_idle() is used when waiting for a delay equal or larger than
-+ * the system timer tick period.  This should put the CPU into idle mode
-+ * to save power and to be woken up only when some interrupts are pending.
-+ * As above, this should not rely upon standard kernel code.
-+ */
++              if (len == 0) {
++                      skb_pull(skb, hdrlen);
++                      continue;
++              }
 +
-+#if defined(CONFIG_CPU_XSCALE)
-+#define xip_cpu_idle()  asm volatile ("mcr p14, 0, %0, c7, c0, 0" :: "r" (1))
-+#else
-+#define xip_cpu_idle()  do { } while (0)
-+#endif
++              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;
++              }
 +
-+#else
++              skb_pull(skb, hdrlen + len);
++      }
 +
-+#define __xipram
++      kfree_skb(skb);
++      return 0;
++}
 +
-+#endif /* CONFIG_MTD_XIP */
++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;
 +
-+#endif /* __LINUX_MTD_XIP_H__ */
---- /dev/null
-+++ linux-2.4.21/include/linux/pm-devices.h
-@@ -0,0 +1,41 @@
-+#ifndef _LINUX_PM_DEV_H
-+#define _LINUX_PM_DEV_H
++      BT_DBG("session %p data %p len %d", session, data, len);
 +
-+/*
-+ * Copyright 2002 Montavista Software (mlocke@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, 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.
-+ */
++      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;
++}
 +
-+/*
-+ * Device types
-+ */
-+enum
++static int cmtp_process_transmit(struct cmtp_session *session)
 +{
-+        PM_UNKNOWN_DEV = 0, /* generic */
-+        PM_SYS_DEV,         /* system device (fan, KB controller, ...) */
-+        PM_PCI_DEV,         /* PCI device */
-+        PM_USB_DEV,         /* USB device */
-+        PM_SCSI_DEV,        /* SCSI device */
-+        PM_ISA_DEV,         /* ISA device */
-+        PM_MTD_DEV,         /* Memory Technology Device */
-+      PM_TPANEL_DEV,      /* Memory Technology Device */
-+        PM_STORAGE_DEV,      /* Memory Technology Device */
-+        PM_NETWORK_DEV,      /* Memory Technology Device */
-+        PM_PCMCIA_DEV,      /* Memory Technology Device */
-+        PM_DISPLAY_DEV,      /* Memory Technology Device */
-+        PM_SERIAL_DEV,      /* Memory Technology Device */
-+        PM_BATTERY_DEV,      /* Memory Technology Device */
-+};
++      struct sk_buff *skb, *nskb;
++      unsigned char *hdr;
++      unsigned int size, tail;
 +
-+#endif
---- linux-2.4.21/include/linux/pm.h~pm
-+++ linux-2.4.21/include/linux/pm.h
-@@ -24,6 +24,7 @@
- #ifdef __KERNEL__
- #include <linux/config.h>
-+#include <linux/pm-devices.h>
- #include <linux/list.h>
- /*
-@@ -50,20 +51,6 @@
- typedef int pm_request_t;
--/*
-- * Device types
-- */
--enum
--{
--      PM_UNKNOWN_DEV = 0, /* generic */
--      PM_SYS_DEV,         /* system device (fan, KB controller, ...) */
--      PM_PCI_DEV,         /* PCI device */
--      PM_USB_DEV,         /* USB device */
--      PM_SCSI_DEV,        /* SCSI device */
--      PM_ISA_DEV,         /* ISA device */
--      PM_MTD_DEV,         /* Memory Technology Device */
--};
--
- typedef int pm_dev_t;
- /*
---- /dev/null
-+++ linux-2.4.21/include/linux/rbtree-24.h
-@@ -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.
++      BT_DBG("session %p", session);
 +
-+  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.
++      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("");
 +
-+  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
++      down_read(&cmtp_session_sem);
 +
-+  linux/include/linux/rbtree.h
++      list_for_each(p, &cmtp_session_list) {
++              struct cmtp_session *session;
++              struct cmtp_conninfo ci;
 +
-+  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...
++              session = list_entry(p, struct cmtp_session, list);
 +
-+  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.
++              __cmtp_copy_session(session, &ci);
 +
-+-----------------------------------------------------------------------
-+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;
++              if (copy_to_user(req->ci, &ci, sizeof(ci))) {
++                      err = -EFAULT;
++                      break;
++              }
 +
-+      while (n)
-+      {
-+              page = rb_entry(n, struct page, rb_page_cache);
++              if (++n >= req->cnum)
++                      break;
 +
-+              if (offset < page->offset)
-+                      n = n->rb_left;
-+              else if (offset > page->offset)
-+                      n = n->rb_right;
-+              else
-+                      return page;
++              req->ci++;
 +      }
-+      return NULL;
++      req->cnum = n;
++
++      up_read(&cmtp_session_sem);
++      return err;
 +}
 +
-+static inline struct page * __rb_insert_page_cache(struct inode * inode,
-+                                                 unsigned long offset,
-+                                                 rb_node_t * node)
++int cmtp_get_conninfo(struct cmtp_conninfo *ci)
 +{
-+      rb_node_t ** p = &inode->i_rb_page_cache.rb_node;
-+      rb_node_t * parent = NULL;
-+      struct page * page;
++      struct cmtp_session *session;
++      int err = 0;
 +
-+      while (*p)
-+      {
-+              parent = *p;
-+              page = rb_entry(parent, struct page, rb_page_cache);
++      down_read(&cmtp_session_sem);
 +
-+              if (offset < page->offset)
-+                      p = &(*p)->rb_left;
-+              else if (offset > page->offset)
-+                      p = &(*p)->rb_right;
-+              else
-+                      return page;
-+      }
++      session = __cmtp_get_session(&ci->bdaddr);
++      if (session)
++              __cmtp_copy_session(session, ci);
++      else
++              err = -ENOENT;
 +
-+      rb_link_node(node, parent, p);
++      up_read(&cmtp_session_sem);
++      return err;
++}
 +
-+      return NULL;
++
++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;
 +}
 +
-+static inline struct page * rb_insert_page_cache(struct inode * inode,
-+                                               unsigned long offset,
-+                                               rb_node_t * node)
++void __exit exit_cmtp(void)
 +{
-+      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;
++      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");
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/cmtp/sock.c
+@@ -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.
 +*/
 +
-+#ifndef       _LINUX_RBTREE_H
-+#define       _LINUX_RBTREE_H
++#include <linux/config.h>
++#include <linux/module.h>
 +
++#include <linux/types.h>
++#include <linux/errno.h>
 +#include <linux/kernel.h>
-+#include <linux/stddef.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>
 +
-+typedef struct rb_node_s
++#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 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;
++      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;
 +}
-+rb_node_t;
 +
-+typedef struct rb_root_s
++static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 +{
-+      struct rb_node_s * rb_node;
-+}
-+rb_root_t;
++      struct cmtp_connadd_req ca;
++      struct cmtp_conndel_req cd;
++      struct cmtp_connlist_req cl;
++      struct cmtp_conninfo ci;
++      struct socket *nsock;
++      int err;
 +
-+#define RB_ROOT       (rb_root_t) { NULL, }
-+#define       rb_entry(ptr, type, member)                                     \
-+      ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
++      BT_DBG("cmd %x arg %lx", cmd, arg);
 +
-+extern void rb_insert_color(rb_node_t *, rb_root_t *);
-+extern void rb_erase(rb_node_t *, rb_root_t *);
++      switch (cmd) {
++      case CMTPCONNADD:
++              if (!capable(CAP_NET_ADMIN))
++                      return -EACCES;
 +
-+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;
++              if (copy_from_user(&ca, (void *) arg, sizeof(ca)))
++                      return -EFAULT;
 +
-+      *rb_link = node;
-+}
++              nsock = sockfd_lookup(ca.sock, &err);
++              if (!nsock)
++                      return err;
 +
-+#endif        /* _LINUX_RBTREE_H */
---- linux-2.4.21/include/linux/rbtree.h~mtd-cvs
-+++ linux-2.4.21/include/linux/rbtree.h
-@@ -1,133 +1,25 @@
- /*
--  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;
-+ * 2.5 compatibility
-+ * $Id$
-+ */
--typedef struct rb_root_s
--{
--      struct rb_node_s * rb_node;
--}
--rb_root_t;
-+#ifndef __MTD_COMPAT_RBTREE_H__
-+#define __MTD_COMPAT_RBTREE_H__
--#define RB_ROOT       (rb_root_t) { NULL, }
--#define       rb_entry(ptr, type, member)                                     \
--      ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
-+#include <linux/version.h>
--extern void rb_insert_color(rb_node_t *, rb_root_t *);
--extern void rb_erase(rb_node_t *, rb_root_t *);
-+#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
--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;
-+#include <linux/rbtree-24.h>
--      *rb_link = node;
--}
-+/* 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
--#endif        /* _LINUX_RBTREE_H */
-+#endif /*  __MTD_COMPAT_RBTREE_H__ */
---- /dev/null
-+++ linux-2.4.21/include/linux/rslib.h
-@@ -0,0 +1,105 @@
-+/* 
-+ * include/linux/rslib.h
-+ *
-+ * Overview:
-+ *   Generic Reed Solomon encoder / decoder library
-+ *   
-+ * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
-+ *
-+ * RS code lifted from reed solomon library written by Phil Karn
-+ * Copyright 2002 Phil Karn, KA9Q
-+ *
-+ * $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
-+ * published by the Free Software Foundation.
-+ */
++              if (nsock->sk->state != BT_CONNECTED) {
++                      fput(nsock->file);
++                      return -EBADFD;
++              }
 +
-+#ifndef _RSLIB_H_
-+#define _RSLIB_H_
++              err = cmtp_add_connection(&ca, nsock);
++              if (!err) {
++                      if (copy_to_user((void *) arg, &ca, sizeof(ca)))
++                              err = -EFAULT;
++              } else
++                      fput(nsock->file);
 +
-+#include <linux/list.h>
++              return err;
 +
-+/** 
-+ * struct rs_control - rs control structure
-+ * 
-+ * @mm:               Bits per symbol
-+ * @nn:               Symbols per block (= (1<<mm)-1)
-+ * @alpha_to: log lookup table
-+ * @index_of: Antilog lookup table
-+ * @genpoly:  Generator polynomial 
-+ * @nroots:   Number of generator roots = number of parity symbols
-+ * @fcr:      First consecutive root, index form
-+ * @prim:     Primitive element, index form 
-+ * @iprim:    prim-th root of 1, index form 
-+ * @gfpoly:   The primitive generator polynominal 
-+ * @users:    Users of this structure 
-+ * @list:     List entry for the rs control list
-+*/
-+struct rs_control {
-+      int             mm;
-+      int             nn;
-+      uint16_t        *alpha_to;
-+      uint16_t        *index_of;
-+      uint16_t        *genpoly;
-+      int             nroots;
-+      int             fcr;
-+      int             prim;
-+      int             iprim;
-+      int             gfpoly;
-+      int             users;
-+      struct list_head list;
-+};
++      case CMTPCONNDEL:
++              if (!capable(CAP_NET_ADMIN))
++                      return -EACCES;
 +
-+/* General purpose RS codec, 8-bit data width, symbol width 1-15 bit  */
-+#ifdef CONFIG_REED_SOLOMON_ENC8
-+int encode_rs8(struct rs_control *rs, uint8_t *data, int len, uint16_t *par,
-+             uint16_t invmsk);
-+#endif
-+#ifdef CONFIG_REED_SOLOMON_DEC8
-+int decode_rs8(struct rs_control *rs, uint8_t *data, uint16_t *par, int len, 
-+              uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk,
-+             uint16_t *corr);
-+#endif
++              if (copy_from_user(&cd, (void *) arg, sizeof(cd)))
++                      return -EFAULT;
 +
-+/* General purpose RS codec, 16-bit data width, symbol width 1-15 bit  */
-+#ifdef CONFIG_REED_SOLOMON_ENC16
-+int encode_rs16(struct rs_control *rs, uint16_t *data, int len, uint16_t *par,
-+              uint16_t invmsk);
-+#endif
-+#ifdef CONFIG_REED_SOLOMON_DEC16
-+int decode_rs16(struct rs_control *rs, uint16_t *data, uint16_t *par, int len,
-+              uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk,
-+              uint16_t *corr);
-+#endif
++              return cmtp_del_connection(&cd);
 +
-+/* Create or get a matching rs control structure */
-+struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim, 
-+                         int nroots);
++      case CMTPGETCONNLIST:
++              if (copy_from_user(&cl, (void *) arg, sizeof(cl)))
++                      return -EFAULT;
 +
-+/* Release a rs control structure */
-+void free_rs(struct rs_control *rs);
++              if (cl.cnum <= 0)
++                      return -EINVAL;
 +
-+/** modulo replacement for galois field arithmetics
-+ *
-+ *  @rs:      the rs control structure
-+ *  @x:               the value to reduce
-+ *
-+ *  where
-+ *  rs->mm = number of bits per symbol        
-+ *  rs->nn = (2^rs->mm) - 1
-+ *  
-+ *  Simple arithmetic modulo would return a wrong result for values
-+ *  >= 3 * rs->nn
-+*/
-+static inline int rs_modnn(struct rs_control *rs, int x)
-+{
-+      while (x >= rs->nn) {
-+              x -= rs->nn;
-+              x = (x >> rs->mm) + (x & rs->nn);
++              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 x;
++
++      return -EINVAL;
 +}
 +
-+#endif
---- linux-2.4.21/include/linux/soundcard.h~ucb1x00
-+++ linux-2.4.21/include/linux/soundcard.h
-@@ -811,6 +811,7 @@
- #define SOUND_MIXER_STEREODEVS        0xfb    /* Mixer channels supporting stereo */
- #define SOUND_MIXER_OUTSRC    0xfa    /* Arg contains a bit for each input source to output */
- #define SOUND_MIXER_OUTMASK   0xf9    /* Arg contains a bit for each supported input source to output */
-+#define SOUND_MIXER_AC97      0xf8    /* directly access ac97 registers */
- /*    Device mask bits        */
-@@ -874,6 +875,7 @@
- #define SOUND_MIXER_READ_RECMASK      MIXER_READ(SOUND_MIXER_RECMASK)
- #define SOUND_MIXER_READ_STEREODEVS   MIXER_READ(SOUND_MIXER_STEREODEVS)
- #define SOUND_MIXER_READ_CAPS         MIXER_READ(SOUND_MIXER_CAPS)
-+#define SOUND_MIXER_READ_AC97         MIXER_READ(SOUND_MIXER_AC97)
- #define MIXER_WRITE(dev)              _SIOWR('M', dev, int)
- #define SOUND_MIXER_WRITE_VOLUME      MIXER_WRITE(SOUND_MIXER_VOLUME)
-@@ -900,6 +902,7 @@
- #define SOUND_MIXER_WRITE_LOUD                MIXER_WRITE(SOUND_MIXER_LOUD)
- #define SOUND_MIXER_WRITE_RECSRC      MIXER_WRITE(SOUND_MIXER_RECSRC)
-+#define SOUND_MIXER_WRITE_AC97                MIXER_WRITE(SOUND_MIXER_AC97)
- typedef struct mixer_info
- {
---- /dev/null
-+++ linux-2.4.21/include/linux/suspend.h
-@@ -0,0 +1,10 @@
-+/* $Id$ */
++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
++};
 +
-+#ifndef __MTD_COMPAT_VERSION_H__
-+#include <linux/version.h>
++static int cmtp_sock_create(struct socket *sock, int protocol)
++{
++      struct sock *sk;
 +
-+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
-+#include_next <linux/suspend.h>
-+#endif
++      BT_DBG("sock %p", sock);
 +
-+#endif /* __MTD_COMPAT_VERSION_H__ */
---- linux-2.4.21/include/linux/tty.h~ramses-lcd
-+++ linux-2.4.21/include/linux/tty.h
-@@ -10,8 +10,8 @@
-  * resizing).
-  */
- #define MIN_NR_CONSOLES 1       /* must be at least 1 */
--#define MAX_NR_CONSOLES       63      /* serial lines start at 64 */
--#define MAX_NR_USER_CONSOLES 63       /* must be root to allocate above this */
-+#define MAX_NR_CONSOLES       3       /* serial lines start at 64 */
-+#define MAX_NR_USER_CONSOLES 3        /* must be root to allocate above this */
-               /* Note: the ioctl VT_GETSTATE does not work for
-                  consoles 16 and higher (since it returns a short) */
---- linux-2.4.21/include/linux/usb.h~ramses-usb
-+++ linux-2.4.21/include/linux/usb.h
-@@ -1079,7 +1079,7 @@
- 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)
-+#define dbg(format, arg...) printk(__FILE__ ": " format "\n" , ## arg)
- #else
- #define dbg(format, arg...) do {} while (0)
- #endif
---- linux-2.4.21/include/linux/wireless.h~linux-iw241_we16-6
-+++ linux-2.4.21/include/linux/wireless.h
-@@ -1,7 +1,7 @@
- /*
-  * This file define a set of standard wireless extensions
-  *
-- * Version :  15      12.7.02
-+ * Version :  16      2.4.03
-  *
-  * Authors :  Jean Tourrilhes - HPL - <jt@hpl.hp.com>
-  * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved.
-@@ -69,6 +69,8 @@
- /***************************** INCLUDES *****************************/
-+/* To minimise problems in user space, I might remove those headers
-+ * at some point. Jean II */
- #include <linux/types.h>              /* for "caddr_t" et al          */
- #include <linux/socket.h>             /* for "struct sockaddr" et al  */
- #include <linux/if.h>                 /* for IFNAMSIZ and co... */
-@@ -80,7 +82,7 @@
-  * (there is some stuff that will be added in the future...)
-  * I just plan to increment with each new version.
-  */
--#define WIRELESS_EXT  15
-+#define WIRELESS_EXT  16
- /*
-  * Changes :
-@@ -163,6 +165,16 @@
-  *    - Add IW_TXPOW_RANGE for range of Tx Powers
-  *    - Add IWEVREGISTERED & IWEVEXPIRED events for Access Points
-  *    - Add IW_MODE_MONITOR for passive monitor
-+ *
-+ * V15 to V16
-+ * ----------
-+ *    - Increase the number of bitrates in iw_range to 32 (for 802.11g)
-+ *    - Increase the number of frequencies in iw_range to 32 (for 802.11b+a)
-+ *    - Reshuffle struct iw_range for increases, add filler
-+ *    - Increase IW_MAX_AP to 64 for driver returning a lot of addresses
-+ *    - Remove IW_MAX_GET_SPY because conflict with enhanced spy support
-+ *    - Add SIOCSIWTHRSPY/SIOCGIWTHRSPY and "struct iw_thrspy"
-+ *    - Add IW_ENCODE_TEMP and iw_range->encoding_login_index
-  */
++      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;
++}
+--- linux-2.4.21/net/bluetooth/hci_core.c~bluetooth
++++ linux-2.4.21/net/bluetooth/hci_core.c
+@@ -218,6 +218,10 @@
  
- /**************************** CONSTANTS ****************************/
-@@ -196,9 +208,11 @@
- /* SIOCGIWSTATS is strictly used between user space and the kernel, and
-  * is never passed to the driver (i.e. the driver will never see it). */
+       /* Mandatory initialization */
  
--/* Mobile IP support (statistics per MAC address) */
-+/* Spy support (statistics per MAC address - used for Mobile IP support) */
- #define SIOCSIWSPY    0x8B10          /* set spy addresses */
- #define SIOCGIWSPY    0x8B11          /* get spy info (quality of link) */
-+#define SIOCSIWTHRSPY 0x8B12          /* set spy threshold (spy event) */
-+#define SIOCGIWTHRSPY 0x8B13          /* get spy threshold */
++      /* 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);
  
- /* Access Point manipulation */
- #define SIOCSIWAP     0x8B14          /* set access point MAC addresses */
-@@ -294,7 +308,7 @@
- #define IW_PRIV_TYPE_FLOAT    0x5000  /* struct iw_freq */
- #define IW_PRIV_TYPE_ADDR     0x6000  /* struct sockaddr */
+@@ -395,7 +399,7 @@
+ {
+       struct hci_inquiry_req ir;
+       struct hci_dev *hdev;
+-      int err = 0, do_inquiry = 0;
++      int err = 0, do_inquiry = 0, max_rsp;
+       long timeo;
+       __u8 *buf, *ptr;
+@@ -408,6 +412,7 @@
+       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;
+@@ -418,16 +423,19 @@
+       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) * ir.num_rsp, GFP_KERNEL))) {
++      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, ir.num_rsp, buf);
++      ir.num_rsp = inquiry_cache_dump(hdev, max_rsp, buf);
+       hci_dev_unlock_bh(hdev);
+       BT_DBG("num_rsp %d", ir.num_rsp);
+@@ -708,22 +716,20 @@
+       struct hci_dev_list_req *dl;
+       struct hci_dev_req *dr;
+       struct list_head *p;
+-      int n = 0, size;
++      int n = 0, size, err;
+       __u16 dev_num;
+       if (get_user(dev_num, (__u16 *) arg))
+               return -EFAULT;
  
--#define IW_PRIV_SIZE_FIXED    0x0800  /* Variable or fixed nuber of args */
-+#define IW_PRIV_SIZE_FIXED    0x0800  /* Variable or fixed number of args */
+-      if (!dev_num)
++      if (!dev_num || dev_num > (PAGE_SIZE * 2) / sizeof(*dr))
+               return -EINVAL;
+-      
+-      size = dev_num * sizeof(*dr) + sizeof(*dl);
  
- #define IW_PRIV_SIZE_MASK     0x07FF  /* Max number of those args */
+-      if (verify_area(VERIFY_WRITE, (void *) arg, size))
+-              return -EFAULT;
++      size = sizeof(*dl) + dev_num * sizeof(*dr);
  
-@@ -306,13 +320,13 @@
- /* ----------------------- OTHER CONSTANTS ----------------------- */
+       if (!(dl = kmalloc(size, GFP_KERNEL)))
+               return -ENOMEM;
++
+       dr = dl->dev_req;
  
- /* Maximum frequencies in the range struct */
--#define IW_MAX_FREQUENCIES    16
-+#define IW_MAX_FREQUENCIES    32
- /* Note : if you have something like 80 frequencies,
-  * don't increase this constant and don't fill the frequency list.
-  * The user will be able to set by channel anyway... */
+       read_lock_bh(&hdev_list_lock);
+@@ -738,12 +744,12 @@
+       read_unlock_bh(&hdev_list_lock);
  
- /* Maximum bit rates in the range struct */
--#define IW_MAX_BITRATES               8
-+#define IW_MAX_BITRATES               32
+       dl->dev_num = n;
+-      size = n * sizeof(*dr) + sizeof(*dl);
++      size = sizeof(*dl) + n * sizeof(*dr);
  
- /* Maximum tx powers in the range struct */
- #define IW_MAX_TXPOWER                8
-@@ -320,8 +334,7 @@
-  * a few of them in the struct iw_range. */
+-      copy_to_user((void *) arg, dl, size);
++      err = copy_to_user((void *) arg, dl, size);
+       kfree(dl);
  
- /* Maximum of address that you may set with SPY */
--#define IW_MAX_SPY            8       /* set */
--#define IW_MAX_GET_SPY                64      /* get */
-+#define IW_MAX_SPY            8
+-      return 0;
++      return err ? -EFAULT : 0;
+ }
  
- /* Maximum of address that you may get in the
-    list of access points in range */
-@@ -354,7 +367,8 @@
- #define IW_ENCODE_ENABLED     0x0000  /* Encoding enabled */
- #define IW_ENCODE_RESTRICTED  0x4000  /* Refuse non-encoded packets */
- #define IW_ENCODE_OPEN                0x2000  /* Accept non-encoded packets */
--#define IW_ENCODE_NOKEY         0x0800  /* Key is write only, so not present */
-+#define IW_ENCODE_NOKEY               0x0800  /* Key is write only, so not present */
-+#define IW_ENCODE_TEMP                0x0400  /* Temporary key */
+ int hci_get_dev_info(unsigned long arg)
+--- linux-2.4.21/net/bluetooth/hci_event.c~bluetooth
++++ linux-2.4.21/net/bluetooth/hci_event.c
+@@ -62,9 +62,22 @@
+ /* 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);
  
- /* Power management flags available (along with the value, if any) */
- #define IW_POWER_ON           0x0000  /* No details... */
-@@ -482,6 +496,17 @@
-       __u32           beacon;         /* Missed beacons/superframe */
- };
+       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;
+@@ -307,7 +320,7 @@
+                       status, batostr(&cc->bdaddr), conn);
+       if (status) {
+-              if (conn) {
++              if (conn && conn->state == BT_CONNECT) {
+                       conn->state = BT_CLOSED;
+                       hci_proto_connect_cfm(conn, status);
+                       hci_conn_del(conn);
+@@ -439,6 +452,29 @@
+       hci_dev_unlock(hdev);
+ }
  
-+/*
-+ *    Quality range (for spy threshold)
-+ */
-+struct        iw_thrspy
++/* Inquiry Result With RSSI */
++static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct sk_buff *skb)
 +{
-+      struct sockaddr         addr;           /* Source address (hw/mac) */
-+      struct iw_quality       qual;           /* Quality of the link */
-+      struct iw_quality       low;            /* Low threshold */
-+      struct iw_quality       high;           /* High threshold */
-+};
++      inquiry_info_with_rssi *info = (inquiry_info_with_rssi *) (skb->data + 1);
++      int num_rsp = *((__u8 *) skb->data);
 +
- /* ------------------------ WIRELESS STATS ------------------------ */
- /*
-  * Wireless statistics (used for /proc/net/wireless)
-@@ -534,7 +559,7 @@
-       struct iw_quality qual;         /* Quality part of statistics */
-       struct sockaddr ap_addr;        /* Access point address */
--      struct sockaddr addr;           /* Destination address (hw) */
-+      struct sockaddr addr;           /* Destination address (hw/mac) */
-       struct iw_param param;          /* Other small parameters */
-       struct iw_point data;           /* Other large parameters */
-@@ -582,17 +607,31 @@
-       __u32           min_nwid;       /* Minimal NWID we are able to set */
-       __u32           max_nwid;       /* Maximal NWID we are able to set */
--      /* Frequency */
--      __u16           num_channels;   /* Number of channels [0; num - 1] */
--      __u8            num_frequency;  /* Number of entry in the list */
--      struct iw_freq  freq[IW_MAX_FREQUENCIES];       /* list */
--      /* Note : this frequency list doesn't need to fit channel numbers */
-+      /* Old Frequency (backward compat - moved lower ) */
-+      __u16           old_num_channels;
-+      __u8            old_num_frequency;
-+      /* Filler to keep "version" at the same offset */
-+      __s32           old_freq[6];
-       /* signal level threshold range */
-       __s32   sensitivity;
-       /* Quality of link & SNR stuff */
-+      /* Quality range (link, level, noise)
-+       * If the quality is absolute, it will be in the range [0 ; max_qual],
-+       * if the quality is dBm, it will be in the range [max_qual ; 0].
-+       * Don't forget that we use 8 bit arithmetics... */
-       struct iw_quality       max_qual;       /* Quality of the link */
-+      /* This should contain the average/typical values of the quality
-+       * indicator. This should be the threshold between a "good" and
-+       * a "bad" link (example : monitor going from green to orange).
-+       * Currently, user space apps like quality monitors don't have any
-+       * way to calibrate the measurement. With this, they can split
-+       * the range between 0 and max_qual in different quality level
-+       * (using a geometric subdivision centered on the average).
-+       * I expect that people doing the user space apps will feedback
-+       * us on which value we need to put in each driver... */
-+      struct iw_quality       avg_qual;       /* Quality of the link */
-       /* Rates */
-       __u8            num_bitrates;   /* Number of entries in the list */
-@@ -619,6 +658,8 @@
-       __u16   encoding_size[IW_MAX_ENCODING_SIZES];   /* Different token sizes */
-       __u8    num_encoding_sizes;     /* Number of entry in the list */
-       __u8    max_encoding_tokens;    /* Max number of tokens */
-+      /* For drivers that need a "login/passwd" form */
-+      __u8    encoding_login_index;   /* token index for login token */
-       /* Transmit power */
-       __u16           txpower_capa;   /* What options are supported */
-@@ -638,18 +679,12 @@
-       __s32           min_r_time;     /* Minimal retry lifetime */
-       __s32           max_r_time;     /* Maximal retry lifetime */
++      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)
+ {
+@@ -735,6 +771,10 @@
+               hci_inquiry_result_evt(hdev, skb);
+               break;
  
--      /* Average quality of link & SNR */
--      struct iw_quality       avg_qual;       /* Quality of the link */
--      /* This should contain the average/typical values of the quality
--       * indicator. This should be the threshold between a "good" and
--       * a "bad" link (example : monitor going from green to orange).
--       * Currently, user space apps like quality monitors don't have any
--       * way to calibrate the measurement. With this, they can split
--       * the range between 0 and max_qual in different quality level
--       * (using a geometric subdivision centered on the average).
--       * I expect that people doing the user space apps will feedback
--       * us on which value we need to put in each driver...
--       */
-+      /* Frequency */
-+      __u16           num_channels;   /* Number of channels [0; num - 1] */
-+      __u8            num_frequency;  /* Number of entry in the list */
-+      struct iw_freq  freq[IW_MAX_FREQUENCIES];       /* list */
-+      /* Note : this frequency list doesn't need to fit channel numbers,
-+       * because each entry contain its channel index */
++      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;
+--- linux-2.4.21/net/bluetooth/hci_sock.c~bluetooth
++++ linux-2.4.21/net/bluetooth/hci_sock.c
+@@ -66,20 +66,20 @@
+       /* Packet types */
+       0x10,
+       /* Events */
+-      { 0xd9fe, 0x0 },
++      { 0x1000d9fe, 0x0000300c },
+       /* Commands */
+       {
+               { 0x0 },
+               /* OGF_LINK_CTL */
+-              { 0x2a000002, 0x0, 0x0, 0x0  },
++              { 0xbe000006, 0x00000001, 0x0000, 0x00 },
+               /* OGF_LINK_POLICY */
+-              { 0x1200, 0x0, 0x0, 0x0      },
++              { 0x00005200, 0x00000000, 0x0000, 0x00 },
+               /* OGF_HOST_CTL */
+-              { 0x80100000, 0x2a, 0x0, 0x0 },
++              { 0xaab00200, 0x2b402aaa, 0x0154, 0x00 },
+               /* OGF_INFO_PARAM */
+-              { 0x22a, 0x0, 0x0, 0x0       },
++              { 0x000002be, 0x00000000, 0x0000, 0x00 },
+               /* OGF_STATUS_PARAM */
+-              { 0x2e, 0x0, 0x0, 0x0        }
++              { 0x000000ea, 0x00000000, 0x0000, 0x00 }
+       }
  };
  
- /*
 --- /dev/null
-+++ linux-2.4.21/include/linux/workqueue.h
-@@ -0,0 +1,21 @@
-+/*
-+ * 2.5 compatibility 
-+ * $Id$
-+ */
++++ linux-2.4.21/net/bluetooth/hidp/Config.in
+@@ -0,0 +1,5 @@
++#
++# Bluetooth HIDP layer configuration
++#
 +
-+#ifndef __MTD_COMPAT_WORKQUEUE_H__
-+#define __MTD_COMPAT_WORKQUEUE_H__
++dep_tristate 'HIDP protocol support' CONFIG_BLUEZ_HIDP $CONFIG_INPUT $CONFIG_BLUEZ_L2CAP
+--- /dev/null
++++ linux-2.4.21/net/bluetooth/hidp/Makefile
+@@ -0,0 +1,10 @@
++#
++# Makefile for the Linux Bluetooth HIDP layer
++#
 +
-+#include <linux/version.h>
++O_TARGET := hidp.o
 +
-+#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
++obj-y := core.o sock.o
++obj-m += $(O_TARGET)
 +
-+#endif /* __MTD_COMPAT_WORKQUEUE_H__ */
++include $(TOPDIR)/Rules.make
 --- /dev/null
-+++ linux-2.4.21/include/mtd/inftl-user.h
-@@ -0,0 +1,91 @@
-+/*
-+ * $Id$
-+ *
-+ * Parts of INFTL headers shared with userspace 
-+ *
-+ */
++++ linux-2.4.21/net/bluetooth/hidp/core.c
+@@ -0,0 +1,655 @@
++/* 
++   HIDP implementation for Linux Bluetooth stack (BlueZ).
++   Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
 +
-+#ifndef __MTD_INFTL_USER_H__
-+#define __MTD_INFTL_USER_H__
++   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.
++*/
 +
-+#define       OSAK_VERSION    0x5120
-+#define       PERCENTUSED     98
++#include <linux/config.h>
++#include <linux/module.h>
 +
-+#define       SECTORSIZE      512
++#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>
 +
-+/* Block Control Information */
++#include <linux/input.h>
 +
-+struct inftl_bci {
-+      uint8_t ECCsig[6];
-+      uint8_t Status;
-+      uint8_t Status1;
-+} __attribute__((packed));
++#include <net/bluetooth/bluetooth.h>
++#include <net/bluetooth/l2cap.h>
 +
-+struct inftl_unithead1 {
-+      uint16_t virtualUnitNo;
-+      uint16_t prevUnitNo;
-+      uint8_t ANAC;
-+      uint8_t NACs;
-+      uint8_t parityPerField;
-+      uint8_t discarded;
-+} __attribute__((packed));
++#include "hidp.h"
 +
-+struct inftl_unithead2 {
-+      uint8_t parityPerField;
-+      uint8_t ANAC;
-+      uint16_t prevUnitNo;
-+      uint16_t virtualUnitNo;
-+      uint8_t NACs;
-+      uint8_t discarded;
-+} __attribute__((packed));
++#ifndef CONFIG_BT_HIDP_DEBUG
++#undef  BT_DBG
++#define BT_DBG(D...)
++#endif
 +
-+struct inftl_unittail {
-+      uint8_t Reserved[4];
-+      uint16_t EraseMark;
-+      uint16_t EraseMark1;
-+} __attribute__((packed));
++#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("");
 +
-+union inftl_uci {
-+      struct inftl_unithead1 a;
-+      struct inftl_unithead2 b;
-+      struct inftl_unittail c;
-+};
++      down_read(&hidp_session_sem);
 +
-+struct inftl_oob {
-+      struct inftl_bci b;
-+      union inftl_uci u;
-+};
++      list_for_each(p, &hidp_session_list) {
++              struct hidp_session *session;
++              struct hidp_conninfo ci;
 +
++              session = list_entry(p, struct hidp_session, list);
 +
-+/* INFTL Media Header */
++              __hidp_copy_session(session, &ci);
 +
-+struct INFTLPartition {
-+      __u32 virtualUnits;
-+      __u32 firstUnit;
-+      __u32 lastUnit;
-+      __u32 flags;
-+      __u32 spareUnits;
-+      __u32 Reserved0;
-+      __u32 Reserved1;
-+} __attribute__((packed));
++              if (copy_to_user(req->ci, &ci, sizeof(ci))) {
++                      err = -EFAULT;
++                      break;
++              }
 +
-+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));
++              if (++n >= req->cnum)
++                      break;
 +
-+/* Partition flag types */
-+#define       INFTL_BINARY    0x20000000
-+#define       INFTL_BDTL      0x40000000
-+#define       INFTL_LAST      0x80000000
++              req->ci++;
++      }
++      req->cnum = n;
 +
-+#endif /* __MTD_INFTL_USER_H__ */
++      up_read(&hidp_session_sem);
++      return err;
++}
 +
++int hidp_get_conninfo(struct hidp_conninfo *ci)
++{
++      struct hidp_session *session;
++      int err = 0;
 +
---- /dev/null
-+++ linux-2.4.21/include/mtd/jffs2-user.h
-@@ -0,0 +1,35 @@
-+/*
-+ * $Id$
-+ *
-+ * JFFS2 definitions for use in user space only
-+ */
++      down_read(&hidp_session_sem);
 +
-+#ifndef __JFFS2_USER_H__
-+#define __JFFS2_USER_H__
++      session = __hidp_get_session(&ci->bdaddr);
++      if (session)
++              __hidp_copy_session(session, ci);
++      else
++              err = -ENOENT;
 +
-+/* This file is blessed for inclusion by userspace */
-+#include <linux/jffs2.h>
-+#include <endian.h>
-+#include <byteswap.h>
++      up_read(&hidp_session_sem);
++      return err;
++}
 +
-+#undef cpu_to_je16
-+#undef cpu_to_je32
-+#undef cpu_to_jemode
-+#undef je16_to_cpu
-+#undef je32_to_cpu
-+#undef jemode_to_cpu
++static int __init hidp_init(void)
++{
++      l2cap_load();
 +
-+extern int target_endian;
++      hidp_init_sockets();
 +
-+#define t16(x) ({ uint16_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_16(__b); })
-+#define t32(x) ({ uint32_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_32(__b); })
++      BT_INFO("BlueZ HIDP ver %s", VERSION);
++      BT_INFO("Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>");
 +
-+#define cpu_to_je16(x) ((jint16_t){t16(x)})
-+#define cpu_to_je32(x) ((jint32_t){t32(x)})
-+#define cpu_to_jemode(x) ((jmode_t){t32(x)})
++      return 0;
++}
 +
-+#define je16_to_cpu(x) (t16((x).v16))
-+#define je32_to_cpu(x) (t32((x).v32))
-+#define jemode_to_cpu(x) (t32((x).m))
++static void __exit hidp_exit(void)
++{
++      hidp_cleanup_sockets();
++}
 +
-+#endif /* __JFFS2_USER_H__ */
++module_init(hidp_init);
++module_exit(hidp_exit);
++
++MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
++MODULE_DESCRIPTION("Bluetooth HIDP ver " VERSION);
++MODULE_LICENSE("GPL");
 --- /dev/null
-+++ linux-2.4.21/include/mtd/mtd-abi.h
-@@ -0,0 +1,119 @@
-+/*
-+ * $Id$
-+ *
-+ * Portions of MTD ABI definition which are shared by kernel and user space 
-+ */
++++ linux-2.4.21/net/bluetooth/hidp/hidp.h
+@@ -0,0 +1,122 @@
++/* 
++   HIDP implementation for Linux Bluetooth stack (BlueZ).
++   Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
 +
-+#ifndef __MTD_ABI_H__
-+#define __MTD_ABI_H__
++   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 __KERNEL__ /* Urgh. The whole point of splitting this out into
-+                  separate files was to avoid #ifdef __KERNEL__ */
-+#define __user
-+#endif
++#ifndef __HIDP_H
++#define __HIDP_H
 +
-+struct erase_info_user {
-+      uint32_t start;
-+      uint32_t length;
++#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 mtd_oob_buf {
-+      uint32_t start;
-+      uint32_t length;
-+      unsigned char __user *ptr;
++struct hidp_conndel_req {
++      bdaddr_t bdaddr;
++      __u32    flags;
 +};
 +
-+#define MTD_ABSENT            0
-+#define MTD_RAM                       1
-+#define MTD_ROM                       2
-+#define MTD_NORFLASH          3
-+#define MTD_NANDFLASH         4
-+#define MTD_PEROM             5
-+#define MTD_DATAFLASH         6
-+#define MTD_OTHER             14
-+#define MTD_UNKNOWN           15
++struct hidp_conninfo {
++      bdaddr_t bdaddr;
++      __u32    flags;
++      __u16    state;
++      __u16    vendor;
++      __u16    product;
++      __u16    version;
++      char     name[128];
++};
 +
-+#define MTD_CLEAR_BITS                1       // Bits can be cleared (flash)
-+#define MTD_SET_BITS          2       // Bits can be set
-+#define MTD_ERASEABLE         4       // Has an erase function
-+#define MTD_WRITEB_WRITEABLE  8       // Direct IO is possible
-+#define MTD_VOLATILE          16      // Set for RAMs
-+#define MTD_XIP                       32      // eXecute-In-Place possible
-+#define MTD_OOB                       64      // Out-of-band data (NAND flash)
-+#define MTD_ECC                       128     // Device capable of automatic ECC
-+#define MTD_NO_VIRTBLOCKS     256     // Virtual blocks not allowed
++struct hidp_connlist_req {
++      __u32  cnum;
++      struct hidp_conninfo *ci;
++};
 +
-+// Some common devices / combinations of capabilities
-+#define MTD_CAP_ROM           0
-+#define MTD_CAP_RAM           (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE)
-+#define MTD_CAP_NORFLASH        (MTD_CLEAR_BITS|MTD_ERASEABLE)
-+#define MTD_CAP_NANDFLASH       (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB)
-+#define MTD_WRITEABLE         (MTD_CLEAR_BITS|MTD_SET_BITS)
++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;
 +
-+// Types of automatic ECC/Checksum available
-+#define MTD_ECC_NONE          0       // No automatic ECC available
-+#define MTD_ECC_RS_DiskOnChip 1       // Automatic ECC on DiskOnChip
-+#define MTD_ECC_SW            2       // SW ECC for Toshiba & Samsung devices
++      struct socket *ctrl_sock;
++      struct socket *intr_sock;
 +
-+/* ECC byte placement */
-+#define MTD_NANDECC_OFF               0       // Switch off ECC (Not recommended)
-+#define MTD_NANDECC_PLACE     1       // Use the given placement in the structure (YAFFS1 legacy mode)
-+#define MTD_NANDECC_AUTOPLACE 2       // Use the default placement scheme
-+#define MTD_NANDECC_PLACEONLY 3       // Use the given placement in the structure (Do not store ecc result on read)
++      bdaddr_t bdaddr;
 +
-+/* OTP mode selection */
-+#define MTD_OTP_OFF           0
-+#define MTD_OTP_FACTORY               1
-+#define MTD_OTP_USER          2
++      unsigned long state;
++      unsigned long flags;
++      unsigned long idle_to;
 +
-+struct mtd_info_user {
-+      uint8_t type;
-+      uint32_t flags;
-+      uint32_t size;   // Total size of the MTD
-+      uint32_t erasesize;
-+      uint32_t oobblock;  // Size of OOB blocks (e.g. 512)
-+      uint32_t oobsize;   // Amount of OOB data per block (e.g. 16)
-+      uint32_t ecctype;
-+      uint32_t eccsize;
-+};
++      uint ctrl_mtu;
++      uint intr_mtu;
 +
-+struct region_info_user {
-+      uint32_t offset;                /* At which this region starts, 
-+                                       * from the beginning of the MTD */
-+      uint32_t erasesize;             /* For this region */
-+      uint32_t numblocks;             /* Number of blocks in this region */
-+      uint32_t regionindex;
-+};
++      atomic_t terminate;
 +
-+struct otp_info {
-+      uint32_t start;
-+      uint32_t length;
-+      uint32_t locked;
-+};
++      unsigned char keys[8];
++      unsigned char leds;
 +
-+#define MEMGETINFO              _IOR('M', 1, struct mtd_info_user)
-+#define MEMERASE                _IOW('M', 2, struct erase_info_user)
-+#define MEMWRITEOOB             _IOWR('M', 3, struct mtd_oob_buf)
-+#define MEMREADOOB              _IOWR('M', 4, struct mtd_oob_buf)
-+#define MEMLOCK                 _IOW('M', 5, struct erase_info_user)
-+#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 MEMSETOOBSEL          _IOW('M', 9, struct nand_oobinfo)
-+#define MEMGETOOBSEL          _IOR('M', 10, struct nand_oobinfo)
-+#define MEMGETBADBLOCK                _IOW('M', 11, loff_t)
-+#define MEMSETBADBLOCK                _IOW('M', 12, loff_t)
-+#define OTPSELECT             _IOR('M', 13, int)
-+#define OTPGETREGIONCOUNT     _IOW('M', 14, int)
-+#define OTPGETREGIONINFO      _IOW('M', 15, struct otp_info)
-+#define OTPLOCK               _IOR('M', 16, struct otp_info)
++      struct input_dev *input;
 +
-+struct nand_oobinfo {
-+      uint32_t useecc;
-+      uint32_t eccbytes;
-+      uint32_t oobfree[8][2];
-+      uint32_t eccpos[32];
++      struct timer_list timer;
++
++      struct sk_buff_head ctrl_transmit;
++      struct sk_buff_head intr_transmit;
 +};
 +
-+#endif /* __MTD_ABI_H__ */
++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 */
 --- /dev/null
-+++ linux-2.4.21/include/mtd/mtd-user.h
-@@ -0,0 +1,20 @@
-+/*
-+ * $Id$
-+ *
-+ * MTD ABI header for use by user space only.
-+ */
++++ linux-2.4.21/net/bluetooth/hidp/sock.c
+@@ -0,0 +1,212 @@
++/* 
++   HIDP implementation for Linux Bluetooth stack (BlueZ).
++   Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org>
 +
-+#ifndef __MTD_USER_H__
-+#define __MTD_USER_H__
++   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 <stdint.h>
++#include <linux/config.h>
++#include <linux/module.h>
 +
-+/* This file is blessed for inclusion by userspace */
-+#include <mtd/mtd-abi.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>
 +
-+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;
++#include "hidp.h"
 +
-+#endif /* __MTD_USER_H__ */
---- /dev/null
-+++ linux-2.4.21/include/mtd/nftl-user.h
-@@ -0,0 +1,76 @@
-+/*
-+ * $Id$
-+ *
-+ * Parts of NFTL headers shared with userspace 
-+ *
-+ */
++#ifndef CONFIG_BT_HIDP_DEBUG
++#undef  BT_DBG
++#define BT_DBG(D...)
++#endif
 +
-+#ifndef __MTD_NFTL_USER_H__
-+#define __MTD_NFTL_USER_H__
++static int hidp_sock_release(struct socket *sock)
++{
++      struct sock *sk = sock->sk;
 +
-+/* Block Control Information */
++      BT_DBG("sock %p sk %p", sock, sk);
 +
-+struct nftl_bci {
-+      unsigned char ECCSig[6];
-+      uint8_t Status;
-+      uint8_t Status1;
-+}__attribute__((packed));
++      if (!sk)
++              return 0;
 +
-+/* Unit Control Information */
++      sock_orphan(sk);
++      sock_put(sk);
 +
-+struct nftl_uci0 {
-+      uint16_t VirtUnitNum;
-+      uint16_t ReplUnitNum;
-+      uint16_t SpareVirtUnitNum;
-+      uint16_t SpareReplUnitNum;
-+} __attribute__((packed));
++      MOD_DEC_USE_COUNT;
++      return 0;
++}
 +
-+struct nftl_uci1 {
-+      uint32_t WearInfo;
-+      uint16_t EraseMark;
-+      uint16_t EraseMark1;
-+} __attribute__((packed));
++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;
 +
-+struct nftl_uci2 {
-+        uint16_t FoldMark;
-+        uint16_t FoldMark1;
-+      uint32_t unused;
-+} __attribute__((packed));
++      BT_DBG("cmd %x arg %lx", cmd, arg);
 +
-+union nftl_uci {
-+      struct nftl_uci0 a;
-+      struct nftl_uci1 b;
-+      struct nftl_uci2 c;
-+};
++      switch (cmd) {
++      case HIDPCONNADD:
++              if (!capable(CAP_NET_ADMIN))
++                      return -EACCES;
 +
-+struct nftl_oob {
-+      struct nftl_bci b;
-+      union nftl_uci u;
-+};
++              if (copy_from_user(&ca, (void *) arg, sizeof(ca)))
++                      return -EFAULT;
 +
-+/* NFTL Media Header */
++              csock = sockfd_lookup(ca.ctrl_sock, &err);
++              if (!csock)
++                      return err;
 +
-+struct NFTLMediaHeader {
-+      char DataOrgID[6];
-+      uint16_t NumEraseUnits;
-+      uint16_t FirstPhysicalEUN;
-+      uint32_t FormattedSize;
-+      unsigned char UnitSizeFactor;
-+} __attribute__((packed));
++              isock = sockfd_lookup(ca.intr_sock, &err);
++              if (!isock) {
++                      fput(csock->file);
++                      return err;
++              }
 +
-+#define MAX_ERASE_ZONES (8192 - 512)
++              if (csock->sk->state != BT_CONNECTED || isock->sk->state != BT_CONNECTED) {
++                      fput(csock->file);
++                      fput(isock->file);
++                      return -EBADFD;
++              }
 +
-+#define ERASE_MARK 0x3c69
-+#define SECTOR_FREE 0xff
-+#define SECTOR_USED 0x55
-+#define SECTOR_IGNORE 0x11
-+#define SECTOR_DELETED 0x00
++              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);
++              }
 +
-+#define FOLD_MARK_IN_PROGRESS 0x5555
++              return err;
 +
-+#define ZONE_GOOD 0xff
-+#define ZONE_BAD_ORIGINAL 0
-+#define ZONE_BAD_MARKED 7
++      case HIDPCONNDEL:
++              if (!capable(CAP_NET_ADMIN))
++                      return -EACCES;
 +
++              if (copy_from_user(&cd, (void *) arg, sizeof(cd)))
++                      return -EFAULT;
 +
-+#endif /* __MTD_NFTL_USER_H__ */
---- linux-2.4.21/include/net/iw_handler.h~linux-iw241_we16-6
-+++ linux-2.4.21/include/net/iw_handler.h
-@@ -1,7 +1,7 @@
- /*
-  * This file define the new driver API for Wireless Extensions
-  *
-- * Version :  4       21.6.02
-+ * Version :  5       4.12.02
-  *
-  * Authors :  Jean Tourrilhes - HPL - <jt@hpl.hp.com>
-  * Copyright (c) 2001-2002 Jean Tourrilhes, All Rights Reserved.
-@@ -206,7 +206,7 @@
-  * will be needed...
-  * I just plan to increment with each new version.
-  */
--#define IW_HANDLER_VERSION    4
-+#define IW_HANDLER_VERSION    5
- /*
-  * Changes :
-@@ -220,10 +220,18 @@
-  * V3 to V4
-  * --------
-  *    - Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes
-+ *
-+ * V4 to V5
-+ * --------
-+ *    - Add new spy support : struct iw_spy_data & prototypes
-  */
- /**************************** CONSTANTS ****************************/
-+/* Enable enhanced spy support. Disable to reduce footprint */
-+#define IW_WIRELESS_SPY
-+#define IW_WIRELESS_THRSPY
++              return hidp_del_connection(&cd);
 +
- /* Special error message for the driver to indicate that we
-  * should do a commit after return from the iw_handler */
- #define EIWCOMMIT     EINPROGRESS
-@@ -315,6 +323,9 @@
-        * We will automatically export that to user space... */
-       struct iw_priv_args *   private_args;
-+      /* Driver enhanced spy support */
-+      long                    spy_offset;     /* Spy data offset */
++      case HIDPGETCONNLIST:
++              if (copy_from_user(&cl, (void *) arg, sizeof(cl)))
++                      return -EFAULT;
 +
-       /* In the long term, get_wireless_stats will move from
-        * 'struct net_device' to here, to minimise bloat. */
- };
-@@ -350,6 +361,33 @@
- /* Need to think of short header translation table. Later. */
-+/* --------------------- ENHANCED SPY SUPPORT --------------------- */
-+/*
-+ * In the old days, the driver was handling spy support all by itself.
-+ * Now, the driver can delegate this task to Wireless Extensions.
-+ * It needs to include this struct in its private part and use the
-+ * standard spy iw_handler.
-+ */
++              if (cl.cnum <= 0)
++                      return -EINVAL;
 +
-+/*
-+ * Instance specific spy data, i.e. addresses spied and quality for them.
-+ */
-+struct iw_spy_data
-+{
-+#ifdef IW_WIRELESS_SPY
-+      /* --- Standard spy support --- */
-+      int                     spy_number;
-+      u_char                  spy_address[IW_MAX_SPY][ETH_ALEN];
-+      struct iw_quality       spy_stat[IW_MAX_SPY];
-+#ifdef IW_WIRELESS_THRSPY
-+      /* --- Enhanced spy support (event) */
-+      struct iw_quality       spy_thr_low;    /* Low threshold */
-+      struct iw_quality       spy_thr_high;   /* High threshold */
-+      u_char                  spy_thr_under[IW_MAX_SPY];
-+#endif /* IW_WIRELESS_THRSPY */
-+#endif /* IW_WIRELESS_SPY */
-+};
++              err = hidp_get_connlist(&cl);
++              if (!err && copy_to_user((void *) arg, &cl, sizeof(cl)))
++                      return -EFAULT;
 +
- /**************************** PROTOTYPES ****************************/
- /*
-  * Functions part of the Wireless Extensions (defined in net/core/wireless.c).
-@@ -376,6 +414,31 @@
- /* We may need a function to send a stream of events to user space.
-  * More on that later... */
-+/* Standard handler for SIOCSIWSPY */
-+extern int iw_handler_set_spy(struct net_device *     dev,
-+                            struct iw_request_info *  info,
-+                            union iwreq_data *        wrqu,
-+                            char *                    extra);
-+/* Standard handler for SIOCGIWSPY */
-+extern int iw_handler_get_spy(struct net_device *     dev,
-+                            struct iw_request_info *  info,
-+                            union iwreq_data *        wrqu,
-+                            char *                    extra);
-+/* Standard handler for SIOCSIWTHRSPY */
-+extern int iw_handler_set_thrspy(struct net_device *  dev,
-+                               struct iw_request_info *info,
-+                               union iwreq_data *     wrqu,
-+                               char *                 extra);
-+/* Standard handler for SIOCGIWTHRSPY */
-+extern int iw_handler_get_thrspy(struct net_device *  dev,
-+                               struct iw_request_info *info,
-+                               union iwreq_data *     wrqu,
-+                               char *                 extra);
-+/* Driver call to update spy records */
-+extern void wireless_spy_update(struct net_device *   dev,
-+                              unsigned char *         address,
-+                              struct iw_quality *     wstats);
++              return err;
 +
- /************************* INLINE FUNTIONS *************************/
- /*
-  * Function that are so simple that it's more efficient inlining them
---- linux-2.4.21/init/do_mounts.c~small-nocramdisk
-+++ linux-2.4.21/init/do_mounts.c
-@@ -16,8 +16,6 @@
- #include <linux/ext2_fs.h>
- #include <linux/romfs_fs.h>
--#define BUILD_CRAMDISK
--
- extern int get_filesystem_list(char * buf);
- extern asmlinkage long sys_mount(char *dev_name, char *dir_name, char *type,
---- linux-2.4.21/kernel/ksyms.c~ramses
-+++ linux-2.4.21/kernel/ksyms.c
-@@ -585,6 +585,11 @@
- EXPORT_SYMBOL(tasklist_lock);
- EXPORT_SYMBOL(pidhash);
-+#ifdef CONFIG_ARCH_RAMSES
-+#include <asm/arch/ramses.h>
-+EXPORT_SYMBOL(ramses_control_shadow);
-+EXPORT_SYMBOL(ramses_flags);
-+#endif
- /* debug */
- EXPORT_SYMBOL(dump_stack);
---- linux-2.4.21/kernel/pm.c~pm
-+++ linux-2.4.21/kernel/pm.c
-@@ -234,7 +234,7 @@
-       struct list_head *entry;
-       
-       down(&pm_devs_lock);
--      entry = pm_devs.next;
-+      entry = (rqst==PM_RESUME) ? pm_devs.prev : pm_devs.next;
-       while (entry != &pm_devs) {
-               struct pm_dev *dev = list_entry(entry, struct pm_dev, entry);
-               if (dev->callback) {
-@@ -249,7 +249,7 @@
-                               return status;
-                       }
-               }
--              entry = entry->next;
-+              entry = (rqst==PM_RESUME) ? entry->prev : entry->next;
-       }
-       up(&pm_devs_lock);
-       return 0;
---- linux-2.4.21/lib/Config.in~mtd-cvs
-+++ linux-2.4.21/lib/Config.in
-@@ -35,4 +35,20 @@
-   fi
- fi
-+if [ "$CONFIG_MTD_DOCPROBE" = "y" -o \
-+     "$CONFIG_MTD_NAND_RTC_FROM4" = "y" -o \
-+     "$CONFIG_MTD_NAND_DISKONCHIP" = "y" ]; then
-+   define_tristate CONFIG_REED_SOLOMON y
-+   define_tristate CONFIG_REED_SOLOMON_DEC16 y
-+else
-+  if [ "$CONFIG_MTD_DOCPROBE" = "m" -o \
-+     "$CONFIG_MTD_NAND_RTC_FROM4" = "m" -o \
-+     "$CONFIG_MTD_NAND_DISKONCHIP" = "m" ]; then
-+     define_tristate CONFIG_REED_SOLOMON m
-+     define_tristate CONFIG_REED_SOLOMON_DEC16 y
-+  else
-+     define_tristate CONFIG_REED_SOLOMON n
-+  fi
-+fi
++      case HIDPGETCONNINFO:
++              if (copy_from_user(&ci, (void *) arg, sizeof(ci)))
++                      return -EFAULT;
 +
- endmenu
---- linux-2.4.21/lib/Makefile~mtd-cvs
-+++ linux-2.4.21/lib/Makefile
-@@ -22,6 +22,7 @@
- subdir-$(CONFIG_ZLIB_INFLATE) += zlib_inflate
- subdir-$(CONFIG_ZLIB_DEFLATE) += zlib_deflate
-+subdir-$(CONFIG_REED_SOLOMON) += reed_solomon
- # Include the subdirs, if necessary.
- obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o))
---- /dev/null
-+++ linux-2.4.21/lib/reed_solomon/Makefile
-@@ -0,0 +1,12 @@
-+#
-+# This is a modified version of reed solomon lib, 
-+#
++              err = hidp_get_conninfo(&ci);
++              if (!err && copy_to_user((void *) arg, &ci, sizeof(ci)))
++                      return -EFAULT;
++
++              return err;
++      }
++
++      return -EINVAL;
++}
 +
-+O_TARGET:= reed_solomon.o     
++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
++};
 +
-+export-objs := rslib.o
++static int hidp_sock_create(struct socket *sock, int protocol)
++{
++      struct sock *sk;
 +
-+obj-y := rslib.o
-+obj-m := $(O_TARGET)
++      BT_DBG("sock %p", sock);
 +
-+include $(TOPDIR)/Rules.make
---- /dev/null
-+++ linux-2.4.21/lib/reed_solomon/decode_rs.c
-@@ -0,0 +1,272 @@
-+/* 
-+ * lib/reed_solomon/decode_rs.c
-+ *
-+ * Overview:
-+ *   Generic Reed Solomon encoder / decoder library
-+ *   
-+ * Copyright 2002, Phil Karn, KA9Q
-+ * May be used under the terms of the GNU General Public License (GPL)
-+ *
-+ * Adaption to the kernel by Thomas Gleixner (tglx@linutronix.de)
-+ *
-+ * $Id$
-+ *
-+ */
++      if (sock->type != SOCK_RAW)
++              return -ESOCKTNOSUPPORT;
 +
-+/* Generic data width independent code which is included by the 
-+ * wrappers.
-+ */
-+{ 
-+      int deg_lambda, el, deg_omega;
-+      int i, j, r, k, pad;
-+      int nn = rs->nn;
-+      int nroots = rs->nroots;
-+      int fcr = rs->fcr;
-+      int prim = rs->prim;
-+      int iprim = rs->iprim;
-+      uint16_t *alpha_to = rs->alpha_to;
-+      uint16_t *index_of = rs->index_of;
-+      uint16_t u, q, tmp, num1, num2, den, discr_r, syn_error;
-+      /* Err+Eras Locator poly and syndrome poly The maximum value
-+       * of nroots is 8. So the necessary stack size will be about
-+       * 220 bytes max.
-+       */
-+      uint16_t lambda[nroots + 1], syn[nroots];
-+      uint16_t b[nroots + 1], t[nroots + 1], omega[nroots + 1];
-+      uint16_t root[nroots], reg[nroots + 1], loc[nroots];
-+      int count = 0;
-+      uint16_t msk = (uint16_t) rs->nn;
++      sock->ops = &hidp_sock_ops;
 +
-+      /* Check length parameter for validity */
-+      pad = nn - nroots - len;
-+      if (pad < 0 || pad >= nn)
-+              return -ERANGE;
-+              
-+      /* Does the caller provide the syndrome ? */
-+      if (s != NULL) 
-+              goto decode;
++      if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1)))
++              return -ENOMEM;
 +
-+      /* form the syndromes; i.e., evaluate data(x) at roots of
-+       * g(x) */
-+      for (i = 0; i < nroots; i++)
-+              syn[i] = (((uint16_t) data[0]) ^ invmsk) & msk;
++      MOD_INC_USE_COUNT;
 +
-+      for (j = 1; j < len; j++) {
-+              for (i = 0; i < nroots; i++) {
-+                      if (syn[i] == 0) {
-+                              syn[i] = (((uint16_t) data[j]) ^ 
-+                                        invmsk) & msk;
-+                      } else {
-+                              syn[i] = ((((uint16_t) data[j]) ^
-+                                         invmsk) & msk) ^ 
-+                                      alpha_to[rs_modnn(rs, index_of[syn[i]] +
-+                                                     (fcr + i) * prim)];
-+                      }
-+              }
-+      }
++      sock->state = SS_UNCONNECTED;
++      sock_init_data(sock, sk);
 +
-+      for (j = 0; j < nroots; j++) {
-+              for (i = 0; i < nroots; i++) {
-+                      if (syn[i] == 0) {
-+                              syn[i] = ((uint16_t) par[j]) & msk;
-+                      } else {
-+                              syn[i] = (((uint16_t) par[j]) & msk) ^ 
-+                                      alpha_to[rs_modnn(rs, index_of[syn[i]] +
-+                                                     (fcr+i)*prim)];
-+                      }
-+              }
-+      }
-+      s = syn;
++      sk->destruct = NULL;
++      sk->protocol = protocol;
 +
-+      /* Convert syndromes to index form, checking for nonzero condition */
-+      syn_error = 0;
-+      for (i = 0; i < nroots; i++) {
-+              syn_error |= s[i];
-+              s[i] = index_of[s[i]];
-+      }
++      return 0;
++}
 +
-+      if (!syn_error) {
-+              /* if syndrome is zero, data[] is a codeword and there are no
-+               * errors to correct. So return data[] unmodified
-+               */
-+              count = 0;
-+              goto finish;
-+      }
++static struct net_proto_family hidp_sock_family_ops = {
++      family:         PF_BLUETOOTH,
++      create:         hidp_sock_create
++};
 +
-+ decode:
-+      memset(&lambda[1], 0, nroots * sizeof(lambda[0]));
-+      lambda[0] = 1;
++int __init hidp_init_sockets(void)
++{
++      int err;
 +
-+      if (no_eras > 0) {
-+              /* Init lambda to be the erasure locator polynomial */
-+              lambda[1] = alpha_to[rs_modnn(rs, 
-+                                            prim * (nn - 1 - eras_pos[0]))];
-+              for (i = 1; i < no_eras; i++) {
-+                      u = rs_modnn(rs, prim * (nn - 1 - eras_pos[i]));
-+                      for (j = i + 1; j > 0; j--) {
-+                              tmp = index_of[lambda[j - 1]];
-+                              if (tmp != nn) {
-+                                      lambda[j] ^= 
-+                                              alpha_to[rs_modnn(rs, u + tmp)];
-+                              }
-+                      }
-+              }
-+      }
++      if ((err = bluez_sock_register(BTPROTO_HIDP, &hidp_sock_family_ops)))
++              BT_ERR("Can't register HIDP socket layer (%d)", err);
 +
-+      for (i = 0; i < nroots + 1; i++)
-+              b[i] = index_of[lambda[i]];
++      return err;
++}
 +
-+      /*
-+       * Begin Berlekamp-Massey algorithm to determine error+erasure
-+       * locator polynomial
-+       */
-+      r = no_eras;
-+      el = no_eras;
-+      while (++r <= nroots) { /* r is the step number */
-+              /* Compute discrepancy at the r-th step in poly-form */
-+              discr_r = 0;
-+              for (i = 0; i < r; i++) {
-+                      if ((lambda[i] != 0) && (s[r - i - 1] != nn)) {
-+                              discr_r ^= 
-+                                      alpha_to[rs_modnn(rs, 
-+                                                        index_of[lambda[i]] +
-+                                                        s[r - i - 1])];
-+                      }
-+              }
-+              discr_r = index_of[discr_r];    /* Index form */
-+              if (discr_r == nn) {
-+                      /* 2 lines below: B(x) <-- x*B(x) */
-+                      memmove (&b[1], b, nroots * sizeof (b[0]));
-+                      b[0] = nn;
-+              } else {
-+                      /* 7 lines below: T(x) <-- lambda(x)-discr_r*x*b(x) */
-+                      t[0] = lambda[0];
-+                      for (i = 0; i < nroots; i++) {
-+                              if (b[i] != nn) {
-+                                      t[i + 1] = lambda[i + 1] ^ 
-+                                              alpha_to[rs_modnn(rs, discr_r +
-+                                                                b[i])];
-+                              } else
-+                                      t[i + 1] = lambda[i + 1];
-+                      }
-+                      if (2 * el <= r + no_eras - 1) {
-+                              el = r + no_eras - el;
-+                              /*
-+                               * 2 lines below: B(x) <-- inv(discr_r) *
-+                               * lambda(x)
-+                               */
-+                              for (i = 0; i <= nroots; i++) {
-+                                      b[i] = (lambda[i] == 0) ? nn :
-+                                              rs_modnn(rs, index_of[lambda[i]]
-+                                                       - discr_r + nn);
-+                              }
-+                      } else {
-+                              /* 2 lines below: B(x) <-- x*B(x) */
-+                              memmove(&b[1], b, nroots * sizeof(b[0]));
-+                              b[0] = nn;
-+                      }
-+                      memcpy(lambda, t, (nroots + 1) * sizeof(t[0]));
-+              }
-+      }
++void __exit hidp_cleanup_sockets(void)
++{
++      int err;
 +
-+      /* Convert lambda to index form and compute deg(lambda(x)) */
-+      deg_lambda = 0;
-+      for (i = 0; i < nroots + 1; i++) {
-+              lambda[i] = index_of[lambda[i]];
-+              if (lambda[i] != nn)
-+                      deg_lambda = i;
-+      }
-+      /* Find roots of error+erasure locator polynomial by Chien search */
-+      memcpy(&reg[1], &lambda[1], nroots * sizeof(reg[0]));
-+      count = 0;              /* Number of roots of lambda(x) */
-+      for (i = 1, k = iprim - 1; i <= nn; i++, k = rs_modnn(rs, k + iprim)) {
-+              q = 1;          /* lambda[0] is always 0 */
-+              for (j = deg_lambda; j > 0; j--) {
-+                      if (reg[j] != nn) {
-+                              reg[j] = rs_modnn(rs, reg[j] + j);
-+                              q ^= alpha_to[reg[j]];
-+                      }
-+              }
-+              if (q != 0)
-+                      continue;       /* Not a root */
-+              /* store root (index-form) and error location number */
-+              root[count] = i;
-+              loc[count] = k;
-+              /* If we've already found max possible roots,
-+               * abort the search to save time
-+               */
-+              if (++count == deg_lambda)
-+                      break;
-+      }
-+      if (deg_lambda != count) {
-+              /*
-+               * deg(lambda) unequal to number of roots => uncorrectable
-+               * error detected
-+               */
-+              count = -1;
-+              goto finish;
++      if ((err = bluez_sock_unregister(BTPROTO_HIDP)))
++              BT_ERR("Can't unregister HIDP socket layer (%d)", err);
++}
+--- linux-2.4.21/net/bluetooth/l2cap.c~bluetooth
++++ linux-2.4.21/net/bluetooth/l2cap.c
+@@ -27,7 +27,7 @@
+  *
+  * $Id$
+  */
+-#define VERSION "2.1"
++#define VERSION "2.3"
+ #include <linux/config.h>
+ #include <linux/module.h>
+@@ -284,7 +284,7 @@
+                       l2cap_disconn_req req;
+                       sk->state = BT_DISCONN;
+-                      l2cap_sock_set_timer(sk, HZ * 5);
++                      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);
+@@ -309,11 +309,9 @@
+ 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);
+ }
+@@ -527,7 +525,8 @@
+               goto done;
+ wait:
+-      err = bluez_sock_w4_connect(sk, flags);
++      err = bluez_sock_wait_state(sk, BT_CONNECTED,
++                      sock_sndtimeo(sk, flags & O_NONBLOCK));
+ done:
+       release_sock(sk);
+@@ -760,32 +759,39 @@
+ 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;
+-      l2cap_sock_clear_timer(sk);
+-
+       lock_sock(sk);
+-      sk->shutdown = SHUTDOWN_MASK;
+-      __l2cap_sock_close(sk, ECONNRESET);
+-      release_sock(sk);
++      if (!sk->shutdown) {
++              sk->shutdown = SHUTDOWN_MASK;
++              l2cap_sock_clear_timer(sk);
++              __l2cap_sock_close(sk, 0);
+-      return 0;
++              if (sk->linger)
++                      err = bluez_sock_wait_state(sk, BT_CLOSED, sk->lingertime);
 +      }
-+      /*
-+       * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo
-+       * x**nroots). in index form. Also find deg(omega).
-+       */
-+      deg_omega = deg_lambda - 1;
-+      for (i = 0; i <= deg_omega; i++) {
-+              tmp = 0;
-+              for (j = i; j >= 0; j--) {
-+                      if ((s[i - j] != nn) && (lambda[j] != nn))
-+                              tmp ^=
-+                                  alpha_to[rs_modnn(rs, s[i - j] + lambda[j])];
-+              }
-+              omega[i] = index_of[tmp];
++      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_close(sk);
+-      return 0;
++      l2cap_sock_kill(sk);
++      return err;
+ }
+ /* --------- L2CAP channels --------- */
+@@ -917,10 +923,12 @@
+               hci_conn_put(conn->hcon);
+       }
+-      sk->state = BT_CLOSED;
+-      sk->err   = err;
++      sk->state  = BT_CLOSED;
+       sk->zapped = 1;
++      if (err)
++              sk->err = err;
++
+       if (parent)
+               parent->data_ready(parent, 0);
+       else
+@@ -956,6 +964,22 @@
+       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);
++}
 +
-+      /*
-+       * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 =
-+       * inv(X(l))**(fcr-1) and den = lambda_pr(inv(X(l))) all in poly-form
-+       */
-+      for (j = count - 1; j >= 0; j--) {
-+              num1 = 0;
-+              for (i = deg_omega; i >= 0; i--) {
-+                      if (omega[i] != nn)
-+                              num1 ^= alpha_to[rs_modnn(rs, omega[i] + 
-+                                                      i * root[j])];
+ static void l2cap_chan_ready(struct sock *sk)
+ {
+       struct sock *parent = bluez_pi(sk)->parent;
+@@ -1320,15 +1344,18 @@
+ {
+       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(0);
++      rsp->flags  = __cpu_to_le16(flags);
+       return ptr - data;
+ }
+@@ -1441,7 +1468,7 @@
+       case L2CAP_CR_SUCCESS:
+               sk->state = BT_CONFIG;
+               l2cap_pi(sk)->dcid = dcid;
+-              l2cap_pi(sk)->conf_state |= CONF_REQ_SENT;
++              l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;
+               l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req);
+               break;
+@@ -1476,7 +1503,7 @@
+       l2cap_parse_conf_req(sk, req->data, cmd->len - L2CAP_CONF_REQ_SIZE);
+-      if (flags & 0x01) {
++      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;
+@@ -1489,12 +1516,12 @@
+               goto unlock;
+       /* Output config done */
+-      l2cap_pi(sk)->conf_state |= CONF_OUTPUT_DONE;
++      l2cap_pi(sk)->conf_state |= L2CAP_CONF_OUTPUT_DONE;
+-      if (l2cap_pi(sk)->conf_state & CONF_INPUT_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 & CONF_REQ_SENT)) {
++      } 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);
+       }
+@@ -1520,18 +1547,34 @@
+       if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
+               return -ENOENT;
+-      if (result) {
+-              l2cap_disconn_req req;
++      switch (result) {
++      case L2CAP_CONF_SUCCESS:
++              break;
+-              /* They didn't like our options. Well... we do not negotiate.
+-               * Close channel.
+-               */
++      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);
+-
+-              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_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);
 +              }
-+              num2 = alpha_to[rs_modnn(rs, root[j] * (fcr - 1) + nn)];
-+              den = 0;
+               goto done;
+       }
+@@ -1539,9 +1582,9 @@
+               goto done;
+       /* Input config done */
+-      l2cap_pi(sk)->conf_state |= CONF_INPUT_DONE;
++      l2cap_pi(sk)->conf_state |= L2CAP_CONF_INPUT_DONE;
+-      if (l2cap_pi(sk)->conf_state & CONF_OUTPUT_DONE) {
++      if (l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE) {
+               sk->state = BT_CONNECTED;
+               l2cap_chan_ready(sk);
+       }
+@@ -1592,13 +1635,42 @@
+       if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
+               return 0;
+-      l2cap_chan_del(sk, ECONNABORTED);
++      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;
 +
-+              /* lambda[i+1] for i even is the formal derivative
-+               * lambda_pr of lambda[i] */
-+              for (i = min(deg_lambda, nroots - 1) & ~1; i >= 0; i -= 2) {
-+                      if (lambda[i + 1] != nn) {
-+                              den ^= alpha_to[rs_modnn(rs, lambda[i + 1] + 
-+                                                     i * root[j])];
-+                      }
-+              }
-+              /* Apply error to data */
-+              if (num1 != 0 && loc[j] >= pad) {
-+                      uint16_t cor = alpha_to[rs_modnn(rs,index_of[num1] + 
-+                                                     index_of[num2] +
-+                                                     nn - index_of[den])];
-+                      /* Store the error correction pattern, if a
-+                       * correction buffer is available */
-+                      if (corr) {
-+                              corr[j] = cor;
-+                      } else {
-+                              /* If a data buffer is given and the
-+                               * error is inside the message,
-+                               * correct it */
-+                              if (data && (loc[j] < (nn - nroots)))
-+                                      data[loc[j] - pad] ^= cor;
-+                      }
-+              }
-+      }
++      type = __le16_to_cpu(req->type);
 +
-+finish:
-+      if (eras_pos != NULL) {
-+              for (i = 0; i < count; i++)
-+                      eras_pos[i] = loc[i] - pad;
-+      }
-+      return count;
++      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;
 +}
---- /dev/null
-+++ linux-2.4.21/lib/reed_solomon/encode_rs.c
-@@ -0,0 +1,54 @@
-+/* 
-+ * lib/reed_solomon/encode_rs.c
-+ *
-+ * Overview:
-+ *   Generic Reed Solomon encoder / decoder library
-+ *   
-+ * Copyright 2002, Phil Karn, KA9Q
-+ * May be used under the terms of the GNU General Public License (GPL)
-+ *
-+ * Adaption to the kernel by Thomas Gleixner (tglx@linutronix.de)
-+ *
-+ * $Id$
-+ *
-+ */
 +
-+/* Generic data width independent code which is included by the 
-+ * wrappers.
-+ * int encode_rsX (struct rs_control *rs, uintX_t *data, int len, uintY_t *par)
-+ */
++static inline int l2cap_information_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, u8 *data)
 +{
-+      int i, j, pad;
-+      int nn = rs->nn;
-+      int nroots = rs->nroots;
-+      uint16_t *alpha_to = rs->alpha_to;
-+      uint16_t *index_of = rs->index_of;
-+      uint16_t *genpoly = rs->genpoly;
-+      uint16_t fb;
-+      uint16_t msk = (uint16_t) rs->nn;
++      l2cap_info_rsp *rsp = (l2cap_info_rsp *) data;
++      u16 type, result;
 +
-+      /* Check length parameter for validity */
-+      pad = nn - nroots - len;
-+      if (pad < 0 || pad >= nn)
-+              return -ERANGE;
++      type   = __le16_to_cpu(rsp->type);
++      result = __le16_to_cpu(rsp->result);
++
++      BT_DBG("type 0x%4.4x result 0x%2.2x", type, result);
 +
-+      for (i = 0; i < len; i++) {
-+              fb = index_of[((((uint16_t) data[i])^invmsk) & msk) ^ par[0]];
-+              /* feedback term is non-zero */
-+              if (fb != nn) { 
-+                      for (j = 1; j < nroots; j++) {
-+                              par[j] ^= alpha_to[rs_modnn(rs, fb + 
-+                                                       genpoly[nroots - j])];
-+                      }
-+              }
-+              /* Shift */
-+              memmove(&par[0], &par[1], sizeof(uint16_t) * (nroots - 1));
-+              if (fb != nn) {
-+                      par[nroots - 1] = alpha_to[rs_modnn(rs, 
-+                                                          fb + genpoly[0])];
-+              } else {
-+                      par[nroots - 1] = 0;
-+              }
-+      }
 +      return 0;
 +}
---- /dev/null
-+++ linux-2.4.21/lib/reed_solomon/rslib.c
-@@ -0,0 +1,335 @@
-+/* 
-+ * lib/reed_solomon/rslib.c
-+ *
-+ * Overview:
-+ *   Generic Reed Solomon encoder / decoder library
-+ *   
-+ * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
-+ *
-+ * Reed Solomon code lifted from reed solomon library written by Phil Karn
-+ * Copyright 2002 Phil Karn, KA9Q
-+ *
-+ * $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
-+ * published by the Free Software Foundation.
-+ *
-+ * Description:
-+ *    
-+ * The generic Reed Solomon library provides runtime configurable
-+ * encoding / decoding of RS codes.
-+ * Each user must call init_rs to get a pointer to a rs_control
-+ * structure for the given rs parameters. This structure is either
-+ * generated or a already available matching control structure is used.
-+ * If a structure is generated then the polynomial arrays for
-+ * fast encoding / decoding are built. This can take some time so
-+ * make sure not to call this function from a time critical path.
-+ * Usually a module / driver should initialize the necessary 
-+ * rs_control structure on module / driver init and release it
-+ * on exit.
-+ * The encoding puts the calculated syndrome into a given syndrome 
-+ * buffer. 
-+ * The decoding is a two step process. The first step calculates
-+ * the syndrome over the received (data + syndrome) and calls the
-+ * second stage, which does the decoding / error correction itself.
-+ * Many hw encoders provide a syndrome calculation over the received
-+ * data + syndrome and can call the second stage directly.
-+ *
-+ */
 +
-+#include <linux/errno.h>
-+#include <linux/kernel.h>
-+#include <linux/init.h>
-+#include <linux/module.h>
-+#include <linux/rslib.h>
-+#include <linux/slab.h>
-+#include <asm/semaphore.h>
+ static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
+ {
+       __u8 *data = skb->data;
+@@ -1606,6 +1678,8 @@
+       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;
+@@ -1621,6 +1695,10 @@
+               }
+               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;
+@@ -1645,23 +1723,23 @@
+                       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:
++                      break;
++
+               case L2CAP_INFO_REQ:
++                      err = l2cap_information_req(conn, &cmd, data);
++                      break;
++
+               case L2CAP_INFO_RSP:
+-                      l2cap_raw_recv(conn, skb);
++                      err = l2cap_information_rsp(conn, &cmd, data);
+                       break;
+               default:
+-                      BT_ERR("Uknown signaling command 0x%2.2x", cmd.code);
++                      BT_ERR("Unknown signaling command 0x%2.2x", cmd.code);
+                       err = -EINVAL;
+                       break;
+               };
+@@ -1670,7 +1748,7 @@
+                       l2cap_cmd_rej rej;
+                       BT_DBG("error %d", err);
+-                      /* FIXME: Map err to a valid reason. */
++                      /* 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);
+               }
+@@ -1943,26 +2021,36 @@
+                       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 small (len %d)", skb->len);
++                      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;
+-              BT_DBG("Start: total len %d, frag len %d", len, skb->len);
+-
+               if (len == skb->len) {
+                       /* Complete frame received */
+                       l2cap_recv_frame(conn, skb);
+                       return 0;
+               }
+-              /* Allocate skb for the complete frame (with header) */
+-              if (!(conn->rx_skb = bluez_skb_alloc(len, GFP_ATOMIC)))
++              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);
+@@ -1972,15 +2060,17 @@
+               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 large (len %d, expect %d)",
++                      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;
+               }
+@@ -2114,6 +2204,16 @@
+               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);
+--- linux-2.4.21/net/bluetooth/rfcomm/core.c~bluetooth
++++ linux-2.4.21/net/bluetooth/rfcomm/core.c
+@@ -51,7 +51,7 @@
+ #include <net/bluetooth/l2cap.h>
+ #include <net/bluetooth/rfcomm.h>
+-#define VERSION "1.0"
++#define VERSION "1.1"
+ #ifndef CONFIG_BLUEZ_RFCOMM_DEBUG
+ #undef  BT_DBG
+@@ -198,10 +198,11 @@
+       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->credits    = RFCOMM_MAX_CREDITS;
++      d->cfc        = RFCOMM_CFC_DISABLED;
+       d->rx_credits = RFCOMM_DEFAULT_CREDITS;
+ }
+@@ -274,13 +275,13 @@
+ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel)
+ {
+       struct rfcomm_session *s;
+-      u8 dlci = __dlci(0, channel);
+       int err = 0;
++      u8 dlci;
+-      BT_DBG("dlc %p state %ld %s %s channel %d dlci %d", 
+-                      d, d->state, batostr(src), batostr(dst), channel, dlci);
++      BT_DBG("dlc %p state %ld %s %s channel %d", 
++                      d, d->state, batostr(src), batostr(dst), channel);
+-      if (dlci < 1 || dlci > 62)
++      if (channel < 1 || channel > 30)
+               return -EINVAL;
+       if (d->state != BT_OPEN && d->state != BT_CLOSED)
+@@ -293,20 +294,23 @@
+                       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->dlci     = dlci;
++      d->addr     = __addr(s->initiator, dlci);
++      d->priority = 7;
+-      d->state   = BT_CONFIG;
++      d->state    = BT_CONFIG;
+       rfcomm_dlc_link(s, d);
+-      d->mtu     = s->mtu;
+-      d->credits = s->credits;
++      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);
+@@ -406,7 +410,7 @@
+ {
+       BT_DBG("dlc %p state %ld", d, d->state);
+-      if (!d->credits) {
++      if (!d->cfc) {
+               d->v24_sig |= RFCOMM_V24_FC;
+               set_bit(RFCOMM_MSC_PENDING, &d->flags);
+       }
+@@ -417,7 +421,7 @@
+ {
+       BT_DBG("dlc %p state %ld", d, d->state);
+-      if (!d->credits) {
++      if (!d->cfc) {
+               d->v24_sig &= ~RFCOMM_V24_FC;
+               set_bit(RFCOMM_MSC_PENDING, &d->flags);
+       }
+@@ -470,8 +474,8 @@
+       s->state = state;
+       s->sock  = sock;
+-      s->mtu     = RFCOMM_DEFAULT_MTU;
+-      s->credits = RFCOMM_MAX_CREDITS;
++      s->mtu   = RFCOMM_DEFAULT_MTU;
++      s->cfc   = RFCOMM_CFC_UNKNOWN;
+       
+       list_add(&s->list, &session_list);
+@@ -707,7 +711,7 @@
+       hdr->len  = __len8(sizeof(*mcc) + 1);
+       mcc = (void *) ptr; ptr += sizeof(*mcc);
+-      mcc->type = __mcc_type(s->initiator, RFCOMM_NSC);
++      mcc->type = __mcc_type(cr, RFCOMM_NSC);
+       mcc->len  = __len8(1);
+       /* Type that we didn't like */
+@@ -733,16 +737,16 @@
+       hdr->len  = __len8(sizeof(*mcc) + sizeof(*pn));
+       mcc = (void *) ptr; ptr += sizeof(*mcc);
+-      mcc->type = __mcc_type(s->initiator, RFCOMM_PN);
++      mcc->type = __mcc_type(cr, RFCOMM_PN);
+       mcc->len  = __len8(sizeof(*pn));
+       pn = (void *) ptr; ptr += sizeof(*pn);
+       pn->dlci        = d->dlci;
+-      pn->priority    = 0;
++      pn->priority    = d->priority;
+       pn->ack_timer   = 0;
+       pn->max_retrans = 0;
+-      if (d->credits) {
++      if (s->cfc) {
+               pn->flow_ctrl = cr ? 0xf0 : 0xe0;
+               pn->credits = RFCOMM_DEFAULT_CREDITS;
+       } else {
+@@ -842,7 +846,51 @@
+       msc = (void *) ptr; ptr += sizeof(*msc);
+       msc->dlci    = __addr(1, dlci);
+-      msc->v24_sig = v24_sig;
++      msc->v24_sig = v24_sig | 0x01;
 +
-+/* This list holds all currently allocated rs control structures */
-+static LIST_HEAD (rslist);
-+/* Protection for the list */
-+static DECLARE_MUTEX(rslistlock);
++      *ptr = __fcs(buf); ptr++;
 +
-+/** 
-+ * rs_init - Initialize a Reed-Solomon codec
-+ *
-+ * @symsize:  symbol size, bits (1-8)
-+ * @gfpoly:   Field generator polynomial coefficients
-+ * @fcr:      first root of RS code generator polynomial, index form
-+ * @prim:     primitive element to generate polynomial roots
-+ * @nroots:   RS code generator polynomial degree (number of roots)
-+ *
-+ * Allocate a control structure and the polynom arrays for faster
-+ * en/decoding. Fill the arrays according to the given parameters
-+ */
-+static struct rs_control *rs_init(int symsize, int gfpoly, int fcr, 
-+                                 int prim, int nroots)
++      return rfcomm_send_frame(s, buf, ptr - buf);
++}
++
++static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr)
 +{
-+      struct rs_control *rs;
-+      int i, j, sr, root, iprim;
++      struct rfcomm_hdr *hdr;
++      struct rfcomm_mcc *mcc;
++      u8 buf[16], *ptr = buf;
 +
-+      /* Allocate the control structure */
-+      rs = kmalloc(sizeof (struct rs_control), GFP_KERNEL);
-+      if (rs == NULL)
-+              return NULL;
++      BT_DBG("%p cr %d", s, cr);
 +
-+      INIT_LIST_HEAD(&rs->list);
++      hdr = (void *) ptr; ptr += sizeof(*hdr);
++      hdr->addr = __addr(s->initiator, 0);
++      hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
++      hdr->len  = __len8(sizeof(*mcc));
 +
-+      rs->mm = symsize;
-+      rs->nn = (1 << symsize) - 1;
-+      rs->fcr = fcr;
-+      rs->prim = prim;
-+      rs->nroots = nroots;
-+      rs->gfpoly = gfpoly;
++      mcc = (void *) ptr; ptr += sizeof(*mcc);
++      mcc->type = __mcc_type(cr, RFCOMM_FCOFF);
++      mcc->len  = __len8(0);
 +
-+      /* Allocate the arrays */
-+      rs->alpha_to = kmalloc(sizeof(uint16_t) * (rs->nn + 1), GFP_KERNEL);
-+      if (rs->alpha_to == NULL)
-+              goto errrs;
++      *ptr = __fcs(buf); ptr++;
 +
-+      rs->index_of = kmalloc(sizeof(uint16_t) * (rs->nn + 1), GFP_KERNEL);
-+      if (rs->index_of == NULL)
-+              goto erralp;
++      return rfcomm_send_frame(s, buf, ptr - buf);
++}
 +
-+      rs->genpoly = kmalloc(sizeof(uint16_t) * (rs->nroots + 1), GFP_KERNEL);
-+      if(rs->genpoly == NULL)
-+              goto erridx;
++static int rfcomm_send_fcon(struct rfcomm_session *s, int cr)
++{
++      struct rfcomm_hdr *hdr;
++      struct rfcomm_mcc *mcc;
++      u8 buf[16], *ptr = buf;
 +
-+      /* Generate Galois field lookup tables */
-+      rs->index_of[0] = rs->nn;       /* log(zero) = -inf */
-+      rs->alpha_to[rs->nn] = 0;       /* alpha**-inf = 0 */
-+      sr = 1;
-+      for (i = 0; i < rs->nn; i++) {
-+              rs->index_of[sr] = i;
-+              rs->alpha_to[i] = sr;
-+              sr <<= 1;
-+              if (sr & (1 << symsize))
-+                      sr ^= gfpoly;
-+              sr &= rs->nn;
-+      }
-+      /* If it's not primitive, exit */
-+      if(sr != 1)
-+              goto errpol;
++      BT_DBG("%p cr %d", s, cr);
 +
-+      /* Find prim-th root of 1, used in decoding */
-+      for(iprim = 1; (iprim % prim) != 0; iprim += rs->nn);
-+      /* prim-th root of 1, index form */
-+      rs->iprim = iprim / prim;
++      hdr = (void *) ptr; ptr += sizeof(*hdr);
++      hdr->addr = __addr(s->initiator, 0);
++      hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
++      hdr->len  = __len8(sizeof(*mcc));
 +
-+      /* Form RS code generator polynomial from its roots */
-+      rs->genpoly[0] = 1;
-+      for (i = 0, root = fcr * prim; i < nroots; i++, root += prim) {
-+              rs->genpoly[i + 1] = 1;
-+              /* Multiply rs->genpoly[] by  @**(root + x) */
-+              for (j = i; j > 0; j--) {
-+                      if (rs->genpoly[j] != 0) {
-+                              rs->genpoly[j] = rs->genpoly[j -1] ^ 
-+                                      rs->alpha_to[rs_modnn(rs, 
-+                                      rs->index_of[rs->genpoly[j]] + root)];
-+                      } else
-+                              rs->genpoly[j] = rs->genpoly[j - 1];
++      mcc = (void *) ptr; ptr += sizeof(*mcc);
++      mcc->type = __mcc_type(cr, RFCOMM_FCON);
++      mcc->len  = __len8(0);
+       *ptr = __fcs(buf); ptr++;
+@@ -1076,6 +1124,8 @@
+               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);
+       }
+@@ -1085,29 +1135,23 @@
+ 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 (cr) {
+-              if (pn->flow_ctrl == 0xf0) {
+-                      d->tx_credits = pn->credits;
+-              } else {
+-                      set_bit(RFCOMM_TX_THROTTLED, &d->flags);
+-                      d->credits = 0;
+-              }
+-
+-              d->mtu = btohs(pn->mtu);
++      if (pn->flow_ctrl == 0xf0 || pn->flow_ctrl == 0xe0) {
++              d->cfc = s->cfc = RFCOMM_CFC_ENABLED;
++              d->tx_credits = pn->credits;
+       } else {
+-              if (pn->flow_ctrl == 0xe0) {
+-                      d->tx_credits = pn->credits;
+-              } else {
+-                      set_bit(RFCOMM_TX_THROTTLED, &d->flags);
+-                      d->credits = 0;
+-              }
+-
+-              d->mtu = btohs(pn->mtu);
++              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;
+ }
+@@ -1133,7 +1177,7 @@
+                       switch (d->state) {
+                       case BT_CONFIG:
+                               rfcomm_apply_pn(d, cr, pn);
+-                              
++
+                               d->state = BT_CONNECT;
+                               rfcomm_send_sabm(s, d->dlci);
+                               break;
+@@ -1144,7 +1188,7 @@
+               if (!cr)
+                       return 0;
+-              
++
+               /* PN request for non existing DLC.
+                * Assume incomming connection. */
+               if (rfcomm_connect_ind(s, channel, &d)) {
+@@ -1153,7 +1197,7 @@
+                       rfcomm_dlc_link(s, d);
+                       rfcomm_apply_pn(d, cr, pn);
+-                      
++
+                       d->state = BT_OPEN;
+                       rfcomm_send_pn(s, 0, d);
+               } else {
+@@ -1198,6 +1242,14 @@
+       }
+       /* 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) {
+@@ -1223,23 +1275,26 @@
+               }
+       }
+       if (rpn->param_mask & RFCOMM_RPN_PM_FLOW) {
+-              if (rpn->flow_ctrl != RFCOMM_RPN_FLOW_NONE) {
+-                      BT_DBG("RPN flow ctrl mismatch 0x%x", rpn->flow_ctrl);
+-                      rpn->flow_ctrl = RFCOMM_RPN_FLOW_NONE;
++              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) {
+-              if (rpn->xon_char != RFCOMM_RPN_XON_CHAR) {
+-                      BT_DBG("RPN XON char mismatch 0x%x", rpn->xon_char);
+-                      rpn->xon_char = RFCOMM_RPN_XON_CHAR;
++              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) {
+-              if (rpn->xoff_char != RFCOMM_RPN_XOFF_CHAR) {
+-                      BT_DBG("RPN XOFF char mismatch 0x%x", rpn->xoff_char);
+-                      rpn->xoff_char = RFCOMM_RPN_XOFF_CHAR;
++              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;
+               }
+       }
+@@ -1280,12 +1335,12 @@
+       BT_DBG("dlci %d cr %d v24 0x%x", dlci, cr, msc->v24_sig);
+-      if (!cr)
++      d = rfcomm_dlc_get(s, dlci);
++      if (!d)
+               return 0;
+-      d = rfcomm_dlc_get(s, dlci);
+-      if (d) {
+-              if (msc->v24_sig & RFCOMM_V24_FC && !d->credits)
++      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);
+@@ -1296,7 +1351,11 @@
+               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;
+ }
+@@ -1330,6 +1389,20 @@
+               rfcomm_recv_msc(s, cr, skb);
+               break;
++      case RFCOMM_FCOFF:
++              if (cr) {
++                      set_bit(RFCOMM_TX_THROTTLED, &s->flags);
++                      rfcomm_send_fcoff(s, 0);
 +              }
-+              /* rs->genpoly[0] can never be zero */
-+              rs->genpoly[0] = 
-+                      rs->alpha_to[rs_modnn(rs, 
-+                              rs->index_of[rs->genpoly[0]] + root)];
-+      }
-+      /* convert rs->genpoly[] to index form for quicker encoding */
-+      for (i = 0; i <= nroots; i++)
-+              rs->genpoly[i] = rs->index_of[rs->genpoly[i]];
-+      return rs;
++              break;
 +
-+      /* Error exit */
-+errpol:
-+      kfree(rs->genpoly);
-+erridx:
-+      kfree(rs->index_of);
-+erralp:
-+      kfree(rs->alpha_to);
-+errrs:
-+      kfree(rs);
-+      return NULL;
-+}
++      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);
+@@ -1358,7 +1431,7 @@
+               goto drop;
+       }
+-      if (pf && d->credits) {
++      if (pf && d->cfc) {
+               u8 credits = *(u8 *) skb->data; skb_pull(skb, 1);
+               d->tx_credits += credits;
+@@ -1463,20 +1536,20 @@
+       struct sk_buff *skb;
+       int err;
+-      BT_DBG("dlc %p state %ld credits %d rx_credits %d tx_credits %d", 
+-                      d, d->state, d->credits, d->rx_credits, d->tx_credits);
++      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->credits) {
++      if (d->cfc) {
+               /* CFC enabled. 
+                * Give them some credits */
+               if (!test_bit(RFCOMM_RX_THROTTLED, &d->flags) &&
+-                              d->rx_credits <= (d->credits >> 2)) {
+-                      rfcomm_send_credits(d->session, d->addr, d->credits - d->rx_credits);
+-                      d->rx_credits = d->credits;
++                              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. 
+@@ -1497,7 +1570,7 @@
+               d->tx_credits--;
+       }
+-      if (d->credits && !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);
+@@ -1520,7 +1593,11 @@
+                       continue;
+               }
+-              if (d->state == BT_CONNECTED || d->state == BT_DISCONN)
++              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);
+       }
+ }
+@@ -1577,9 +1654,10 @@
+       nsock->sk->state_change = rfcomm_l2state_change;
+       s = rfcomm_session_add(nsock, BT_OPEN);
+-      if (s)
++      if (s) {
+               rfcomm_session_hold(s);
+-      else
++              rfcomm_schedule(RFCOMM_SCHED_RX);
++      } else
+               sock_release(nsock);
+ }
+@@ -1815,6 +1893,8 @@
+ /* ---- Initialization ---- */
+ int __init rfcomm_init(void)
+ {
++      l2cap_load();
 +
-+/** 
-+ *  free_rs - Free the rs control structure, if its not longer used
-+ *
-+ *  @rs:      the control structure which is not longer used by the
-+ *            caller
-+ */
-+void free_rs(struct rs_control *rs)
-+{
-+      down(&rslistlock);
-+      rs->users--;
-+      if(!rs->users) {
-+              list_del(&rs->list);
-+              kfree(rs->alpha_to);
-+              kfree(rs->index_of);
-+              kfree(rs->genpoly);
-+              kfree(rs);
+       kernel_thread(rfcomm_run, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+       rfcomm_init_sockets();
+--- linux-2.4.21/net/bluetooth/rfcomm/sock.c~bluetooth
++++ linux-2.4.21/net/bluetooth/rfcomm/sock.c
+@@ -188,8 +188,10 @@
+       BT_DBG("parent %p", parent);
+       /* Close not yet accepted dlcs */
+-      while ((sk = bluez_accept_dequeue(parent, NULL)))
++      while ((sk = bluez_accept_dequeue(parent, NULL))) {
+               rfcomm_sock_close(sk);
++              rfcomm_sock_kill(sk);
++      }
+       parent->state  = BT_CLOSED;
+       parent->zapped = 1;
+@@ -211,15 +213,10 @@
+       sock_put(sk);
+ }
+-/* Close socket.
+- * Must be called on unlocked socket.
+- */
+-static void rfcomm_sock_close(struct sock *sk)
++static void __rfcomm_sock_close(struct sock *sk)
+ {
+       struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
+-      lock_sock(sk);
+-
+       BT_DBG("sk %p state %d socket %p", sk, sk->state, sk->socket);
+       switch (sk->state) {
+@@ -236,11 +233,17 @@
+       default:
+               sk->zapped = 1;
+               break;
+-      };
 +      }
-+      up(&rslistlock);
 +}
-+
-+/** 
-+ * init_rs - Find a matching or allocate a new rs control structure
-+ *
-+ *  @symsize: the symbol size (number of bits)
-+ *  @gfpoly:  the extended Galois field generator polynomial coefficients,
-+ *            with the 0th coefficient in the low order bit. The polynomial
-+ *            must be primitive;
-+ *  @fcr:     the first consecutive root of the rs code generator polynomial 
-+ *            in index form
-+ *  @prim:    primitive element to generate polynomial roots
-+ *  @nroots:  RS code generator polynomial degree (number of roots)
++/* Close socket.
++ * Must be called on unlocked socket.
 + */
-+struct rs_control *init_rs(int symsize, int gfpoly, int fcr, int prim, 
-+                         int nroots)
++static void rfcomm_sock_close(struct sock *sk)
 +{
-+      struct list_head        *tmp;
-+      struct rs_control       *rs;
++      lock_sock(sk);
++      __rfcomm_sock_close(sk);
+       release_sock(sk);
+-
+-      rfcomm_sock_kill(sk);
+ }
+ static void rfcomm_sock_init(struct sock *sk, struct sock *parent)
+@@ -374,7 +377,8 @@
+       
+       err = rfcomm_dlc_open(d, &bluez_pi(sk)->src, &sa->rc_bdaddr, sa->rc_channel);
+       if (!err)
+-              err = bluez_sock_w4_connect(sk, flags);
++              err = bluez_sock_wait_state(sk, BT_CONNECTED,
++                              sock_sndtimeo(sk, flags & O_NONBLOCK));
+       release_sock(sk);
+       return err;
+@@ -558,9 +562,6 @@
+       int target, err = 0, copied = 0;
+       long timeo;
+-      if (sk->state != BT_CONNECTED)
+-              return -EINVAL;
+-
+       if (flags & MSG_OOB)
+               return -EOPNOTSUPP;
+@@ -635,23 +636,6 @@
+       return copied ? : err;
+ }
+-static int rfcomm_sock_shutdown(struct socket *sock, int how)
+-{
+-      struct sock *sk = sock->sk;
+-
+-      BT_DBG("sock %p, sk %p", sock, sk);
+-
+-      if (!sk) return 0;
+-
+-      lock_sock(sk);
+-      sk->shutdown = SHUTDOWN_MASK;
+-      if (sk->state == BT_CONNECTED)
+-              rfcomm_dlc_close(rfcomm_pi(sk)->dlc, 0);
+-      release_sock(sk);
+-
+-      return 0;
+-}
+-
+ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
+ {
+       struct sock *sk = sock->sk;
+@@ -711,19 +695,42 @@
+       return err;
+ }
++static int rfcomm_sock_shutdown(struct socket *sock, int how)
++{
++      struct sock *sk = sock->sk;
++      int err = 0;
 +
-+      /* Sanity checks */
-+      if (symsize < 1)
-+              return NULL;
-+      if (fcr < 0 || fcr >= (1<<symsize))
-+              return NULL;
-+      if (prim <= 0 || prim >= (1<<symsize))
-+              return NULL;
-+      if (nroots < 0 || nroots >= (1<<symsize) || nroots > 8)
-+              return NULL;
-+      
-+      down(&rslistlock);
++      BT_DBG("sock %p, sk %p", sock, sk);
 +
-+      /* Walk through the list and look for a matching entry */
-+      list_for_each(tmp, &rslist) {
-+              rs = list_entry(tmp, struct rs_control, list);
-+              if (symsize != rs->mm)
-+                      continue;
-+              if (gfpoly != rs->gfpoly)
-+                      continue;
-+              if (fcr != rs->fcr)
-+                      continue;       
-+              if (prim != rs->prim)
-+                      continue;       
-+              if (nroots != rs->nroots)
-+                      continue;
-+              /* We have a matching one already */
-+              rs->users++;
-+              goto out;
-+      }
++      if (!sk) return 0;
 +
-+      /* Create a new one */
-+      rs = rs_init(symsize, gfpoly, fcr, prim, nroots);
-+      if (rs) {
-+              rs->users = 1;
-+              list_add(&rs->list, &rslist);
++      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);
 +      }
-+out:  
-+      up(&rslistlock);
-+      return rs;
++      release_sock(sk);
++      return err;
 +}
 +
-+#ifdef CONFIG_REED_SOLOMON_ENC8
-+/** 
-+ *  encode_rs8 - Calculate the parity for data values (8bit data width)
-+ *
-+ *  @rs:      the rs control structure
-+ *  @data:    data field of a given type
-+ *  @len:     data length 
-+ *  @par:     parity data, must be initialized by caller (usually all 0)
-+ *  @invmsk:  invert data mask (will be xored on data)
-+ *
-+ *  The parity uses a uint16_t data type to enable
-+ *  symbol size > 8. The calling code must take care of encoding of the
-+ *  syndrome result for storage itself.
-+ */
-+int encode_rs8(struct rs_control *rs, uint8_t *data, int len, uint16_t *par, 
-+             uint16_t invmsk)
-+{
-+#include "encode_rs.c"
-+}
-+EXPORT_SYMBOL_GPL(encode_rs8);
-+#endif
+ 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;
+-      sock_orphan(sk);
+-      rfcomm_sock_close(sk);
++      err = rfcomm_sock_shutdown(sock, 2);
+-      return 0;
++      sock_orphan(sk);
++      rfcomm_sock_kill(sk);
++      return err;
+ }
+ /* ---- RFCOMM core layer callbacks ---- 
+--- linux-2.4.21/net/bluetooth/rfcomm/tty.c~bluetooth
++++ linux-2.4.21/net/bluetooth/rfcomm/tty.c
+@@ -109,6 +109,13 @@
+ 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);
+ }
+@@ -132,10 +139,13 @@
+       struct rfcomm_dev *dev;
+       read_lock(&rfcomm_dev_lock);
 +
-+#ifdef CONFIG_REED_SOLOMON_DEC8
-+/** 
-+ *  decode_rs8 - Decode codeword (8bit data width)
-+ *
-+ *  @rs:      the rs control structure
-+ *  @data:    data field of a given type
-+ *  @par:     received parity data field
-+ *  @len:     data length
-+ *  @s:               syndrome data field (if NULL, syndrome is calculated)
-+ *  @no_eras: number of erasures
-+ *  @eras_pos:        position of erasures, can be NULL
-+ *  @invmsk:  invert data mask (will be xored on data, not on parity!)
-+ *  @corr:    buffer to store correction bitmask on eras_pos
-+ *
-+ *  The syndrome and parity uses a uint16_t data type to enable
-+ *  symbol size > 8. The calling code must take care of decoding of the
-+ *  syndrome result and the received parity before calling this code.
-+ */
-+int decode_rs8(struct rs_control *rs, uint8_t *data, uint16_t *par, int len,
-+             uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk, 
-+             uint16_t *corr)
-+{
-+#include "decode_rs.c"
-+}
-+EXPORT_SYMBOL_GPL(decode_rs8);
-+#endif
+       dev = __rfcomm_dev_get(id);
++      if (dev)
++              rfcomm_dev_hold(dev);
 +
-+#ifdef CONFIG_REED_SOLOMON_ENC16
-+/**
-+ *  encode_rs16 - Calculate the parity for data values (16bit data width)
-+ *
-+ *  @rs:      the rs control structure
-+ *  @data:    data field of a given type
-+ *  @len:     data length 
-+ *  @par:     parity data, must be initialized by caller (usually all 0)
-+ *  @invmsk:  invert data mask (will be xored on data, not on parity!)
-+ *
-+ *  Each field in the data array contains up to symbol size bits of valid data.
-+ */
-+int encode_rs16(struct rs_control *rs, uint16_t *data, int len, uint16_t *par, 
-+      uint16_t invmsk)
-+{
-+#include "encode_rs.c"
-+}
-+EXPORT_SYMBOL_GPL(encode_rs16);
-+#endif
+       read_unlock(&rfcomm_dev_lock);
+-      if (dev) rfcomm_dev_hold(dev);
+       return dev;
+ }
+@@ -260,9 +270,9 @@
+       skb->destructor = rfcomm_wfree;
+ }
+-static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, int priority)
++static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, int force, int priority)
+ {
+-      if (atomic_read(&dev->wmem_alloc) < rfcomm_room(dev->dlc)) {
++      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);
+@@ -328,12 +338,14 @@
+       BT_DBG("dev_id %id flags 0x%x", req.dev_id, req.flags);
+-      if (!capable(CAP_NET_ADMIN))
+-              return -EPERM;
+-
+       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;
++      }
 +
-+#ifdef CONFIG_REED_SOLOMON_DEC16
-+/** 
-+ *  decode_rs16 - Decode codeword (16bit data width)
-+ *
-+ *  @rs:      the rs control structure
-+ *  @data:    data field of a given type
-+ *  @par:     received parity data field
-+ *  @len:     data length
-+ *  @s:               syndrome data field (if NULL, syndrome is calculated)
-+ *  @no_eras: number of erasures
-+ *  @eras_pos:        position of erasures, can be NULL
-+ *  @invmsk:  invert data mask (will be xored on data, not on parity!) 
-+ *  @corr:    buffer to store correction bitmask on eras_pos
-+ *
-+ *  Each field in the data array contains up to symbol size bits of valid data.
-+ */
-+int decode_rs16(struct rs_control *rs, uint16_t *data, uint16_t *par, int len,
-+              uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk, 
-+              uint16_t *corr)
+       if (req.flags & (1 << RFCOMM_HANGUP_NOW))
+               rfcomm_dlc_close(dev->dlc, 0);
+@@ -347,7 +359,7 @@
+       struct rfcomm_dev_list_req *dl;
+       struct rfcomm_dev_info *di;
+       struct list_head *p;
+-      int n = 0, size;
++      int n = 0, size, err;
+       u16 dev_num;
+       BT_DBG("");
+@@ -355,14 +367,11 @@
+       if (get_user(dev_num, (u16 *) arg))
+               return -EFAULT;
+-      if (!dev_num)
++      if (!dev_num || dev_num > (PAGE_SIZE * 4) / sizeof(*di))
+               return -EINVAL;
+       size = sizeof(*dl) + dev_num * sizeof(*di);
+-      if (verify_area(VERIFY_WRITE, (void *)arg, size))
+-              return -EFAULT;
+-
+       if (!(dl = kmalloc(size, GFP_KERNEL)))
+               return -ENOMEM;
+@@ -387,9 +396,10 @@
+       dl->dev_num = n;
+       size = sizeof(*dl) + n * sizeof(*di);
+-      copy_to_user((void *) arg, dl, size);
++      err = copy_to_user((void *) arg, dl, size);
+       kfree(dl);
+-      return 0;
++
++      return err ? -EFAULT : 0;
+ }
+ static int rfcomm_get_dev_info(unsigned long arg)
+@@ -486,7 +496,8 @@
+                               rfcomm_dev_del(dev);
+                               /* We have to drop DLC lock here, otherwise
+-                               * rfcomm_dev_put() will dead lock if it's the last refference */
++                                 rfcomm_dev_put() will dead lock if it's
++                                 the last reference. */
+                               rfcomm_dlc_unlock(dlc);
+                               rfcomm_dev_put(dev);
+                               rfcomm_dlc_lock(dlc);
+@@ -541,6 +552,10 @@
+       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;
+@@ -627,9 +642,9 @@
+               size = min_t(uint, count, dlc->mtu);
+               if (from_user)
+-                      skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_KERNEL);
++                      skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, 0, GFP_KERNEL);
+               else
+-                      skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_ATOMIC);
++                      skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, 0, GFP_ATOMIC);
+               
+               if (!skb)
+                       break;
+@@ -653,6 +668,27 @@
+       return sent ? sent : err;
+ }
++static void rfcomm_tty_put_char(struct tty_struct *tty, unsigned char ch)
 +{
-+#include "decode_rs.c"
-+}
-+EXPORT_SYMBOL_GPL(decode_rs16);
-+#endif
++      struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
++      struct rfcomm_dlc *dlc = dev->dlc;
++      struct sk_buff *skb;
 +
-+EXPORT_SYMBOL_GPL(init_rs);
-+EXPORT_SYMBOL_GPL(free_rs);
++      BT_DBG("tty %p char %x", tty, ch);
 +
-+MODULE_LICENSE("GPL");
-+MODULE_DESCRIPTION("Reed Solomon encoder/decoder");
-+MODULE_AUTHOR("Phil Karn, Thomas Gleixner");
++      skb = rfcomm_wmalloc(dev, 1 + RFCOMM_SKB_RESERVE, 1, GFP_ATOMIC);
 +
---- linux-2.4.21/mm/swap.c~swap-performance
-+++ linux-2.4.21/mm/swap.c
-@@ -28,7 +28,7 @@
- int page_cluster;
++      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;
+@@ -879,6 +915,7 @@
+       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,
+--- linux-2.4.21/net/bluetooth/sco.c~bluetooth
++++ linux-2.4.21/net/bluetooth/sco.c
+@@ -332,8 +332,10 @@
+       BT_DBG("parent %p", parent);
+       /* Close not yet accepted channels */
+-      while ((sk = bluez_accept_dequeue(parent, NULL)))
++      while ((sk = bluez_accept_dequeue(parent, NULL))) {
+               sco_sock_close(sk);
++              sco_sock_kill(sk);
++      }
+       parent->state  = BT_CLOSED;
+       parent->zapped = 1;
+@@ -388,8 +390,6 @@
+       };
  
- pager_daemon_t pager_daemon = {
--      512,    /* base number for calculating the number of tries */
-+      128,    /* base number for calculating the number of tries */
-       SWAP_CLUSTER_MAX,       /* minimum number of tries */
-       8,      /* do swap I/O in clusters of this size */
- };
---- linux-2.4.21/mm/vmalloc.c~vmalloc
-+++ linux-2.4.21/mm/vmalloc.c
-@@ -183,6 +183,9 @@
-               return NULL;
+       release_sock(sk);
+-
+-      sco_sock_kill(sk);
+ }
  
-       size += PAGE_SIZE;
-+#ifdef VMALLOC_ALIGN
-+      size = (size + VMALLOC_ALIGN - 1) & ~(VMALLOC_ALIGN - 1);
-+#endif
-       if (!size) {
-               kfree (area);
-               return NULL;
+ static void sco_sock_init(struct sock *sk, struct sock *parent)
+@@ -508,7 +508,8 @@
+       if ((err = sco_connect(sk)))
+               goto done;
+-      err = bluez_sock_w4_connect(sk, flags);
++      err = bluez_sock_wait_state(sk, BT_CONNECTED,
++                      sock_sndtimeo(sk, flags & O_NONBLOCK));
+ done:
+       release_sock(sk);
+@@ -712,16 +713,23 @@
+ 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;
+-      sock_orphan(sk);
+       sco_sock_close(sk);
++      if (sk->linger) {
++              lock_sock(sk);
++              err = bluez_sock_wait_state(sk, BT_CLOSED, sk->lingertime);
++              release_sock(sk);
++      }
+-      return 0;
++      sock_orphan(sk);
++      sco_sock_kill(sk);
++      return err;
+ }
+ static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent)
+--- linux-2.4.21/net/bluetooth/syms.c~bluetooth
++++ linux-2.4.21/net/bluetooth/syms.c
+@@ -78,4 +78,4 @@
+ EXPORT_SYMBOL(bluez_sock_poll);
+ EXPORT_SYMBOL(bluez_accept_enqueue);
+ EXPORT_SYMBOL(bluez_accept_dequeue);
+-EXPORT_SYMBOL(bluez_sock_w4_connect);
++EXPORT_SYMBOL(bluez_sock_wait_state);
 --- linux-2.4.21/net/core/wireless.c~linux-iw241_we16-6
 +++ linux-2.4.21/net/core/wireless.c
 @@ -2,7 +2,7 @@
                        /* Oh, well.  At least we tried. */
 --- linux-2.4.21/net/netsyms.c~linux-iw241_we16-6
 +++ linux-2.4.21/net/netsyms.c
-@@ -601,6 +601,11 @@
+@@ -160,6 +160,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);
+@@ -601,6 +602,11 @@
  #if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO)
  #include <net/iw_handler.h>
  EXPORT_SYMBOL(wireless_send_event);
index fc249fe..3e8d26e 100644 (file)
@@ -5,7 +5,7 @@ LICENSE = "GPL"
 KV = "2.4.21"
 RMKV = "2"
 PXAV = "1"
-PR = "r4"
+PR = "r5"
 
 SRC_URI = "ftp://ftp.kernel.org/pub/linux/kernel/v2.4/linux-${KV}.tar.bz2 \
           http://lorien.handhelds.org/ftp.arm.linux.org.uk/kernel/v2.4/patch-${KV}-rmk${RMKV}.bz2;patch=1 \