Add NAND flash/YAFFS2 patches for RB532 by David Goodenough
authorFelix Fietkau <nbd@openwrt.org>
Thu, 26 Oct 2006 01:08:41 +0000 (01:08 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Thu, 26 Oct 2006 01:08:41 +0000 (01:08 +0000)
Read the kernel command line from an otherwise unused area of the kernel image (will be used for changing the command line on the fly when copying the system from CF to NAND)

SVN-Revision: 5296

target/image/rb532/Makefile
target/linux/rb532-2.6/config
target/linux/rb532-2.6/patches/100-rb5xx_support.patch
target/linux/rb532-2.6/patches/120-cf.patch
target/linux/rb532-2.6/patches/140-cmdline_hack.patch [new file with mode: 0644]
target/linux/rb532-2.6/patches/500-Nand.patch [new file with mode: 0644]
target/linux/rb532-2.6/patches/510-Yaffs.patch [new file with mode: 0644]

index 2a61a484dcd408245ee4b71e3ad0d555a5482922..7213cf8b8aea2e7dfdffa51d6e306752dd5cf457 100644 (file)
@@ -6,6 +6,7 @@
 #
 include $(TOPDIR)/rules.mk
 include $(INCLUDE_DIR)/image.mk
+include $(INCLUDE_DIR)/kernel.mk
 
 LOADADDR = 0x81000000          # RAM start + 16M 
 KERNEL_ENTRY = 0x80101000
@@ -23,19 +24,41 @@ define Build/Clean
        $(MAKE) -C ../generic/lzma-loader $(LOADER_MAKEOPTS) clean
 endef
 
+CMDLINE_SIZE=512
+CMDLINE_OFFSET=4112
+
 define Image/Prepare
-       cat $(KDIR)/vmlinux | $(STAGING_DIR)/bin/lzma e -si -so -eos -lc1 -lp2 -pb2 > $(KDIR)/vmlinux.lzma
-       $(MAKE) -C ../generic/lzma-loader $(LOADER_MAKEOPTS) clean compile
+       echo 'root=/dev/cfa2 ' | \
+               dd bs=$(CMDLINE_SIZE) count=1 conv=sync | \
+               dd of=$(LINUX_DIR)/vmlinux bs=$(CMDLINE_OFFSET) conv=notrunc seek=1
+       $(KERNEL_CROSS)objcopy -O binary -R .reginfo -R .note -R .comment -R .mdebug -S $(LINUX_DIR)/vmlinux $(LINUX_KERNEL)
 endef
 
 define Image/BuildKernel
+       cat $(KDIR)/vmlinux | $(STAGING_DIR)/bin/lzma e -si -so -eos -lc1 -lp2 -pb2 > $(KDIR)/vmlinux.lzma
+       $(MAKE) -C ../generic/lzma-loader $(LOADER_MAKEOPTS) clean compile
        $(CP) $(KDIR)/loader.elf $(BIN_DIR)/openwrt-$(BOARD)-$(KERNEL)-vmlinux
 endef
 
 PARTITION1=\x80\x01\x01\x00\x27\x01\x20\x7b\x20\x00\x00\x00\xe0\x1e\x00\x00# 4 MB  (kernel part)
 PARTITION2=\x00\x00\x01\x7c\x83\x01\xa0\x64\x00\x1f\x00\x00\x40\x7a\x00\x00# 16 MB (rootfs part)
 
-define Image/Build/jffs2-128k
+define Image/cmdline/jffs2-64k
+block2mtd.block2mtd=/dev/cfa2,65536 root=/dev/mtdblock0 rootfstype=jffs2
+endef
+
+define Image/cmdline/jffs2-128k
+block2mtd.block2mtd=/dev/cfa2,131072 root=/dev/mtdblock0 rootfstype=jffs2
+endef
+
+define Image/cmdline/ext2
+root=/dev/cfa2 rootfstype=ext2
+endef
+
+define Image/Build
+       echo '$(strip $(call Image/cmdline/$(1))) ' | \
+               dd bs=$(CMDLINE_SIZE) count=1 conv=sync | \
+               dd of=$(LINUX_DIR)/vmlinux bs=$(CMDLINE_OFFSET) conv=notrunc seek=1
        ( \
                echo -ne OWRT | dd bs=$$$$((0x1be)) conv=sync; \
                ( \
@@ -44,14 +67,10 @@ define Image/Build/jffs2-128k
                ) | dd bs=$$$$((0x40)) conv=sync; \
                echo -ne '\x55\xaa'; \
                dd if=/dev/zero bs=$$$$((0x3e00)) conv=sync count=1; \
-               dd if=$(KDIR)/loader.elf bs=$$$$((0x3dc000)) conv=sync; \
+               dd if=$(LINUX_DIR)/vmlinux bs=$$$$((0x3dc000)) conv=sync; \
                cat $(KDIR)/root.$(1); \
                echo -ne '\xde\xad\xc0\xde'; \
        ) > $(BIN_DIR)/openwrt-$(BOARD)-$(KERNEL)-$(1).bin
 endef
 
-define Image/Build
-       $(call Image/Build/$(1),$(1))
-endef
-
 $(eval $(call BuildImage))
index 703aad108cf35681b8c520df23e55756c4dd8da7..f6d77cf944e56ed881f8deee5d66afb8ca052652 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Automatically generated make config: don't edit
 # Linux kernel version: 2.6.17
-# Sun Jun 18 17:29:23 2006
+# Wed Oct 25 20:06:48 2006
 #
 CONFIG_MIPS=y
 
@@ -156,7 +156,9 @@ CONFIG_SYSCTL=y
 # CONFIG_AUDIT is not set
 # CONFIG_IKCONFIG is not set
 # CONFIG_RELAY is not set
-CONFIG_INITRAMFS_SOURCE=""
+CONFIG_INITRAMFS_SOURCE="../../root"
+CONFIG_INITRAMFS_ROOT_UID=0
+CONFIG_INITRAMFS_ROOT_GID=0
 CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_EMBEDDED=y
 # CONFIG_KALLSYMS is not set
@@ -294,7 +296,7 @@ CONFIG_INET6_AH=m
 CONFIG_INET6_ESP=m
 CONFIG_INET6_IPCOMP=m
 CONFIG_INET6_XFRM_TUNNEL=m
-# CONFIG_INET6_TUNNEL is not set
+CONFIG_INET6_TUNNEL=m
 # CONFIG_IPV6_TUNNEL is not set
 CONFIG_NETFILTER=y
 # CONFIG_NETFILTER_DEBUG is not set
@@ -637,7 +639,12 @@ CONFIG_MTD_BLOCK2MTD=y
 #
 # NAND Flash Device Drivers
 #
-# CONFIG_MTD_NAND is not set
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_VERIFY_WRITE=y
+CONFIG_MTD_NAND_RB500=y
+CONFIG_MTD_NAND_IDS=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
 
 #
 # OneNAND Flash Device Drivers
@@ -806,13 +813,6 @@ CONFIG_NET_WIRELESS_RTNETLINK=y
 # Obsolete Wireless cards support (pre-802.11)
 #
 # CONFIG_STRIP is not set
-# CONFIG_PCMCIA_WAVELAN is not set
-# CONFIG_PCMCIA_NETWAVE is not set
-
-#
-# Wireless 802.11 Frequency Hopping cards support
-#
-# CONFIG_PCMCIA_RAYCS is not set
 
 #
 # Wireless 802.11b ISA/PCI cards support
@@ -822,12 +822,6 @@ CONFIG_NET_WIRELESS_RTNETLINK=y
 # CONFIG_HERMES is not set
 # CONFIG_ATMEL is not set
 
-#
-# Wireless 802.11b Pcmcia/Cardbus cards support
-#
-# CONFIG_AIRO_CS is not set
-# CONFIG_PCMCIA_WL3501 is not set
-
 #
 # Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support
 #
@@ -837,15 +831,9 @@ CONFIG_HOSTAP_FIRMWARE=y
 CONFIG_HOSTAP_FIRMWARE_NVRAM=y
 # CONFIG_HOSTAP_PLX is not set
 # CONFIG_HOSTAP_PCI is not set
-CONFIG_HOSTAP_CS=m
 # CONFIG_BCM43XX is not set
 CONFIG_NET_WIRELESS=y
 
-#
-# PCMCIA network device support
-#
-# CONFIG_NET_PCMCIA is not set
-
 #
 # Wan interfaces
 #
@@ -918,7 +906,6 @@ CONFIG_INPUT=y
 CONFIG_SERIAL_8250=y
 CONFIG_SERIAL_8250_CONSOLE=y
 # CONFIG_SERIAL_8250_PCI is not set
-# CONFIG_SERIAL_8250_CS is not set
 CONFIG_SERIAL_8250_NR_UARTS=2
 CONFIG_SERIAL_8250_RUNTIME_UARTS=2
 # CONFIG_SERIAL_8250_EXTENDED is not set
@@ -951,13 +938,6 @@ CONFIG_UNIX98_PTYS=y
 # Ftape, the floppy tape device driver
 #
 # CONFIG_DRM is not set
-
-#
-# PCMCIA character devices
-#
-# CONFIG_SYNCLINK_CS is not set
-# CONFIG_CARDMAN_4000 is not set
-# CONFIG_CARDMAN_4040 is not set
 # CONFIG_RAW_DRIVER is not set
 
 #
@@ -1068,7 +1048,7 @@ CONFIG_LEDS_TRIGGER_TIMER=y
 #
 # File systems
 #
-CONFIG_EXT2_FS=m
+CONFIG_EXT2_FS=y
 # CONFIG_EXT2_FS_XATTR is not set
 # CONFIG_EXT2_FS_XIP is not set
 CONFIG_EXT3_FS=m
@@ -1140,6 +1120,7 @@ CONFIG_RAMFS=y
 # CONFIG_BEFS_FS is not set
 # CONFIG_BFS_FS is not set
 # CONFIG_EFS_FS is not set
+CONFIG_YAFFS_FS=y
 # CONFIG_JFFS_FS is not set
 CONFIG_JFFS2_FS=y
 CONFIG_JFFS2_FS_DEBUG=0
@@ -1270,7 +1251,7 @@ CONFIG_NLS_UTF8=m
 CONFIG_LOG_BUF_SHIFT=14
 # CONFIG_DEBUG_FS is not set
 CONFIG_CROSSCOMPILE=y
-CONFIG_CMDLINE="root=/dev/mtdblock1 rootfstype=squashfs,jffs2 noinitrd console=ttyS0,115200"
+CONFIG_CMDLINE="noinitrd console=ttyS0,115200"
 
 #
 # Security options
index f846c78c2b5418f465a37c752128ae74f3f38ce2..9a8f21d2076f056972697be3552736460d52c54b 100644 (file)
@@ -567,7 +567,7 @@ diff -urN linux.old/arch/mips/pci/pci-rc32434.c linux.dev/arch/mips/pci/pci-rc32
 diff -urN linux.old/arch/mips/rb500/devices.c linux.dev/arch/mips/rb500/devices.c
 --- linux.old/arch/mips/rb500/devices.c        1970-01-01 01:00:00.000000000 +0100
 +++ linux.dev/arch/mips/rb500/devices.c        2006-10-11 21:56:38.000000000 +0200
-@@ -0,0 +1,211 @@
+@@ -0,0 +1,198 @@
 +#include <linux/kernel.h>
 +#include <linux/init.h>
 +#include <linux/module.h>
@@ -677,10 +677,25 @@ diff -urN linux.old/arch/mips/rb500/devices.c linux.dev/arch/mips/rb500/devices.
 +      .num_resources = ARRAY_SIZE(cf_slot0_res),
 +};
 +
++/* Resources and device for NAND.  There is no data needed and no irqs, so just define the memory used. */
++static struct resource nand_slot0_res[] = {
++      {
++              .name = "nand_membase",
++              .flags = IORESOURCE_MEM 
++      }
++};
++ 
++static struct platform_device nand_slot0 = {
++      .id = 0,
++      .name = "rb500-nand",
++      .resource = nand_slot0_res,
++      .num_resources = ARRAY_SIZE(nand_slot0_res),
++};
 +
 +
 +static struct platform_device *rb500_devs[] = {
 +      &korina_dev0,
++      &nand_slot0,
 +      &cf_slot0
 +};
 +
@@ -711,6 +726,7 @@ diff -urN linux.old/arch/mips/rb500/devices.c linux.dev/arch/mips/rb500/devices.
 +
 +/* DEVICE CONTROLLER 1 */
 +#define CFG_DC_DEV1 (void*)0xb8010010
++#define CFG_DC_DEV2 (void*)0xb8010020
 +#define CFG_DC_DEVBASE    0x0
 +#define CFG_DC_DEVMASK    0x4
 +#define CFG_DC_DEVC       0x8
@@ -726,6 +742,10 @@ diff -urN linux.old/arch/mips/rb500/devices.c linux.dev/arch/mips/rb500/devices.
 +              cf_slot0_res[0].start = readl(CFG_DC_DEV1 + CFG_DC_DEVBASE);
 +              cf_slot0_res[0].end = cf_slot0_res[0].start + 0x1000;
 +      }
++      
++      /* There is always a NAND device */
++      nand_slot0_res[0].start = readl( CFG_DC_DEV2 + CFG_DC_DEVBASE);
++      nand_slot0_res[0].end = nand_slot0_res[0].start + 0x1000;
 +              
 +      return platform_add_devices(rb500_devs, ARRAY_SIZE(rb500_devs));
 +}
@@ -745,39 +765,6 @@ diff -urN linux.old/arch/mips/rb500/devices.c linux.dev/arch/mips/rb500/devices.
 +extern void block2mtd_setup(char *initstr);
 +extern void mount_devfs_fs(void);
 +
-+static int __init setup_mtd(void)
-+{
-+      struct hd_struct **part;
-+      int num = 0, i;
-+      char initstr[64];
-+      
-+      if (cf_slot0_data.gd == NULL)
-+              return 0;
-+      
-+      /* count partitions */
-+      part = cf_slot0_data.gd->part;
-+      while (part[num] != NULL) {
-+              num++;
-+      }
-+
-+      if (num < 2)
-+              return 0;
-+
-+      mount_devfs_fs();
-+      printk("Setting up block2mtd devices\n");
-+
-+      block2mtd_setup("/dev/cf/card0/part1,131072,kernel");
-+      block2mtd_setup("/dev/cf/card0/part2,131072,rootfs");
-+      
-+      for (i = 2; part[i]; i++) {
-+              sprintf(initstr, "/dev/cf/card0/part%d,131072,part%d", i + 1, i + 1);
-+              block2mtd_setup(initstr);
-+      }
-+      
-+      return 0;
-+}
-+
-+late_initcall(setup_mtd);
 +#endif
 diff -urN linux.old/arch/mips/rb500/early_serial.c linux.dev/arch/mips/rb500/early_serial.c
 --- linux.old/arch/mips/rb500/early_serial.c   1970-01-01 01:00:00.000000000 +0100
@@ -1262,7 +1249,7 @@ diff -urN linux.old/arch/mips/rb500/Makefile linux.dev/arch/mips/rb500/Makefile
 diff -urN linux.old/arch/mips/rb500/misc.c linux.dev/arch/mips/rb500/misc.c
 --- linux.old/arch/mips/rb500/misc.c   1970-01-01 01:00:00.000000000 +0100
 +++ linux.dev/arch/mips/rb500/misc.c   2006-10-11 21:56:38.000000000 +0200
-@@ -0,0 +1,54 @@
+@@ -0,0 +1,56 @@
 +#include <linux/module.h>
 +#include <linux/kernel.h>   /* printk() */
 +#include <linux/types.h>    /* size_t */
@@ -1273,7 +1260,7 @@ diff -urN linux.old/arch/mips/rb500/misc.c linux.dev/arch/mips/rb500/misc.c
 +#define GPIO_BADDR  0xb8050000
 +
 +
-+static unsigned char *devCtl3Base = (unsigned char *) KSEG1ADDR(0x18010030);
++static volatile unsigned char *devCtl3Base = 0;
 +static unsigned char latchU5State = 0;
 +static spinlock_t clu5Lock = SPIN_LOCK_UNLOCKED;
 +
@@ -1294,6 +1281,8 @@ diff -urN linux.old/arch/mips/rb500/misc.c linux.dev/arch/mips/rb500/misc.c
 +    unsigned flags;
 +    spin_lock_irqsave(&clu5Lock, flags);
 +    latchU5State = (latchU5State | orMask) & ~nandMask;
++    if( !devCtl3Base) devCtl3Base = (volatile unsigned char *)
++              KSEG1ADDR(*(volatile unsigned *) KSEG1ADDR(0x18010030));
 +    *devCtl3Base = latchU5State;
 +    spin_unlock_irqrestore(&clu5Lock, flags);
 +}
index 657d8157d09b617fcdd72581bec98f145d242ce6..f12b0a910d429592f8e65fc544fdbdc2422c1293 100644 (file)
@@ -1,7 +1,7 @@
 diff -urN linux.old/drivers/block/Kconfig linux.dev/drivers/block/Kconfig
---- linux.old/drivers/block/Kconfig    2006-06-08 20:20:52.000000000 +0200
-+++ linux.dev/drivers/block/Kconfig    2006-06-08 22:14:58.000000000 +0200
-@@ -453,4 +453,12 @@
+--- linux.old/drivers/block/Kconfig    2006-10-26 02:43:39.000000000 +0200
++++ linux.dev/drivers/block/Kconfig    2006-10-26 00:11:14.000000000 +0200
+@@ -456,4 +456,12 @@
        This driver provides Support for ATA over Ethernet block
        devices like the Coraid EtherDrive (R) Storage Blade.
  
@@ -14,9 +14,18 @@ diff -urN linux.old/drivers/block/Kconfig linux.dev/drivers/block/Kconfig
 +        device driver for it.
 +
  endmenu
+diff -urN linux.old/drivers/block/Makefile linux.dev/drivers/block/Makefile
+--- linux.old/drivers/block/Makefile   2006-06-18 03:49:35.000000000 +0200
++++ linux.dev/drivers/block/Makefile   2006-10-26 02:44:10.000000000 +0200
+@@ -29,4 +29,5 @@
+ obj-$(CONFIG_VIODASD)         += viodasd.o
+ obj-$(CONFIG_BLK_DEV_SX8)     += sx8.o
+ obj-$(CONFIG_BLK_DEV_UB)      += ub.o
++obj-$(CONFIG_BLK_DEV_CF_MIPS) += rb500/
 diff -urN linux.old/drivers/block/rb500/ata.c linux.dev/drivers/block/rb500/ata.c
 --- linux.old/drivers/block/rb500/ata.c        1970-01-01 01:00:00.000000000 +0100
-+++ linux.dev/drivers/block/rb500/ata.c        2006-06-09 01:58:40.000000000 +0200
++++ linux.dev/drivers/block/rb500/ata.c        2006-10-26 00:11:14.000000000 +0200
 @@ -0,0 +1,474 @@
 +#include <linux/kernel.h>     /* printk() */
 +#include <linux/module.h>     /* module to be loadable */
@@ -494,7 +503,7 @@ diff -urN linux.old/drivers/block/rb500/ata.c linux.dev/drivers/block/rb500/ata.
 +/*eof*/
 diff -urN linux.old/drivers/block/rb500/ata.h linux.dev/drivers/block/rb500/ata.h
 --- linux.old/drivers/block/rb500/ata.h        1970-01-01 01:00:00.000000000 +0100
-+++ linux.dev/drivers/block/rb500/ata.h        2006-06-09 00:15:23.000000000 +0200
++++ linux.dev/drivers/block/rb500/ata.h        2006-10-26 00:11:14.000000000 +0200
 @@ -0,0 +1,132 @@
 +#ifndef __CFMIPS_ATA_H__
 +#define __CFMIPS_ATA_H__
@@ -630,7 +639,7 @@ diff -urN linux.old/drivers/block/rb500/ata.h linux.dev/drivers/block/rb500/ata.
 +#endif
 diff -urN linux.old/drivers/block/rb500/bdev.c linux.dev/drivers/block/rb500/bdev.c
 --- linux.old/drivers/block/rb500/bdev.c       1970-01-01 01:00:00.000000000 +0100
-+++ linux.dev/drivers/block/rb500/bdev.c       2006-06-15 16:29:04.000000000 +0200
++++ linux.dev/drivers/block/rb500/bdev.c       2006-10-26 00:11:14.000000000 +0200
 @@ -0,0 +1,340 @@
 +/* CF-mips driver
 +   This is a block driver for the direct (mmaped) interface to the CF-slot,
@@ -974,17 +983,8 @@ diff -urN linux.old/drivers/block/rb500/bdev.c linux.dev/drivers/block/rb500/bde
 +
 diff -urN linux.old/drivers/block/rb500/Makefile linux.dev/drivers/block/rb500/Makefile
 --- linux.old/drivers/block/rb500/Makefile     1970-01-01 01:00:00.000000000 +0100
-+++ linux.dev/drivers/block/rb500/Makefile     2006-06-08 22:14:58.000000000 +0200
++++ linux.dev/drivers/block/rb500/Makefile     2006-10-26 00:11:14.000000000 +0200
 @@ -0,0 +1,3 @@
 +## Makefile for the RB532 CF port
 +
 +obj-y         += bdev.o ata.o
-diff -urN linux.old/drivers/Makefile linux.dev/drivers/Makefile
---- linux.old/drivers/Makefile 2006-06-08 20:20:52.000000000 +0200
-+++ linux.dev/drivers/Makefile 2006-06-08 22:14:58.000000000 +0200
-@@ -73,3 +73,4 @@
- obj-y                         += firmware/
- obj-$(CONFIG_CRYPTO)          += crypto/
- obj-$(CONFIG_SUPERH)          += sh/
-+obj-$(CONFIG_BLK_DEV_CF_MIPS) += block/rb500/
-\ No newline at end of file
diff --git a/target/linux/rb532-2.6/patches/140-cmdline_hack.patch b/target/linux/rb532-2.6/patches/140-cmdline_hack.patch
new file mode 100644 (file)
index 0000000..55cf888
--- /dev/null
@@ -0,0 +1,33 @@
+diff -ur linux.old/arch/mips/kernel/head.S linux.dev/arch/mips/kernel/head.S
+--- linux.old/arch/mips/kernel/head.S  2006-10-26 00:11:13.000000000 +0200
++++ linux.dev/arch/mips/kernel/head.S  2006-10-26 02:40:10.000000000 +0200
+@@ -132,6 +132,8 @@
+       j kernel_entry
+       nop
++      nop
++EXPORT(_image_cmdline)
+       /*
+        * Reserved space for exception handlers.
+diff -ur linux.old/arch/mips/rb500/prom.c linux.dev/arch/mips/rb500/prom.c
+--- linux.old/arch/mips/rb500/prom.c   2006-10-26 00:11:14.000000000 +0200
++++ linux.dev/arch/mips/rb500/prom.c   2006-10-26 02:40:46.000000000 +0200
+@@ -128,6 +128,7 @@
+       /* FIXME: STUB */
+ }
++extern char _image_cmdline;
+ void __init prom_setup_cmdline(void){
+       char cmd_line[CL_SIZE];
+       char *cp;
+@@ -163,6 +164,9 @@
+               strcpy(cp,prom_argv[i]);
+               cp+=strlen(prom_argv[i]);
+       }
++      *(cp++) = ' ';
++      strcpy(cp,&_image_cmdline);
++      cp += strlen(&_image_cmdline);
+       
+       i=strlen(arcs_cmdline);
+       if (i>0){
diff --git a/target/linux/rb532-2.6/patches/500-Nand.patch b/target/linux/rb532-2.6/patches/500-Nand.patch
new file mode 100644 (file)
index 0000000..dc310e9
--- /dev/null
@@ -0,0 +1,248 @@
+diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
+index cfe288a..c528024 100644
+--- a/drivers/mtd/nand/Kconfig
++++ b/drivers/mtd/nand/Kconfig
+@@ -55,6 +55,12 @@ config MTD_NAND_TOTO
+       help
+         Support for NAND flash on Texas Instruments Toto platform.
++config MTD_NAND_RB500
++      tristate "NAND Flash device on RB500 board"
++      depends on MTD_NAND
++      help
++        Support for NAND flash on RB500 platform.
++
+ config MTD_NAND_IDS
+       tristate
+diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
+index 4174202..2be57c1 100644
+--- a/drivers/mtd/nand/Makefile
++++ b/drivers/mtd/nand/Makefile
+@@ -8,6 +8,7 @@ obj-$(CONFIG_MTD_NAND_IDS)             += nand_ids.
+ obj-$(CONFIG_MTD_NAND_SPIA)           += spia.o
+ obj-$(CONFIG_MTD_NAND_TOTO)           += toto.o
++obj-$(CONFIG_MTD_NAND_RB500)          += rbmipsnand.o
+ obj-$(CONFIG_MTD_NAND_AUTCPU12)               += autcpu12.o
+ obj-$(CONFIG_MTD_NAND_EDB7312)                += edb7312.o
+ obj-$(CONFIG_MTD_NAND_AU1550)         += au1550nd.o
+diff --git a/drivers/mtd/nand/rbmipsnand.c b/drivers/mtd/nand/rbmipsnand.c
+new file mode 100644
+index 0000000..6f7452a
+--- /dev/null
++++ b/drivers/mtd/nand/rbmipsnand.c
+@@ -0,0 +1,211 @@
++#include <linux/init.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++#include <linux/delay.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/bootinfo.h>
++//#include <asm/rb/rb100.h>
++
++#define IDT434_REG_BASE ((volatile void *) KSEG1ADDR(0x18000000))
++
++#define SMEM1(x) (*((volatile unsigned char *) (KSEG1ADDR(SMEM1_BASE) + x)))
++
++#define GPIOF 0x050000
++#define GPIOC 0x050004
++#define GPIOD 0x050008
++
++#define GPIO_RDY (1 << 0x08)
++#define GPIO_WPX (1 << 0x09)
++#define GPIO_ALE (1 << 0x0a)
++#define GPIO_CLE (1 << 0x0b)
++
++#define NAND_RW_REG   0x0     //data register
++#define NAND_SET_CEn  0x1     //CE# low
++#define NAND_CLR_CEn  0x2     //CE# high
++#define NAND_CLR_CLE  0x3     //CLE low
++#define NAND_SET_CLE  0x4     //CLE high
++#define NAND_CLR_ALE  0x5     //ALE low
++#define NAND_SET_ALE  0x6     //ALE high
++#define NAND_SET_SPn  0x7     //SP# low (use spare area)
++#define NAND_CLR_SPn  0x8     //SP# high (do not use spare area)
++#define NAND_SET_WPn  0x9     //WP# low
++#define NAND_CLR_WPn  0xA     //WP# high
++#define NAND_STS_REG  0xB     //Status register
++
++#define DEV2BASE 0x010020
++
++#define LO_WPX   (1 << 0)
++#define LO_ALE   (1 << 1)
++#define LO_CLE   (1 << 2)
++#define LO_CEX   (1 << 3)
++#define LO_FOFF  (1 << 5)
++#define LO_SPICS (1 << 6)
++#define LO_ULED  (1 << 7)
++
++#define MEM32(x) *((volatile unsigned *) (x))
++static void __iomem *p_nand;
++
++extern void changeLatchU5(unsigned char orMask, unsigned char nandMask);
++
++static int rb500_dev_ready(struct mtd_info *mtd) {
++    return MEM32(IDT434_REG_BASE + GPIOD) & GPIO_RDY;
++}
++/*
++static int rb100_dev_ready(struct mtd_info *mtd) {
++    return SMEM1(NAND_STS_REG) & 0x80;
++}
++*/
++static unsigned long iflags = 0;
++static int ioff = 0;
++/*
++static void rbmips_hwcontrol400(struct mtd_info *mtd, int cmd) {
++    switch (cmd) {
++    case NAND_CTL_SETCLE:
++      MEM32(IDT434_REG_BASE + GPIOD) |= GPIO_CLE;
++        break;
++    case NAND_CTL_CLRCLE:
++      MEM32(IDT434_REG_BASE + GPIOD) &= ~GPIO_CLE;
++        break;
++    case NAND_CTL_SETALE:
++      MEM32(IDT434_REG_BASE + GPIOD) |= GPIO_ALE;
++        break;
++    case NAND_CTL_CLRALE:
++      MEM32(IDT434_REG_BASE + GPIOD) &= ~GPIO_ALE;
++        break;
++    default:
++      break;
++    }
++}
++*/
++static void rbmips_hwcontrol500(struct mtd_info *mtd, int cmd) {
++    switch (cmd) {
++    case NAND_CTL_SETCLE:
++      changeLatchU5(LO_CLE, 0);
++        break;
++    case NAND_CTL_CLRCLE:
++      changeLatchU5(0, LO_CLE);
++        break;
++    case NAND_CTL_SETALE:
++      changeLatchU5(LO_ALE, 0);
++        break;
++    case NAND_CTL_CLRALE:
++      changeLatchU5(0, LO_ALE);
++        break;
++    default:
++        break;
++    }
++}
++/*
++static void rbmips_hwcontrol100(struct mtd_info *mtd, int cmd){
++    switch(cmd){
++    case NAND_CTL_SETCLE: 
++      SMEM1(NAND_SET_CLE) = 0x01; 
++      break;
++    case NAND_CTL_CLRCLE: 
++      SMEM1(NAND_CLR_CLE) = 0x01; 
++      break;
++    case NAND_CTL_SETALE: 
++      SMEM1(NAND_SET_ALE) = 0x01; 
++      break;
++    case NAND_CTL_CLRALE: 
++      SMEM1(NAND_CLR_ALE) = 0x01; 
++      break;
++    case NAND_CTL_SETNCE: 
++      SMEM1(NAND_SET_CEn) = 0x01; 
++      break;
++    case NAND_CTL_CLRNCE: 
++      SMEM1(NAND_CLR_CEn) = 0x01; 
++      break;
++    }
++}
++*/
++static struct mtd_partition partition_info[] = {
++    {
++        name: "RouterBoard NAND Boot",
++        offset: 0,
++      size: 4 * 1024 * 1024
++    },
++    {
++        name: "RouterBoard NAND Main",
++      offset: MTDPART_OFS_NXTBLK,
++      size: MTDPART_SIZ_FULL
++    }
++};
++
++static struct mtd_info rmtd;
++static struct nand_chip rnand;
++
++static unsigned init_ok = 0;
++
++unsigned get_rbnand_block_size(void) {
++      if (init_ok) return rmtd.oobblock; else return 0;
++}
++
++EXPORT_SYMBOL(get_rbnand_block_size);
++
++int __init rbmips_init(void) {
++      memset(&rmtd, 0, sizeof(rmtd));
++      memset(&rnand, 0, sizeof(rnand));
++/*
++      if (is_rb500()) {
++              if (is_rb400()) {
++                      printk("RB400 nand\n");
++                      MEM32(IDT434_REG_BASE + GPIOD) |= GPIO_WPX;
++                      MEM32(IDT434_REG_BASE + GPIOD) &= ~GPIO_CLE;
++                      MEM32(IDT434_REG_BASE + GPIOD) &= ~GPIO_ALE;
++                      rnand.hwcontrol = rbmips_hwcontrol400;
++              } else {
++*/            
++                      printk("RB500 nand\n");
++                      changeLatchU5(LO_WPX | LO_FOFF | LO_CEX,
++                                    LO_ULED | LO_ALE | LO_CLE);
++                      rnand.hwcontrol = rbmips_hwcontrol500;
++//            }
++    
++              rnand.dev_ready = rb500_dev_ready;
++              rnand.IO_ADDR_W = (unsigned char *)
++                      KSEG1ADDR(MEM32(IDT434_REG_BASE + DEV2BASE));
++              rnand.IO_ADDR_R = rnand.IO_ADDR_W;
++/*    } else if (is_rb100()) {
++              printk("RB100 nand\n");
++              MEM32(0xB2000064) = 0x100;
++              MEM32(0xB2000008) = 0x1;
++              SMEM1(NAND_SET_SPn) = 0x01; 
++              SMEM1(NAND_CLR_WPn) = 0x01; 
++              rnand.IO_ADDR_R = (unsigned char *)KSEG1ADDR(SMEM1_BASE);
++              rnand.IO_ADDR_W = rnand.IO_ADDR_R;
++              rnand.hwcontrol = rbmips_hwcontrol100;
++              rnand.dev_ready = rb100_dev_ready;
++      }
++*/
++      p_nand = (void __iomem *)ioremap(( void*)0x18a20000, 0x1000);
++      if (!p_nand) {
++              printk("RBnand Unable ioremap buffer");
++              return -ENXIO;
++      }
++      rnand.eccmode = NAND_ECC_SOFT;
++      rnand.chip_delay = 25;
++      rnand.options |= NAND_NO_AUTOINCR;
++      rmtd.priv = &rnand;
++
++      int *b = ( int *)KSEG1ADDR( 0x18010020);
++      printk( "dev2base 0x%08x mask 0x%08x c 0x%08x tc 0x%08x\n", b[ 0], b[ 1], b[ 2], b[ 3]);
++
++      if (nand_scan(&rmtd, 1) && nand_scan(&rmtd, 1)
++          && nand_scan(&rmtd, 1)  && nand_scan(&rmtd, 1)) {
++              printk("RBxxx nand device not found");
++              iounmap ((void *)p_nand);
++              return -ENXIO;
++      }
++
++      add_mtd_partitions(&rmtd, partition_info, 2);
++      init_ok = 1;
++      return 0;
++}
++
++module_init(rbmips_init);
++
++
++
++
++
diff --git a/target/linux/rb532-2.6/patches/510-Yaffs.patch b/target/linux/rb532-2.6/patches/510-Yaffs.patch
new file mode 100644 (file)
index 0000000..937b928
--- /dev/null
@@ -0,0 +1,11286 @@
+diff --git a/fs/Kconfig b/fs/Kconfig
+index f9b5842..556f12a 100644
+--- a/fs/Kconfig
++++ b/fs/Kconfig
+@@ -1022,6 +1022,15 @@ config EFS_FS
+         To compile the EFS file system support as a module, choose M here: the
+         module will be called efs.
++config YAFFS_FS
++      tristate "Yet Another Flash File System (YAFFS) support"
++      depends on MTD
++      help
++        JFFS is the Journaling Flash File System developed by Axis
++        Communications in Sweden, aimed at providing a crash/powerdown-safe
++        file system for disk-less embedded devices. Further information is
++        available at (<http://developer.axis.com/software/jffs/>).
++
+ config JFFS_FS
+       tristate "Journalling Flash File System (JFFS) support"
+       depends on MTD
+diff --git a/fs/Makefile b/fs/Makefile
+index 078d3d1..2062d2f 100644
+--- a/fs/Makefile
++++ b/fs/Makefile
+@@ -84,6 +85,7 @@ obj-$(CONFIG_UFS_FS)         += ufs/
+ obj-$(CONFIG_EFS_FS)          += efs/
+ obj-$(CONFIG_JFFS_FS)         += jffs/
+ obj-$(CONFIG_JFFS2_FS)                += jffs2/
++obj-$(CONFIG_YAFFS_FS)                += yaffs/
+ obj-$(CONFIG_AFFS_FS)         += affs/
+ obj-$(CONFIG_ROMFS_FS)                += romfs/
+ obj-$(CONFIG_QNX4FS_FS)               += qnx4/
+diff --git a/fs/yaffs/Makefile b/fs/yaffs/Makefile
+new file mode 100644
+index 0000000..615c2b2
+--- /dev/null
++++ b/fs/yaffs/Makefile
+@@ -0,0 +1,18 @@
++#
++# Makefile for the Linux msdos filesystem routines.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definitions are now in the main makefile.
++
++
++EXTRA_CFLAGS += -DCONFIG_YAFFS_YAFFS1 -DCONFIG_YAFFS_YAFFS2
++
++
++obj-$(CONFIG_YAFFS_FS) += yaffs.o
++
++yaffs-y = yaffs_fs.o yaffs_guts.o yaffs_mtdif.o yaffs_tagscompat.o \
++        yaffs_packedtags2.o yaffs_mtdif2.o yaffs_tagsvalidity.o \
++      yaffs_ecc.o
+diff --git a/fs/yaffs/devextras.h b/fs/yaffs/devextras.h
+new file mode 100644
+index 0000000..752c2cc
+--- /dev/null
++++ b/fs/yaffs/devextras.h
+@@ -0,0 +1,271 @@
++/*
++ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
++ * devextras.h
++ *
++ * Copyright (C) 2002 Aleph One Ltd.
++ *   for Toby Churchill Ltd and Brightstar Engineering
++ *
++ * Created by Charles Manning <charles@aleph1.co.uk>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU Lesser General Public License version 2.1 as
++ * published by the Free Software Foundation.
++ *
++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
++ *
++ * This file is just holds extra declarations used during development.
++ * Most of these are from kernel includes placed here so we can use them in 
++ * applications.
++ *
++ * $Id: devextras.h,v 1.1 2004/11/03 08:14:07 charles Exp $
++ *
++ */
++ 
++#ifndef __EXTRAS_H__
++#define __EXTRAS_H__
++
++#if defined WIN32
++#define __inline__ __inline
++#define new newHack
++#endif
++
++#if !(defined __KERNEL__) || (defined WIN32)
++
++// User space defines
++
++typedef unsigned char   __u8;
++typedef unsigned short  __u16;
++typedef unsigned        __u32;
++
++
++/*
++ * Simple doubly linked list implementation.
++ *
++ * Some of the internal functions ("__xxx") are useful when
++ * manipulating whole lists rather than single entries, as
++ * sometimes we already know the next/prev entries and we can
++ * generate better code by using them directly rather than
++ * using the generic single-entry routines.
++ */
++ 
++ #define prefetch(x) 1
++ 
++
++struct list_head {
++      struct list_head *next, *prev;
++};
++
++#define LIST_HEAD_INIT(name) { &(name), &(name) }
++
++#define LIST_HEAD(name) \
++      struct list_head name = LIST_HEAD_INIT(name)
++
++#define INIT_LIST_HEAD(ptr) do { \
++      (ptr)->next = (ptr); (ptr)->prev = (ptr); \
++} while (0)
++
++/*
++ * Insert a new entry between two known consecutive entries.
++ *
++ * This is only for internal list manipulation where we know
++ * the prev/next entries already!
++ */
++static __inline__ void __list_add(struct list_head * new,
++      struct list_head * prev,
++      struct list_head * next)
++{
++      next->prev = new;
++      new->next = next;
++      new->prev = prev;
++      prev->next = new;
++}
++
++/**
++ * list_add - add a new entry
++ * @new: new entry to be added
++ * @head: list head to add it after
++ *
++ * Insert a new entry after the specified head.
++ * This is good for implementing stacks.
++ */
++static __inline__ void list_add(struct list_head *new, struct list_head *head)
++{
++      __list_add(new, head, head->next);
++}
++
++/**
++ * list_add_tail - add a new entry
++ * @new: new entry to be added
++ * @head: list head to add it before
++ *
++ * Insert a new entry before the specified head.
++ * This is useful for implementing queues.
++ */
++static __inline__ void list_add_tail(struct list_head *new, struct list_head *head)
++{
++      __list_add(new, head->prev, head);
++}
++
++/*
++ * Delete a list entry by making the prev/next entries
++ * point to each other.
++ *
++ * This is only for internal list manipulation where we know
++ * the prev/next entries already!
++ */
++static __inline__ void __list_del(struct list_head * prev,
++                                struct list_head * next)
++{
++      next->prev = prev;
++      prev->next = next;
++}
++
++/**
++ * list_del - deletes entry from list.
++ * @entry: the element to delete from the list.
++ * Note: list_empty on entry does not return true after this, the entry is in an undefined state.
++ */
++static __inline__ void list_del(struct list_head *entry)
++{
++      __list_del(entry->prev, entry->next);
++}
++
++/**
++ * list_del_init - deletes entry from list and reinitialize it.
++ * @entry: the element to delete from the list.
++ */
++static __inline__ void list_del_init(struct list_head *entry)
++{
++      __list_del(entry->prev, entry->next);
++      INIT_LIST_HEAD(entry);
++}
++
++/**
++ * list_empty - tests whether a list is empty
++ * @head: the list to test.
++ */
++static __inline__ int list_empty(struct list_head *head)
++{
++      return head->next == head;
++}
++
++/**
++ * list_splice - join two lists
++ * @list: the new list to add.
++ * @head: the place to add it in the first list.
++ */
++static __inline__ void list_splice(struct list_head *list, struct list_head *head)
++{
++      struct list_head *first = list->next;
++
++      if (first != list) {
++              struct list_head *last = list->prev;
++              struct list_head *at = head->next;
++
++              first->prev = head;
++              head->next = first;
++
++              last->next = at;
++              at->prev = last;
++      }
++}
++
++/**
++ * list_entry - get the struct for this entry
++ * @ptr:      the &struct list_head pointer.
++ * @type:     the type of the struct this is embedded in.
++ * @member:   the name of the list_struct within the struct.
++ */
++#define list_entry(ptr, type, member) \
++      ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
++
++/**
++ * list_for_each      -       iterate over a list
++ * @pos:      the &struct list_head to use as a loop counter.
++ * @head:     the head for your list.
++ */
++#define list_for_each(pos, head) \
++      for (pos = (head)->next, prefetch(pos->next); pos != (head); \
++              pos = pos->next, prefetch(pos->next))
++
++/**
++ * list_for_each_safe -       iterate over a list safe against removal of list entry
++ * @pos:      the &struct list_head to use as a loop counter.
++ * @n:                another &struct list_head to use as temporary storage
++ * @head:     the head for your list.
++ */
++#define list_for_each_safe(pos, n, head) \
++      for (pos = (head)->next, n = pos->next; pos != (head); \
++              pos = n, n = pos->next)
++
++
++
++
++/*
++ * File types
++ */
++#define DT_UNKNOWN    0
++#define DT_FIFO               1
++#define DT_CHR                2
++#define DT_DIR                4
++#define DT_BLK                6
++#define DT_REG                8
++#define DT_LNK                10
++#define DT_SOCK               12
++#define DT_WHT                14
++
++#ifndef WIN32
++#include <sys/stat.h>
++#endif
++
++/*
++ * Attribute flags.  These should be or-ed together to figure out what
++ * has been changed!
++ */
++#define ATTR_MODE     1
++#define ATTR_UID      2
++#define ATTR_GID      4
++#define ATTR_SIZE     8
++#define ATTR_ATIME    16
++#define ATTR_MTIME    32
++#define ATTR_CTIME    64
++#define ATTR_ATIME_SET        128
++#define ATTR_MTIME_SET        256
++#define ATTR_FORCE    512     /* Not a change, but a change it */
++#define ATTR_ATTR_FLAG        1024
++
++
++struct iattr {
++      unsigned int    ia_valid;
++      unsigned                ia_mode;
++      unsigned                ia_uid;
++      unsigned                ia_gid;
++      unsigned                ia_size;
++      unsigned                ia_atime;
++      unsigned        ia_mtime;
++      unsigned        ia_ctime;
++      unsigned int    ia_attr_flags;
++};
++
++#define KERN_DEBUG
++
++
++#else
++
++#ifndef WIN32
++#include <linux/types.h>
++#include <linux/list.h>
++#include <linux/fs.h>
++#include <linux/stat.h>
++#endif
++
++#endif
++
++
++
++#if defined WIN32
++#undef new
++#endif 
++
++#endif
++
+diff --git a/fs/yaffs/yaffs_ecc.c b/fs/yaffs/yaffs_ecc.c
+new file mode 100644
+index 0000000..166bcad
+--- /dev/null
++++ b/fs/yaffs/yaffs_ecc.c
+@@ -0,0 +1,287 @@
++/*
++ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
++ *
++ * yaffs_ecc.c: ECC generation/correction algorithms.
++ *
++ * Copyright (C) 2002 Aleph One Ltd.
++ *
++ * Created by Charles Manning <charles@aleph1.co.uk>
++ *
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public License
++ * version 2.1 as published by the Free Software Foundation.
++ */
++ 
++ /*
++ * This code implements the ECC algorithm used in SmartMedia.
++ *
++ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. 
++ * The two unused bit are set to 1.
++ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two such ECC 
++ * blocks are used on a 512-byte NAND page.
++ *
++ */
++
++// Table generated by gen-ecc.c
++// Using a table means we do not have to calculate p1..p4 and p1'..p4'
++// for each byte of data. These are instead provided in a table in bits7..2.
++// Bit 0 of each entry indicates whether the entry has an odd or even parity, and therefore
++// this bytes influence on the line parity.
++
++const char *yaffs_ecc_c_version = "$Id: yaffs_ecc.c,v 1.4 2005/07/31 00:28:04 charles Exp $";
++
++#include "yportenv.h"
++
++#include "yaffs_ecc.h"
++
++static const unsigned char column_parity_table[] = {
++0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, 
++0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, 
++0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, 
++0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, 
++0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, 
++0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, 
++0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, 
++0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, 
++0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, 
++0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, 
++0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, 
++0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, 
++0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, 
++0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, 
++0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, 
++0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, 
++};
++
++
++static int yaffs_CountBits(unsigned char x)
++{
++      int r = 0;
++      while(x)
++      {
++              if(x & 1) r++;
++              x >>= 1;
++      }
++      return r;
++}
++
++static int yaffs_CountBits32(unsigned  x)
++{
++      int r = 0;
++      while(x)
++      {
++              if(x & 1) r++;
++              x >>= 1;
++      }
++      return r;
++}
++
++
++void yaffs_ECCCalculate(const unsigned char *data,unsigned char *ecc)
++{
++      unsigned int i;
++      
++      unsigned char col_parity = 0;
++      unsigned char line_parity = 0;
++      unsigned char line_parity_prime = 0;
++      unsigned char t;
++      unsigned char b;
++      
++      for(i = 0; i < 256; i++)
++      {
++              b = column_parity_table[*data++];
++              col_parity ^= b;
++
++              if(b & 0x01) // odd number of bits in the byte
++              {
++                      line_parity ^= i;
++                      line_parity_prime ^= ~i;
++              }
++              
++      }
++      
++      ecc[2] = (~col_parity) | 0x03;
++      
++      t = 0;
++      if(line_parity       & 0x80) t |= 0x80;
++      if(line_parity_prime & 0x80) t |= 0x40;
++      if(line_parity       & 0x40) t |= 0x20;
++      if(line_parity_prime & 0x40) t |= 0x10;
++      if(line_parity       & 0x20) t |= 0x08;
++      if(line_parity_prime & 0x20) t |= 0x04;
++      if(line_parity       & 0x10) t |= 0x02;
++      if(line_parity_prime & 0x10) t |= 0x01;
++      ecc[1] = ~t;
++      
++      t = 0;
++      if(line_parity       & 0x08) t |= 0x80;
++      if(line_parity_prime & 0x08) t |= 0x40;
++      if(line_parity       & 0x04) t |= 0x20;
++      if(line_parity_prime & 0x04) t |= 0x10;
++      if(line_parity       & 0x02) t |= 0x08;
++      if(line_parity_prime & 0x02) t |= 0x04;
++      if(line_parity       & 0x01) t |= 0x02;
++      if(line_parity_prime & 0x01) t |= 0x01;
++      ecc[0] = ~t;
++
++#ifdef CONFIG_YAFFS_ECC_WRONG_ORDER
++      // Swap the bytes into the wrong order
++      t = ecc[0];
++      ecc[0] = ecc[1];
++      ecc[1] = t;
++#endif 
++}
++
++int yaffs_ECCCorrect(unsigned char *data, unsigned char *read_ecc, const unsigned char *test_ecc)
++{
++      unsigned char d0, d1, d2; // deltas 
++
++      d0 = read_ecc[0] ^ test_ecc[0];
++      d1 = read_ecc[1] ^ test_ecc[1];
++      d2 = read_ecc[2] ^ test_ecc[2];
++      
++      
++      
++      if((d0 | d1 | d2) == 0)
++      {
++              // no error
++              return 0;
++      }
++      
++      if( ((d0 ^ (d0 >> 1)) & 0x55) == 0x55 &&
++          ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 &&
++          ((d2 ^ (d2 >> 1)) & 0x54) == 0x54)
++      {
++              // Single bit (recoverable) error in data
++
++              unsigned byte;
++              unsigned bit;
++
++#ifdef CONFIG_YAFFS_ECC_WRONG_ORDER
++              // swap the bytes to correct for the wrong order
++              unsigned char t;
++              
++              t = d0;
++              d0 = d1;
++              d1 = t;
++#endif
++              
++              bit = byte = 0;
++              
++              
++              if(d1 & 0x80) byte |= 0x80;
++              if(d1 & 0x20) byte |= 0x40;
++              if(d1 & 0x08) byte |= 0x20;
++              if(d1 & 0x02) byte |= 0x10;
++              if(d0 & 0x80) byte |= 0x08;
++              if(d0 & 0x20) byte |= 0x04;
++              if(d0 & 0x08) byte |= 0x02;
++              if(d0 & 0x02) byte |= 0x01;
++
++              if(d2 & 0x80) bit |= 0x04;
++              if(d2 & 0x20) bit |= 0x02;
++              if(d2 & 0x08) bit |= 0x01;
++
++              data[byte] ^= (1 << bit);
++              
++              return 1;
++      }
++      
++      if((yaffs_CountBits(d0)+yaffs_CountBits(d1)+yaffs_CountBits(d2)) == 1)
++      {
++              // Reccoverable error in ecc
++              
++              read_ecc[0] = test_ecc[0];
++              read_ecc[1] = test_ecc[1];
++              read_ecc[2] = test_ecc[2];
++              
++              return 1;
++      }
++      
++      // Unrecoverable error
++      
++      return -1;
++          
++
++}
++
++
++
++void yaffs_ECCCalculateOther(const unsigned char *data,unsigned nBytes, yaffs_ECCOther *eccOther)
++{
++      unsigned int i;
++      
++      unsigned char col_parity = 0;
++      unsigned line_parity = 0;
++      unsigned line_parity_prime = 0;
++      unsigned char b;
++      
++      for(i = 0; i < nBytes; i++)
++      {
++              b = column_parity_table[*data++];
++              col_parity ^= b;
++
++              if(b & 0x01) // odd number of bits in the byte
++              {
++                      line_parity ^= i;
++                      line_parity_prime ^= ~i;
++              }
++              
++      }
++      
++      eccOther->colParity = (col_parity >> 2) & 0x3f;
++      eccOther->lineParity = line_parity;
++      eccOther->lineParityPrime = line_parity_prime;
++}
++
++int yaffs_ECCCorrectOther(unsigned char *data, unsigned nBytes, yaffs_ECCOther *read_ecc, const yaffs_ECCOther  *test_ecc)
++{
++      unsigned char cDelta; // column parity delta
++      unsigned lDelta; // line parity delta
++      unsigned lDeltaPrime; // line parity delta
++      unsigned bit;
++
++      cDelta = read_ecc->colParity ^ test_ecc->colParity;
++      lDelta = read_ecc->lineParity ^ test_ecc->lineParity;   
++      lDeltaPrime = read_ecc->lineParityPrime ^ test_ecc->lineParityPrime;
++      
++      if((cDelta | lDelta | lDeltaPrime) == 0)
++      {
++              // no error
++              return 0;
++      }
++      
++      if( lDelta == ~lDeltaPrime &&
++           (((cDelta ^ (cDelta >> 1)) & 0x15) == 0x15)) // Not correct 
++      {
++              // Single bit (recoverable) error in data
++
++              bit = 0;
++
++              if(cDelta & 0x20) bit |= 0x04;
++              if(cDelta & 0x08) bit |= 0x02;
++              if(cDelta & 0x02) bit |= 0x01;
++
++              data[lDelta] ^= (1 << bit);
++              
++              return 1;
++      }
++      
++      if((yaffs_CountBits32(lDelta) + yaffs_CountBits32(lDeltaPrime) + yaffs_CountBits(cDelta)) == 1)
++      {
++              // Reccoverable error in ecc
++              
++              *read_ecc  = *test_ecc;         
++              return 1;
++      }
++      
++      // Unrecoverable error
++      
++      return -1;
++          
++
++}
++
++
++
+diff --git a/fs/yaffs/yaffs_ecc.h b/fs/yaffs/yaffs_ecc.h
+new file mode 100644
+index 0000000..f96d707
+--- /dev/null
++++ b/fs/yaffs/yaffs_ecc.h
+@@ -0,0 +1,41 @@
++/*
++ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
++ *
++ * yaffs_ecc.c: ECC generation/correction algorithms.
++ *
++ * Copyright (C) 2002 Aleph One Ltd.
++ *
++ * Created by Charles Manning <charles@aleph1.co.uk>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ */
++ 
++ /*
++ * This code implements the ECC algorithm used in SmartMedia.
++ *
++ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. 
++ * The two unused bit are set to 1.
++ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two such ECC 
++ * blocks are used on a 512-byte NAND page.
++ *
++ */
++
++#ifndef __YAFFS_ECC_H__
++#define __YAFFS_ECC_H__
++
++typedef struct
++{
++      unsigned char colParity;
++      unsigned lineParity;
++      unsigned lineParityPrime;
++} yaffs_ECCOther;
++
++void yaffs_ECCCalculate(const unsigned char *data,unsigned char *ecc);
++int yaffs_ECCCorrect(unsigned char *data, unsigned char *read_ecc, const unsigned char *test_ecc);
++
++void yaffs_ECCCalculateOther(const unsigned char *data,unsigned nBytes, yaffs_ECCOther *ecc);
++int yaffs_ECCCorrectOther(unsigned char *data, unsigned nBytes, yaffs_ECCOther *read_ecc, const yaffs_ECCOther *test_ecc);
++#endif
+diff --git a/fs/yaffs/yaffs_fs.c b/fs/yaffs/yaffs_fs.c
+new file mode 100644
+index 0000000..717f41a
+--- /dev/null
++++ b/fs/yaffs/yaffs_fs.c
+@@ -0,0 +1,1727 @@
++/*
++ * YAFFS: Yet another FFS. A NAND-flash specific file system.
++ * yaffs_fs.c
++ *
++ * Copyright (C) 2002 Aleph One Ltd.
++ *   for Toby Churchill Ltd and Brightstar Engineering
++ *
++ * Created by Charles Manning <charles@aleph1.co.uk>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This is the file system front-end to YAFFS that hooks it up to
++ * the VFS.
++ *
++ * Special notes: 
++ * >> 2.4: sb->u.generic_sbp points to the yaffs_Device associated with this superblock
++ * >> 2.6: sb->s_fs_info  points to the yaffs_Device associated with this superblock
++ * >> inode->u.generic_ip points to the associated yaffs_Object.
++ *
++ *
++ * Acknowledgements:
++ * * Luc van OostenRyck for numerous patches.
++ * * Nick Bane for numerous patches.
++ * * Nick Bane for 2.5/2.6 integration.
++ * * Andras Toth for mknod rdev issue.
++ * * Michael Fischer for finding the problem with inode inconsistency.
++ * * Some code bodily lifted from JFFS2.
++ */
++
++
++const char *yaffs_fs_c_version = "$Id: yaffs_fs.c,v 1.27 2005/08/04 22:47:36 luc Exp $";
++extern const char *yaffs_guts_c_version;
++
++
++#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/proc_fs.h>
++#include <linux/smp_lock.h>
++#include <linux/pagemap.h>
++#include <linux/mtd/mtd.h>
++#include <linux/interrupt.h>
++#include <linux/string.h>
++
++
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
++
++#include <linux/statfs.h>     /* Added NCB 15-8-2003 */
++#include <asm/statfs.h>
++#define UnlockPage(p) unlock_page(p)
++#define Page_Uptodate(page)   test_bit(PG_uptodate, &(page)->flags)
++#define yaffs_devname(sb, buf)        bdevname(sb->s_bdev, buf)       // FIXME: use sb->s_id instead ?
++
++#else
++
++#include <linux/locks.h>
++#define       BDEVNAME_SIZE           0
++#define       yaffs_devname(sb, buf)  kdevname(sb->s_dev)
++
++#endif
++
++#include <asm/uaccess.h>
++
++#include "yportenv.h"
++#include "yaffs_guts.h"
++
++
++
++
++unsigned yaffs_traceMask = YAFFS_TRACE_ALWAYS | YAFFS_TRACE_BAD_BLOCKS;
++//unsigned yaffs_traceMask = 0xFFFFFFFF;
++
++
++#ifdef CONFIG_YAFFS_YAFFS1
++#include <linux/mtd/mtd.h>
++#include "yaffs_mtdif.h"
++#include "yaffs_mtdif2.h"
++#endif //CONFIG_YAFFS_YAFFS1
++
++//#define T(x) printk x
++
++
++
++#define yaffs_InodeToObject(iptr) ((yaffs_Object *)((iptr)->u.generic_ip))
++#define yaffs_DentryToObject(dptr) yaffs_InodeToObject((dptr)->d_inode)
++
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
++#define yaffs_SuperToDevice(sb)       ((yaffs_Device *)sb->s_fs_info)
++#else
++#define yaffs_SuperToDevice(sb)       ((yaffs_Device *)sb->u.generic_sbp)
++#endif
++
++
++static void yaffs_put_super(struct super_block *sb);
++
++static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, loff_t *pos);
++
++static int yaffs_file_flush(struct file* file);
++
++static int yaffs_sync_object(struct file * file, struct dentry *dentry, int datasync);
++
++static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir);
++
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
++static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *n);
++static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *n);
++#else
++static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode);
++static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry);
++#endif
++static int yaffs_link(struct dentry *old_dentry, struct inode * dir, struct dentry * dentry);
++static int yaffs_unlink(struct inode * dir, struct dentry *dentry);
++static int yaffs_symlink(struct inode * dir, struct dentry *dentry, const char * symname);
++static int yaffs_mkdir(struct inode * dir, struct dentry * dentry, int mode);
++
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev);
++#else
++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev);
++#endif
++static int yaffs_rename(struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir,struct dentry *new_dentry);
++static int yaffs_setattr(struct dentry *dentry, struct iattr *attr);
++
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
++static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf);
++#else
++static int yaffs_statfs(struct super_block *sb, struct statfs *buf);
++#endif
++static void yaffs_read_inode (struct inode *inode);
++
++static void yaffs_put_inode (struct inode *inode);
++static void yaffs_delete_inode(struct inode *);
++static void yaffs_clear_inode(struct inode *);
++
++static int yaffs_readpage(struct file *file, struct page * page);
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
++static int yaffs_writepage(struct page *page, struct writeback_control *wbc);
++#else
++static int yaffs_writepage(struct page *page);
++#endif
++static int yaffs_prepare_write(struct file *f, struct page *pg, unsigned offset, unsigned to);
++static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, unsigned to);
++
++static int yaffs_readlink(struct dentry *dentry, char __user *buffer, int buflen);
++static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd);
++
++
++
++static struct address_space_operations yaffs_file_address_operations = {
++      .readpage       = yaffs_readpage,
++      .writepage      = yaffs_writepage,
++      .prepare_write  = yaffs_prepare_write,
++      .commit_write   = yaffs_commit_write,
++};
++
++static struct file_operations yaffs_file_operations = {
++      .read           = generic_file_read,
++      .write          = generic_file_write,
++      .mmap           = generic_file_mmap,
++      .flush          = yaffs_file_flush,
++      .fsync          = yaffs_sync_object,
++};
++
++static struct inode_operations yaffs_file_inode_operations = {
++      .setattr        = yaffs_setattr,
++};
++
++static struct inode_operations yaffs_symlink_inode_operations = {     
++      .readlink       = yaffs_readlink,
++      .follow_link    = yaffs_follow_link,
++      .setattr        = yaffs_setattr,
++};
++
++static struct inode_operations yaffs_dir_inode_operations = {
++      .create         = yaffs_create,
++      .lookup         = yaffs_lookup,
++      .link           = yaffs_link,
++      .unlink         = yaffs_unlink, 
++      .symlink        = yaffs_symlink,
++      .mkdir          = yaffs_mkdir,
++      .rmdir          = yaffs_unlink,
++      .mknod          = yaffs_mknod,
++      .rename         = yaffs_rename,
++      .setattr        = yaffs_setattr,
++};
++
++static struct file_operations yaffs_dir_operations = {
++      .read           = generic_read_dir,
++      .readdir        = yaffs_readdir,
++      .fsync          = yaffs_sync_object,
++};
++
++static struct super_operations yaffs_super_ops = {
++      .statfs         = yaffs_statfs,
++      .read_inode     = yaffs_read_inode,
++      .put_inode      = yaffs_put_inode,
++      .put_super      = yaffs_put_super,
++      .delete_inode   = yaffs_delete_inode,
++      .clear_inode    = yaffs_clear_inode,
++};
++
++
++
++static void yaffs_GrossLock(yaffs_Device *dev)
++{
++      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs locking\n"));
++
++      down(&dev->grossLock);
++}
++
++static void yaffs_GrossUnlock(yaffs_Device *dev)
++{
++      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs unlocking\n"));
++      up(&dev->grossLock);
++
++}
++
++static int yaffs_readlink(struct dentry *dentry, char __user *buffer, int buflen)
++{
++      unsigned char *alias;
++      int ret;
++
++      yaffs_Device *dev = yaffs_DentryToObject(dentry)->myDev;
++
++
++      yaffs_GrossLock(dev);
++      
++      alias = yaffs_GetSymlinkAlias(yaffs_DentryToObject(dentry));
++      
++      yaffs_GrossUnlock(dev);
++      
++      if(!alias)
++              return -ENOMEM;
++
++      ret = vfs_readlink(dentry, buffer, buflen, alias);
++      kfree(alias);
++      return ret;
++}
++
++static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd)
++{
++      unsigned char *alias;
++      int ret;
++      yaffs_Device *dev = yaffs_DentryToObject(dentry)->myDev;
++
++
++      yaffs_GrossLock(dev);
++
++      alias = yaffs_GetSymlinkAlias(yaffs_DentryToObject(dentry));
++      
++      yaffs_GrossUnlock(dev);
++      
++      if(!alias)
++              ret = -ENOMEM;
++      else {
++              ret = vfs_follow_link(nd,alias);
++              kfree(alias);
++      }
++      return ERR_PTR (ret);
++}
++
++
++struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev,yaffs_Object *obj);
++
++/*
++ * Lookup is used to find objects in the fs
++ */
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
++
++static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *n)
++#else
++static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry)
++#endif
++{
++      yaffs_Object *obj;
++      struct inode *inode = NULL; // NCB 2.5/2.6 needs NULL here
++      
++      yaffs_Device *dev = yaffs_InodeToObject(dir)->myDev;
++
++
++      yaffs_GrossLock(dev);
++
++      
++      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_lookup for %d:%s\n",yaffs_InodeToObject(dir)->objectId,dentry->d_name.name));
++      
++      obj = yaffs_FindObjectByName(yaffs_InodeToObject(dir),dentry->d_name.name);
++      
++      obj = yaffs_GetEquivalentObject(obj); // in case it was a hardlink
++      
++
++      
++      if(obj)
++      {
++              T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_lookup found %d\n",obj->objectId));
++              
++              inode = yaffs_get_inode(dir->i_sb, obj->yst_mode,0,obj);
++              
++              if(inode)
++              {
++                      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_loookup dentry \n"));
++/* #if 0 asserted by NCB for 2.5/6 compatability - falls through to d_add even if NULL inode */       
++#if 0
++                      //dget(dentry); // try to solve directory bug
++                      d_add(dentry,inode);
++                      
++                      yaffs_GrossUnlock(dev);
++
++                      // return dentry;
++                      return NULL;
++#endif
++              }
++
++      }
++      else
++      {
++              T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_lookup not found\n"));
++              
++      }
++      yaffs_GrossUnlock(dev);
++
++/* added NCB for 2.5/6 compatability - forces add even if inode is NULL which creates dentry hash*/   
++      d_add(dentry,inode);
++      
++      return NULL;
++      //      return (ERR_PTR(-EIO));
++      
++}
++
++// For now put inode is just for debugging
++// Put inode is called when the inode **structure** is put.
++static void yaffs_put_inode(struct inode *inode)
++{
++      T(YAFFS_TRACE_OS,("yaffs_put_inode: ino %d, count %d\n",(int)inode->i_ino, atomic_read(&inode->i_count)));
++      
++}
++
++// clear is called to tell the fs to release any per-inode data it holds
++static void yaffs_clear_inode(struct inode *inode)
++{
++      yaffs_Object *obj;
++      yaffs_Device *dev;
++      
++      obj = yaffs_InodeToObject(inode);
++      
++      T(YAFFS_TRACE_OS,("yaffs_clear_inode: ino %d, count %d %s\n",(int)inode->i_ino, atomic_read(&inode->i_count),
++              obj ? "object exists" : "null object"));        
++
++      if(obj)
++      {
++              dev = obj->myDev;
++              yaffs_GrossLock(dev);
++              
++              // Clear the association between the inode ant the yaffs_Object.
++              obj->myInode = NULL;
++              inode->u.generic_ip = NULL;
++              
++              // If the object freeing was deferred, then the real free happens now.
++              // This should fix the inode inconsistency problem.
++              
++              yaffs_HandleDeferedFree(obj);
++              
++              yaffs_GrossUnlock(dev);
++      }
++      
++      
++}
++
++// delete is called when the link count is zero and the inode
++// is put (ie. nobody wants to know about it anymore, time to
++// delete the file).
++// NB Must call clear_inode()
++static void yaffs_delete_inode(struct inode *inode)
++{
++      yaffs_Object *obj = yaffs_InodeToObject(inode);
++      yaffs_Device *dev;
++
++      T(YAFFS_TRACE_OS,("yaffs_delete_inode: ino %d, count %d %s\n",(int)inode->i_ino, atomic_read(&inode->i_count),
++              obj ? "object exists" : "null object"));
++      
++      if(obj)
++      {
++              dev = obj->myDev;
++              yaffs_GrossLock(dev);
++              yaffs_DeleteFile(obj);
++              yaffs_GrossUnlock(dev);
++      }
++      clear_inode(inode);
++}
++
++
++static int yaffs_file_flush(struct file* file)
++{
++      yaffs_Object *obj = yaffs_DentryToObject(file->f_dentry);
++      
++      yaffs_Device *dev = obj->myDev;
++      
++      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_file_flush object %d (%s)\n",obj->objectId,
++                              obj->dirty ? "dirty" : "clean"));
++
++      yaffs_GrossLock(dev);
++      
++    yaffs_FlushFile(obj,1);
++
++      yaffs_GrossUnlock(dev);
++
++    return 0;
++}
++
++
++
++static int yaffs_readpage_nolock(struct file *f, struct page * pg)
++{
++      // Lifted from jffs2
++      
++      yaffs_Object *obj;
++      unsigned char *pg_buf;
++      int ret;
++
++      yaffs_Device *dev;
++
++      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_readpage at %08x, size %08x\n",
++                    (unsigned)(pg->index << PAGE_CACHE_SHIFT), (unsigned)PAGE_CACHE_SIZE));
++
++      obj  = yaffs_DentryToObject(f->f_dentry);
++
++      dev = obj->myDev;
++      
++      
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
++        BUG_ON(!PageLocked(pg));
++#else
++      if (!PageLocked(pg))
++                PAGE_BUG(pg);
++#endif
++
++      pg_buf = kmap(pg);
++      /* FIXME: Can kmap fail? */
++
++      yaffs_GrossLock(dev);
++      
++      ret = yaffs_ReadDataFromFile(obj, pg_buf, pg->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE);
++
++      yaffs_GrossUnlock(dev);
++      
++      if(ret >= 0) ret = 0;
++
++      if (ret) {
++              ClearPageUptodate(pg);
++              SetPageError(pg);
++      } else {
++              SetPageUptodate(pg);
++              ClearPageError(pg);
++      }
++
++      flush_dcache_page(pg);
++      kunmap(pg);
++
++
++      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_readpage done\n"));
++      return ret;
++}
++
++static int yaffs_readpage_unlock(struct file *f, struct page *pg)
++{
++      int ret = yaffs_readpage_nolock(f,pg);
++      UnlockPage(pg);
++      return ret;
++}
++
++static int yaffs_readpage(struct file *f, struct page * pg)
++{
++      return yaffs_readpage_unlock(f,pg);
++}
++
++// writepage inspired by/stolen from smbfs
++//
++
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
++static int yaffs_writepage(struct page *page, struct writeback_control *wbc)
++#else
++static int yaffs_writepage(struct page *page)
++#endif
++{
++      struct address_space *mapping = page->mapping;
++      loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
++      struct inode *inode;
++      unsigned long end_index;
++      char *buffer;
++      yaffs_Object *obj;
++      int nWritten = 0;
++      unsigned nBytes;
++
++      if (!mapping)
++              BUG();
++      inode = mapping->host;
++      if (!inode)
++              BUG();
++
++      if (offset > inode->i_size)
++      {
++              T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_writepage at %08x, inode size = %08x!!!\n", (unsigned)(page->index << PAGE_CACHE_SHIFT), (unsigned) inode->i_size));
++              T(YAFFS_TRACE_OS,(KERN_DEBUG"                -> don't care!!\n"));
++              unlock_page(page);
++              return 0;
++      }
++
++      end_index = inode->i_size >> PAGE_CACHE_SHIFT;
++
++      /* easy case */
++      if (page->index < end_index)
++      {
++              nBytes = PAGE_CACHE_SIZE;
++      }
++      else
++      {
++              nBytes = inode->i_size & (PAGE_CACHE_SIZE-1);
++      }
++      //  What's happening here?
++      ///* OK, are we completely out? */
++      //if (page->index >= end_index+1 || !offset)
++      //      return -EIO;
++
++      get_page(page);
++
++
++      buffer = kmap(page);
++
++      obj = yaffs_InodeToObject(inode);
++      yaffs_GrossLock(obj->myDev);
++
++      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_writepage at %08x, size %08x\n", (unsigned)(page->index << PAGE_CACHE_SHIFT), nBytes));
++      T(YAFFS_TRACE_OS,(KERN_DEBUG"writepag0: obj = %05x, ino = %05x\n", (int) obj->variant.fileVariant.fileSize, (int) inode->i_size));
++
++      nWritten = yaffs_WriteDataToFile(obj,buffer,page->index << PAGE_CACHE_SHIFT,nBytes,0);
++
++      T(YAFFS_TRACE_OS,(KERN_DEBUG"writepag1: obj = %05x, ino = %05x\n", (int) obj->variant.fileVariant.fileSize, (int) inode->i_size));
++
++      yaffs_GrossUnlock(obj->myDev);
++      
++      kunmap(page);
++      SetPageUptodate(page);
++      UnlockPage(page);
++      put_page(page);
++
++      return (nWritten == nBytes) ? 0  : -ENOSPC;
++}
++
++
++
++static int yaffs_prepare_write(struct file *f, struct page *pg, unsigned offset, unsigned to)
++{
++
++      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_prepair_write\n"));
++      if(!Page_Uptodate(pg) && (offset || to < PAGE_CACHE_SIZE))
++              return  yaffs_readpage_nolock(f,pg);    
++
++      return 0;
++      
++}
++
++static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, unsigned to)
++{
++
++      void *addr = page_address(pg) + offset;
++      loff_t pos = (((loff_t)pg->index) << PAGE_CACHE_SHIFT) + offset;
++      int nBytes = to - offset;
++      int nWritten;
++      
++      unsigned spos = pos;
++      unsigned saddr = (unsigned)addr;
++
++      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_commit_write addr %x pos %x nBytes %d\n",saddr,spos,nBytes));
++      
++      nWritten = yaffs_file_write(f,addr, nBytes, &pos);
++      
++      if(nWritten != nBytes)
++      {
++              T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_commit_write not same size nWritten %d  nBytes %d\n",nWritten,nBytes));
++              SetPageError(pg);
++              ClearPageUptodate(pg);
++      }
++      else
++      {
++              SetPageUptodate(pg);
++      }
++
++      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_commit_write returning %d\n",nWritten));
++      
++      return nWritten;
++
++}
++
++
++
++static void yaffs_FillInodeFromObject(struct inode *inode, yaffs_Object *obj)
++{
++      if (inode && obj) 
++      {
++              inode->i_ino = obj->objectId;
++              inode->i_mode = obj->yst_mode;
++              inode->i_uid = obj->yst_uid;
++              inode->i_gid = obj->yst_gid;
++              inode->i_blksize = inode->i_sb->s_blocksize;
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
++
++              inode->i_rdev = old_decode_dev(obj->yst_rdev);
++              inode->i_atime.tv_sec = (time_t)(obj->yst_atime);
++              inode->i_atime.tv_nsec = 0;
++              inode->i_mtime.tv_sec = (time_t)obj->yst_mtime;
++              inode->i_mtime.tv_nsec =0;
++              inode->i_ctime.tv_sec = (time_t)obj->yst_ctime;
++              inode->i_ctime.tv_nsec = 0;
++#else
++              inode->i_rdev = obj->yst_rdev;
++              inode->i_atime = obj->yst_atime;
++              inode->i_mtime = obj->yst_mtime;
++              inode->i_ctime = obj->yst_ctime;
++#endif
++              inode->i_size = yaffs_GetObjectFileLength(obj);
++              inode->i_blocks = (inode->i_size + 511) >> 9;
++
++              inode->i_nlink = yaffs_GetObjectLinkCount(obj);
++              
++              T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_FillInode mode %x uid %d gid %d size %d count %d\n",
++                              inode->i_mode, inode->i_uid, inode->i_gid, (int)inode->i_size, atomic_read(&inode->i_count)));
++              
++              switch (obj->yst_mode & S_IFMT) 
++              {
++                      default: // fifo, device or socket
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
++                               init_special_inode(inode, obj->yst_mode,old_decode_dev(obj->yst_rdev));
++#else
++                                 init_special_inode(inode, obj->yst_mode,(dev_t)(obj->yst_rdev));
++#endif                                
++                        break;
++                      case S_IFREG:   // file         
++                              inode->i_op = &yaffs_file_inode_operations;
++                              inode->i_fop = &yaffs_file_operations;
++                              inode->i_mapping->a_ops = &yaffs_file_address_operations;
++                              break;
++                      case S_IFDIR:   // directory
++                              inode->i_op = &yaffs_dir_inode_operations;
++                              inode->i_fop = &yaffs_dir_operations;
++                              break;
++                      case S_IFLNK:   // symlink
++                              inode->i_op = &yaffs_symlink_inode_operations;
++                              break;
++              }
++              
++              
++              inode->u.generic_ip = obj;
++              obj->myInode = inode;
++              
++      }
++      else
++      {
++              T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_FileInode invalid parameters\n"));
++      }
++
++}
++
++struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev,yaffs_Object *obj)
++{
++      struct inode * inode;
++      
++      if(!sb)
++      {
++        T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_get_inode for NULL super_block!!\n"));
++        return NULL;
++        
++      }
++      
++      if(!obj)
++      {
++        T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_get_inode for NULL object!!\n"));
++        return NULL;
++        
++      }
++      
++      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_get_inode for object %d\n",obj->objectId));
++
++      inode = iget(sb,obj->objectId);
++
++      // NB Side effect: iget calls back to yaffs_read_inode().
++      // iget also increments the inode's i_count
++      
++      return inode;
++}
++
++static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, loff_t *pos)
++{
++      yaffs_Object *obj;
++      int nWritten,ipos;
++      struct inode *inode;
++      yaffs_Device *dev;
++      
++      
++      obj  = yaffs_DentryToObject(f->f_dentry);
++      
++      dev = obj->myDev;
++      
++      yaffs_GrossLock(dev);
++
++      inode = f->f_dentry->d_inode;
++
++      if(!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND)
++      {
++              ipos = inode->i_size;
++      }
++      else
++      {
++              ipos = *pos;
++      }
++      
++      
++      if(!obj)
++      {
++              T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_file_write: hey obj is null!\n"));
++      }
++      else
++      {
++              T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_file_write about to write writing %d bytes to object %d at %d\n",n,obj->objectId,ipos));
++      }
++
++      nWritten = yaffs_WriteDataToFile(obj,buf,ipos,n,0);
++
++      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_file_write writing %d bytes, %d written at %d\n",n,nWritten,ipos));
++      if(nWritten > 0)
++      {
++              ipos += nWritten;
++              *pos = ipos;
++              if(ipos > inode->i_size)
++              {
++                      inode->i_size = ipos;
++                      inode->i_blocks = (ipos + 511)>>9;
++                      
++                      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_file_write size updated to %d bytes, %d blocks\n",ipos,(int)(inode->i_blocks)));
++              }
++              
++      }
++      yaffs_GrossUnlock(dev);
++      
++      return nWritten != n ? -ENOSPC : nWritten;
++}
++
++
++
++static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir)
++{
++      yaffs_Object *obj;
++      yaffs_Device *dev;
++      struct inode *inode = f->f_dentry->d_inode;
++      unsigned long offset, curoffs;
++      struct list_head *i;    
++      yaffs_Object *l;
++      
++      char name[YAFFS_MAX_NAME_LENGTH +1];
++              
++      obj =  yaffs_DentryToObject(f->f_dentry);
++      dev = obj->myDev;
++      
++      yaffs_GrossLock(dev);
++      
++      offset = f->f_pos;
++      
++      T(YAFFS_TRACE_OS,("yaffs_readdir: starting at %d\n",(int)offset));
++      
++      if(offset == 0)
++      {
++        T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_readdir: entry . ino %d \n",(int)inode->i_ino));
++              if(filldir(dirent,".",1,offset,inode->i_ino,DT_DIR) < 0)
++              {
++                      goto out;
++              }
++              offset++;
++              f->f_pos++;
++      }
++      if(offset == 1)
++      {
++              T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_readdir: entry .. ino %d \n",(int)f->f_dentry->d_parent->d_inode->i_ino));
++              if(filldir(dirent,"..",2,offset,f->f_dentry->d_parent->d_inode->i_ino,DT_DIR) < 0)
++              {
++                      goto out;
++              }
++              offset++;
++              f->f_pos++;
++      }
++      
++      curoffs = 1;
++      
++      list_for_each(i,&obj->variant.directoryVariant.children)
++      {
++              curoffs++;
++              if(curoffs >= offset)
++              {               
++                      l = list_entry(i, yaffs_Object,siblings);
++                      
++                      yaffs_GetObjectName(l,name,YAFFS_MAX_NAME_LENGTH+1); 
++                      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_readdir: %s inode %d\n",name,yaffs_GetObjectInode(l)));
++                      
++                      if(filldir(dirent,
++                                         name,
++                                         strlen(name),
++                                         offset,
++                                         yaffs_GetObjectInode(l),
++                                         yaffs_GetObjectType(l))
++                                         < 0)
++                      {
++                              goto up_and_out;
++                      }
++                      
++                      offset++;
++                      f->f_pos++;        
++              }
++      }
++
++  up_and_out:
++  out:
++  
++    yaffs_GrossUnlock(dev);
++    
++      return 0;
++}
++
++
++/*
++ * File creation. Allocate an inode, and we're done..
++ */
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
++#else
++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
++#endif
++{
++      struct inode *inode;
++      
++      yaffs_Object *obj = NULL;
++      yaffs_Device *dev;
++      
++      yaffs_Object *parent = yaffs_InodeToObject(dir);
++      
++      int error = -ENOSPC;
++      uid_t uid = current->fsuid;
++      gid_t gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
++
++      if(parent)
++      {
++              T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_mknod: parent object %d type %d\n",
++                                       parent->objectId,parent->variantType));
++      }
++      else
++      {
++              T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_mknod: could not get parent object\n"));
++              return -EPERM;
++      }
++      
++      T(YAFFS_TRACE_OS,("yaffs_mknod: making oject for %s, mode %x dev %x\n",
++                                      dentry->d_name.name, mode,rdev));
++
++      dev = parent->myDev;
++      
++      yaffs_GrossLock(dev);
++
++      switch (mode & S_IFMT) 
++      {
++              default:
++                      // Special (socket, fifo, device...)
++                      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_mknod: making special\n"));
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
++                        obj = yaffs_MknodSpecial(parent,dentry->d_name.name,mode,uid, gid,old_encode_dev(rdev));
++#else
++                        obj = yaffs_MknodSpecial(parent,dentry->d_name.name,mode,uid, gid,rdev);
++#endif                        
++                break;
++              case S_IFREG:   // file         
++                      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_mknod: making file\n"));
++                      obj = yaffs_MknodFile(parent,dentry->d_name.name,mode,uid, gid);
++                      break;
++              case S_IFDIR:   // directory
++                      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_mknod: making directory\n"));
++                      obj = yaffs_MknodDirectory(parent,dentry->d_name.name,mode,uid, gid);
++                      break;
++              case S_IFLNK:   // symlink
++                      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_mknod: making file\n"));
++                      obj = NULL; // Do we ever get here?
++                      break;
++      }
++      
++      if(obj)
++      {
++              inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj);
++              d_instantiate(dentry, inode);
++              T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_mknod created object %d count = %d\n",obj->objectId,atomic_read(&inode->i_count)));
++              error = 0;
++      }
++      else
++      {
++              T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_mknod failed making object\n"));
++              error = -ENOMEM;
++      }
++
++      yaffs_GrossUnlock(dev);
++
++      return error;
++}
++
++static int yaffs_mkdir(struct inode * dir, struct dentry * dentry, int mode)
++{
++      int retVal;
++      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_mkdir\n"));
++      retVal =  yaffs_mknod(dir, dentry, mode | S_IFDIR, 0);
++#if 0
++ // attempt to fix dir bug - didn't work
++      if(!retVal)
++      {
++              dget(dentry);
++      }
++#endif
++      return retVal;
++}
++
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
++static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *n)
++#else
++static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode)
++#endif
++{
++      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_create\n"));
++      return yaffs_mknod(dir, dentry, mode | S_IFREG, 0);
++}
++
++
++static int yaffs_unlink(struct inode * dir, struct dentry *dentry)
++{
++      int retVal;
++      
++      yaffs_Device *dev;
++      
++      
++      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_unlink %d:%s\n",(int)(dir->i_ino),dentry->d_name.name));
++      
++      dev = yaffs_InodeToObject(dir)->myDev;
++      
++      yaffs_GrossLock(dev);
++      
++      
++      retVal = yaffs_Unlink(yaffs_InodeToObject(dir),dentry->d_name.name);
++      
++      
++      yaffs_GrossUnlock(dev);
++      
++      if( retVal == YAFFS_OK)
++      {
++              dentry->d_inode->i_nlink--;
++              mark_inode_dirty(dentry->d_inode);
++              return 0;
++      }
++      else
++      {
++              return -ENOTEMPTY;
++      }
++}
++
++
++/*
++ * Create a link...
++ */
++static int yaffs_link(struct dentry *old_dentry, struct inode * dir, struct dentry * dentry)
++{
++      struct inode *inode = old_dentry->d_inode;
++      yaffs_Object *obj = NULL;
++      yaffs_Object *link=NULL;
++      yaffs_Device *dev;
++      
++      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_link\n"));
++      
++      obj = yaffs_InodeToObject(inode);
++      dev = obj->myDev;
++      
++      yaffs_GrossLock(dev);
++
++      if (!S_ISDIR(inode->i_mode))    // Don't link directories
++      {
++              link = yaffs_Link(yaffs_InodeToObject(dir),dentry->d_name.name,obj);
++      }
++      
++
++      if(link)
++      {
++              old_dentry->d_inode->i_nlink =  yaffs_GetObjectLinkCount(obj);
++              d_instantiate(dentry, old_dentry->d_inode);
++              atomic_inc(&old_dentry->d_inode->i_count);
++              T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_link link count %d i_count %d\n",    
++                      old_dentry->d_inode->i_nlink,atomic_read(&old_dentry->d_inode->i_count)));
++      
++      }
++      
++      yaffs_GrossUnlock(dev);
++      
++
++      if(link)
++      {
++      
++              return 0;
++      }
++      
++      
++      return -EPERM; 
++}
++
++
++static int yaffs_symlink(struct inode * dir, struct dentry *dentry, const char * symname)
++{
++      yaffs_Object *obj;
++      yaffs_Device *dev;
++      uid_t uid = current->fsuid;
++      gid_t gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
++      
++      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_symlink\n"));
++      
++      dev = yaffs_InodeToObject(dir)->myDev;
++      yaffs_GrossLock(dev);
++      obj = yaffs_MknodSymLink(yaffs_InodeToObject(dir), dentry->d_name.name, 
++                                                       S_IFLNK | S_IRWXUGO, uid, gid,
++                                                       symname);
++      yaffs_GrossUnlock(dev);
++
++      if(obj)
++      {
++
++              struct inode* inode;
++      
++              inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj);
++              d_instantiate(dentry, inode);
++              T(YAFFS_TRACE_OS,(KERN_DEBUG"symlink created OK\n"));
++              return 0;
++      }
++      else
++      {
++              T(YAFFS_TRACE_OS,(KERN_DEBUG"symlink not created\n"));
++
++      }
++      
++      return -ENOMEM;
++}
++
++static int yaffs_sync_object(struct file * file, struct dentry *dentry, int datasync)
++{
++
++      yaffs_Object *obj;
++      yaffs_Device *dev;
++      
++      obj = yaffs_DentryToObject(dentry);
++
++      dev = obj->myDev;
++      
++      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_sync_object\n"));
++      yaffs_GrossLock(dev);
++      yaffs_FlushFile(obj,1);
++      yaffs_GrossUnlock(dev);
++      return 0;
++}
++
++/*
++ * The VFS layer already does all the dentry stuff for rename.
++ *
++ * NB: POSIX says you can rename an object over an old object of the same name
++ */
++static int yaffs_rename(struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir,struct dentry *new_dentry)
++{
++      yaffs_Device *dev;
++      int retVal = YAFFS_FAIL;
++      int removed = 0;
++      yaffs_Object *target;
++      
++      dev = yaffs_InodeToObject(old_dir)->myDev;
++
++      yaffs_GrossLock(dev);
++      
++      // Check if the target is an existing directory that is not empty.
++      target = yaffs_FindObjectByName(yaffs_InodeToObject(new_dir),new_dentry->d_name.name);
++      
++      if(target &&
++         target->variantType == YAFFS_OBJECT_TYPE_DIRECTORY &&
++         !list_empty(&target->variant.directoryVariant.children))
++      {
++              retVal = YAFFS_FAIL;
++      }
++      else
++      {
++         
++              // Unlink the target if it exists
++              removed = yaffs_Unlink(yaffs_InodeToObject(new_dir),new_dentry->d_name.name);
++
++      
++              retVal =  yaffs_RenameObject(yaffs_InodeToObject(old_dir),old_dentry->d_name.name,
++                                                                      yaffs_InodeToObject(new_dir),new_dentry->d_name.name);
++                                                                      
++      }
++      yaffs_GrossUnlock(dev);
++      
++      if(retVal == YAFFS_OK)
++      {
++              if(removed == YAFFS_OK)
++              {
++                      new_dentry->d_inode->i_nlink--;
++                      mark_inode_dirty(new_dentry->d_inode);
++              }
++              
++              return 0;
++      }
++      else
++      {
++              return -ENOTEMPTY;
++      }
++      
++
++}
++
++static int yaffs_setattr(struct dentry *dentry, struct iattr *attr)
++{
++      struct inode *inode = dentry->d_inode;
++      int error;
++      yaffs_Device *dev;
++      
++      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_setattr of object %d\n",yaffs_InodeToObject(inode)->objectId));
++      
++      if((error = inode_change_ok(inode,attr)) == 0)
++      {
++      
++              dev = yaffs_InodeToObject(inode)->myDev;
++              yaffs_GrossLock(dev);
++              if(yaffs_SetAttributes(yaffs_InodeToObject(inode),attr) == YAFFS_OK)
++              {
++                      error = 0;
++              }
++              else
++              {
++                      error = -EPERM;
++              }
++              yaffs_GrossUnlock(dev);
++              if (!error)
++                      error = inode_setattr(inode,attr);
++      }
++      return error;
++}
++
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
++static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf)
++#else
++static int yaffs_statfs(struct super_block *sb, struct statfs *buf)
++#endif
++{
++
++      
++      yaffs_Device *dev = yaffs_SuperToDevice(sb);
++      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_statfs\n"));
++
++      yaffs_GrossLock(dev);
++      
++      
++      buf->f_type = YAFFS_MAGIC;
++      buf->f_bsize = sb->s_blocksize;
++      buf->f_namelen = 255;
++      if(sb->s_blocksize > dev->nBytesPerChunk)
++      {
++              
++              buf->f_blocks = (dev->endBlock - dev->startBlock + 1) * dev->nChunksPerBlock/
++                                              (sb->s_blocksize/dev->nBytesPerChunk);
++              buf->f_bfree = yaffs_GetNumberOfFreeChunks(dev)/
++                                              (sb->s_blocksize/dev->nBytesPerChunk);
++      }
++      else
++      {
++              
++              buf->f_blocks = (dev->endBlock - dev->startBlock + 1) * dev->nChunksPerBlock *
++                                              (dev->nBytesPerChunk/sb->s_blocksize);
++              buf->f_bfree = yaffs_GetNumberOfFreeChunks(dev) *
++                                              (dev->nBytesPerChunk/sb->s_blocksize);
++      }
++      buf->f_files = 0;
++      buf->f_ffree = 0;
++      buf->f_bavail =  buf->f_bfree;
++      
++      yaffs_GrossUnlock(dev);
++      return 0;
++}
++
++static void yaffs_read_inode (struct inode *inode)
++{
++      // NB This is called as a side effect of other functions and
++      // thus gross locking should always be in place already.
++      
++      yaffs_Object *obj ; 
++      yaffs_Device *dev = yaffs_SuperToDevice(inode->i_sb);
++      
++      T(YAFFS_TRACE_OS,(KERN_DEBUG"yaffs_read_inode for %d\n",(int)inode->i_ino));
++
++      obj  = yaffs_FindObjectByNumber(dev,inode->i_ino);
++      
++      yaffs_FillInodeFromObject(inode,obj);
++
++}
++
++static LIST_HEAD(yaffs_dev_list);
++
++static void yaffs_put_super(struct super_block *sb)
++{
++      yaffs_Device *dev = yaffs_SuperToDevice(sb);
++      
++      yaffs_GrossLock(dev);
++      if(dev->putSuperFunc)
++      {
++               dev->putSuperFunc(sb);
++      }
++      yaffs_Deinitialise(dev);
++      yaffs_GrossUnlock(dev);
++
++      /* we assume this is protected by lock_kernel() in mount/umount */
++      list_del(&dev->devList);
++
++      kfree(dev);
++}
++
++
++#ifdef CONFIG_YAFFS_YAFFS1
++
++static void  yaffs_MTDPutSuper(struct super_block *sb)
++{
++      
++      struct mtd_info *mtd = yaffs_SuperToDevice(sb)->genericDevice;
++      
++      if(mtd->sync)
++      {
++              mtd->sync(mtd);
++      }
++      
++      put_mtd_device(mtd);
++}
++
++#endif
++
++
++static struct super_block *yaffs_internal_read_super(int yaffsVersion, struct super_block * sb, void * data, int silent)
++{
++      int nBlocks;
++      struct inode * inode = NULL;
++      struct dentry * root;
++      yaffs_Device *dev = 0;
++      char devname_buf[BDEVNAME_SIZE+1];
++      struct mtd_info *mtd;
++      int err;
++      
++      sb->s_magic = YAFFS_MAGIC;
++      sb->s_op = &yaffs_super_ops;
++      
++      if(!sb)
++              printk(KERN_INFO"yaffs: sb is NULL\n");
++      else if(!sb->s_dev)
++              printk(KERN_INFO"yaffs: sb->s_dev is NULL\n");
++      else if(!yaffs_devname(sb, devname_buf))
++              printk(KERN_INFO"yaffs: devname is NULL\n");
++      else
++              printk(KERN_INFO"yaffs: dev is %d name is \"%s\"\n", sb->s_dev, yaffs_devname(sb, devname_buf));
++
++      
++
++      sb->s_blocksize = PAGE_CACHE_SIZE;
++      sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
++      T(YAFFS_TRACE_OS,("yaffs_read_super: Using yaffs%d\n",yaffsVersion));
++      T(YAFFS_TRACE_OS,("yaffs_read_super: block size %d\n", (int)(sb->s_blocksize)));
++
++#ifdef CONFIG_YAFFS_DISABLE_WRITE_VERIFY
++      T(YAFFS_TRACE_OS,("yaffs: Write verification disabled. All guarantees null and void\n"));
++#endif
++
++
++      T(YAFFS_TRACE_ALWAYS,("yaffs: Attempting MTD mount on %u.%u, \"%s\"\n",
++       MAJOR(sb->s_dev),MINOR(sb->s_dev), yaffs_devname(sb, devname_buf)));
++              
++      // Check it's an mtd device.....
++      if(MAJOR(sb->s_dev) != MTD_BLOCK_MAJOR)
++      {
++              return NULL; // This isn't an mtd device
++      } 
++      
++      // Get the device
++      mtd = get_mtd_device(NULL, MINOR(sb->s_dev));
++      if (!mtd) 
++      {
++              T(YAFFS_TRACE_ALWAYS,("yaffs: MTD device #%u doesn't appear to exist\n", MINOR(sb->s_dev)));
++              return NULL;
++      }
++      
++      // Check it's NAND
++      if(mtd->type != MTD_NANDFLASH)
++      {
++              T(YAFFS_TRACE_ALWAYS,("yaffs: MTD device is not NAND it's type %d\n", mtd->type));
++              return NULL;
++      }
++
++      T(YAFFS_TRACE_OS,(" erase %p\n",mtd->erase));
++      T(YAFFS_TRACE_OS,(" read %p\n",mtd->read));
++      T(YAFFS_TRACE_OS,(" write %p\n",mtd->write));
++      T(YAFFS_TRACE_OS,(" readoob %p\n",mtd->read_oob));
++      T(YAFFS_TRACE_OS,(" writeoob %p\n",mtd->write_oob));
++//    T(YAFFS_TRACE_OS,(" block_isbad %p\n",mtd->block_isbad));
++//    T(YAFFS_TRACE_OS,(" block_markbad %p\n",mtd->block_markbad));
++      T(YAFFS_TRACE_OS,(" oobblock %d\n",mtd->oobblock));
++      T(YAFFS_TRACE_OS,(" oobsize %d\n",mtd->oobsize));
++      T(YAFFS_TRACE_OS,(" erasesize %d\n",mtd->erasesize));
++      T(YAFFS_TRACE_OS,(" size %d\n",mtd->size));
++
++      if(yaffsVersion == 2)
++      {
++              // Check for version 2 style functions
++              if(!mtd->erase ||
++//               !mtd->block_isbad ||
++//               !mtd->block_markbad ||
++                 !mtd->read  ||
++                 !mtd->write ||
++                 !mtd->write_ecc ||
++                 !mtd->read_ecc ||
++                 !mtd->read_oob ||
++                 !mtd->write_oob )
++              {
++                      T(YAFFS_TRACE_ALWAYS,("yaffs: MTD device does not support required functions\n"));;
++                      return NULL;
++              }
++      
++              if(mtd->oobblock < YAFFS_MIN_YAFFS2_CHUNK_SIZE ||
++                 mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE)
++              {
++                      T(YAFFS_TRACE_ALWAYS,("yaffs: MTD device does not support have the right page sizes\n"));
++                      return NULL;
++              }               }
++      else
++      {
++              // Check for V1 style functions
++              if(!mtd->erase ||
++                 !mtd->read  ||
++                 !mtd->write ||
++                 !mtd->write_ecc ||
++                 !mtd->read_ecc ||
++                 !mtd->read_oob ||
++                 !mtd->write_oob )
++              {
++                      T(YAFFS_TRACE_ALWAYS,("yaffs: MTD device does not support required functions\n"));;
++                      return NULL;
++              }
++      
++              if(mtd->oobblock != YAFFS_BYTES_PER_CHUNK ||
++                 mtd->oobsize != YAFFS_BYTES_PER_SPARE)
++              {
++                      T(YAFFS_TRACE_ALWAYS,("yaffs: MTD device does not support have the right page sizes\n"));
++                      return NULL;
++              }
++      }
++         
++
++              // OK, so if we got here, we have an MTD that's NAND and looks 
++              // like it has the right capabilities
++              // Set the yaffs_Device up for mtd
++
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
++      sb->s_fs_info = dev = kmalloc(sizeof(yaffs_Device),GFP_KERNEL);
++#else
++      sb->u.generic_sbp = dev = kmalloc(sizeof(yaffs_Device),GFP_KERNEL);
++#endif
++      if(!dev)
++      {
++              // Deep shit could not allocate device structure
++              T(YAFFS_TRACE_ALWAYS,("yaffs_read_super: Failed trying to allocate yaffs_Device. \n"));
++              return NULL;
++      }
++
++      memset(dev,0,sizeof(yaffs_Device));
++      dev->genericDevice = mtd; 
++      dev->name = mtd->name;
++
++      // Set up the memory size parameters....
++      
++      nBlocks = mtd->size / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK);
++      dev->startBlock = 0;
++      dev->endBlock = nBlocks - 1;
++      dev->nChunksPerBlock = YAFFS_CHUNKS_PER_BLOCK;
++      dev->nBytesPerChunk = YAFFS_BYTES_PER_CHUNK;
++      dev->nReservedBlocks = 5;
++      dev->nShortOpCaches = 10; // Enable short op caching
++      
++
++      // ... and the functions.
++      if(yaffsVersion == 2)
++      {
++              dev->writeChunkWithTagsToNAND = nandmtd2_WriteChunkWithTagsToNAND;
++              dev->readChunkWithTagsFromNAND = nandmtd2_ReadChunkWithTagsFromNAND;
++              dev->markNANDBlockBad = nandmtd2_MarkNANDBlockBad;
++              dev->queryNANDBlock = nandmtd2_QueryNANDBlock;
++              dev->spareBuffer = YMALLOC(mtd->oobsize);
++              dev->isYaffs2 = 1;
++              dev->nBytesPerChunk = mtd->oobblock;
++              dev->nChunksPerBlock = mtd->erasesize / mtd->oobblock;
++              nBlocks = mtd->size / mtd->erasesize;
++              dev->startBlock = 0;
++              dev->endBlock = nBlocks - 1;
++      }
++      else
++      {
++              dev->writeChunkToNAND = nandmtd_WriteChunkToNAND;
++              dev->readChunkFromNAND = nandmtd_ReadChunkFromNAND;
++              dev->isYaffs2 = 0;
++      }
++      // ... and common functions
++      dev->eraseBlockInNAND = nandmtd_EraseBlockInNAND;
++      dev->initialiseNAND = nandmtd_InitialiseNAND;
++      
++      dev->putSuperFunc = yaffs_MTDPutSuper;
++      
++#ifndef CONFIG_YAFFS_DOES_ECC
++      dev->useNANDECC = 1;
++#endif
++
++      /* we assume this is protected by lock_kernel() in mount/umount */
++      list_add_tail(&dev->devList, &yaffs_dev_list);
++
++      init_MUTEX(&dev->grossLock);
++      
++      
++      yaffs_GrossLock(dev);
++      
++      err = yaffs_GutsInitialise(dev);
++
++      T(YAFFS_TRACE_OS,("yaffs_read_super: guts initialised %s\n", (err == YAFFS_OK) ? "OK" : "FAILED"));
++
++      // Create root inode
++      if(err == YAFFS_OK)
++        inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0,yaffs_Root(dev));
++
++      yaffs_GrossUnlock(dev);
++
++      if (!inode)
++              return NULL;
++              
++// added NCB
++      inode->i_op = & yaffs_dir_inode_operations;
++      inode->i_fop = & yaffs_dir_operations;
++
++      T(YAFFS_TRACE_OS,("yaffs_read_super: got root inode\n"));
++              
++
++      root = d_alloc_root(inode);
++
++      T(YAFFS_TRACE_OS,("yaffs_read_super: d_alloc_root done\n"));
++
++      if (!root) {
++              iput(inode);
++              return NULL;
++      }
++      sb->s_root = root;
++
++      T(YAFFS_TRACE_OS,("yaffs_read_super: done\n"));
++      return sb;
++}
++
++
++
++#ifdef CONFIG_YAFFS_YAFFS1
++
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
++static int yaffs_internal_read_super_mtd(struct super_block * sb, void * data, int silent)
++{
++       return yaffs_internal_read_super(1,sb,data,silent) ? 0 : -1;
++}
++
++static struct super_block *yaffs_read_super(struct file_system_type * fs, int flags, const char *dev_name, void *data)
++{
++
++    return get_sb_bdev(fs, flags, dev_name, data, yaffs_internal_read_super_mtd);
++}
++
++static struct file_system_type yaffs_fs_type = {
++      .owner          = THIS_MODULE,
++      .name           = "yaffs",
++      .get_sb         = yaffs_read_super,
++      .kill_sb        = kill_block_super,
++      .fs_flags       = FS_REQUIRES_DEV,
++};
++#else
++static struct super_block *yaffs_read_super(struct super_block * sb, void * data, int silent)
++{
++      return yaffs_internal_read_super(1,sb,data,silent);
++}
++
++static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, FS_REQUIRES_DEV);
++#endif
++
++#endif // CONFIG_YAFFS_YAFFS1
++
++#ifdef CONFIG_YAFFS_YAFFS2
++
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
++static int yaffs2_internal_read_super_mtd(struct super_block * sb, void * data, int silent)
++{
++       return yaffs_internal_read_super(2,sb,data,silent) ? 0 : -1;
++}
++
++static struct super_block *yaffs2_read_super(struct file_system_type * fs, int flags, const char *dev_name, void *data)
++{
++
++    return get_sb_bdev(fs, flags, dev_name, data, yaffs2_internal_read_super_mtd);
++}
++
++static struct file_system_type yaffs2_fs_type = {
++      .owner          = THIS_MODULE,
++      .name           = "yaffs2",
++      .get_sb         = yaffs2_read_super,
++      .kill_sb        = kill_block_super,
++      .fs_flags       = FS_REQUIRES_DEV,
++};
++#else
++static struct super_block *yaffs2_read_super(struct super_block * sb, void * data, int silent)
++{
++      return yaffs_internal_read_super(2,sb,data,silent);
++}
++
++static DECLARE_FSTYPE(yaffs2_fs_type, "yaffs2", yaffs2_read_super, FS_REQUIRES_DEV);
++#endif
++
++#endif // CONFIG_YAFFS_YAFFS2
++
++
++
++
++static struct proc_dir_entry *my_proc_entry;
++
++static char * yaffs_dump_dev(char *buf,yaffs_Device *dev)
++{
++      buf +=sprintf(buf,"startBlock......... %d\n",dev->startBlock);
++      buf +=sprintf(buf,"endBlock........... %d\n",dev->endBlock);
++      buf +=sprintf(buf,"chunkGroupBits..... %d\n",dev->chunkGroupBits);
++      buf +=sprintf(buf,"chunkGroupSize..... %d\n",dev->chunkGroupSize);
++      buf +=sprintf(buf,"nErasedBlocks...... %d\n",dev->nErasedBlocks);
++      buf +=sprintf(buf,"nTnodesCreated..... %d\n",dev->nTnodesCreated);
++      buf +=sprintf(buf,"nFreeTnodes........ %d\n",dev->nFreeTnodes);
++      buf +=sprintf(buf,"nObjectsCreated.... %d\n",dev->nObjectsCreated);
++      buf +=sprintf(buf,"nFreeObjects....... %d\n",dev->nFreeObjects);
++      buf +=sprintf(buf,"nFreeChunks........ %d\n",dev->nFreeChunks);
++      buf +=sprintf(buf,"nPageWrites........ %d\n",dev->nPageWrites);
++      buf +=sprintf(buf,"nPageReads......... %d\n",dev->nPageReads);
++      buf +=sprintf(buf,"nBlockErasures..... %d\n",dev->nBlockErasures);
++      buf +=sprintf(buf,"nGCCopies.......... %d\n",dev->nGCCopies);
++      buf +=sprintf(buf,"garbageCollections. %d\n",dev->garbageCollections);
++      buf +=sprintf(buf,"passiveGCs......... %d\n",dev->passiveGarbageCollections);
++      buf +=sprintf(buf,"nRetriedWrites..... %d\n",dev->nRetriedWrites);
++      buf +=sprintf(buf,"nRetireBlocks...... %d\n",dev->nRetiredBlocks);
++      buf +=sprintf(buf,"eccFixed........... %d\n",dev->eccFixed);
++      buf +=sprintf(buf,"eccUnfixed......... %d\n",dev->eccUnfixed);
++      buf +=sprintf(buf,"tagsEccFixed....... %d\n",dev->tagsEccFixed);
++      buf +=sprintf(buf,"tagsEccUnfixed..... %d\n",dev->tagsEccUnfixed);
++      buf +=sprintf(buf,"cacheHits.......... %d\n",dev->cacheHits);
++      buf +=sprintf(buf,"nDeletedFiles...... %d\n",dev->nDeletedFiles);
++      buf +=sprintf(buf,"nUnlinkedFiles..... %d\n",dev->nUnlinkedFiles);
++      buf +=sprintf(buf,"nBackgroudDeletions %d\n",dev->nBackgroundDeletions);
++      buf +=sprintf(buf,"useNANDECC......... %d\n",dev->useNANDECC);
++      buf +=sprintf(buf,"isYaffs2........... %d\n",dev->isYaffs2);
++
++      return buf;     
++}
++
++static int  yaffs_proc_read(
++        char *page,
++      char **start,
++      off_t offset,
++      int count,
++      int *eof,
++      void *data
++      )
++{
++      struct list_head *item;
++      char *buf = page;
++      int step = offset;
++      int n = 0;
++
++      /* Get proc_file_read() to step 'offset' by one on each sucessive call.
++       * We use 'offset' (*ppos) to indicate where we are in devList.
++       * This also assumes the user has posted a read buffer large
++       * enough to hold the complete output; but that's life in /proc.
++       */
++
++      *(int *)start = 1;
++
++      /* Print header first */
++      if (step == 0) {
++              buf += sprintf(buf, "YAFFS built:" __DATE__ " "__TIME__
++              "\n%s\n%s\n", yaffs_fs_c_version, yaffs_guts_c_version);
++      }
++
++      /* hold lock_kernel while traversing yaffs_dev_list */
++      lock_kernel();
++
++      /* Locate and print the Nth entry.  Order N-squared but N is small. */
++      list_for_each(item, &yaffs_dev_list) {
++              yaffs_Device *dev = list_entry(item, yaffs_Device, devList);
++              if (n < step) {
++                      n++;
++                      continue;
++              }
++              buf += sprintf(buf,"\nDevice %d \"%s\"\n", n, dev->name);
++              buf = yaffs_dump_dev(buf, dev);
++              break;
++      }
++      unlock_kernel();
++
++      return buf-page < count ? buf-page : count;
++}
++
++// Stuff to handle installation of file systems
++struct file_system_to_install
++{
++   struct file_system_type *fst;
++   int installed;
++};
++
++static struct file_system_to_install fs_to_install[] =
++{
++#ifdef CONFIG_YAFFS_YAFFS1
++     { &yaffs_fs_type,0},
++#endif
++#ifdef CONFIG_YAFFS_YAFFS2
++     { &yaffs2_fs_type,0},
++#endif
++     { NULL,0}
++};
++
++static int __init init_yaffs_fs(void)
++{
++      int error = 0;
++      struct file_system_to_install *fsinst;  
++      
++   T(YAFFS_TRACE_ALWAYS,("yaffs " __DATE__ " " __TIME__ " Installing. \n"));
++
++
++
++    /* Install the proc_fs entry */
++    my_proc_entry = create_proc_read_entry("yaffs",
++                                           S_IRUGO | S_IFREG,
++                                         &proc_root,
++                                         yaffs_proc_read,
++                                         NULL);
++    if(!my_proc_entry)
++    {
++       return -ENOMEM;
++    }
++    
++    
++
++    // Now add the file system entries
++    
++    fsinst = fs_to_install;
++    
++    while(fsinst->fst && !error)
++    {
++      error = register_filesystem(fsinst->fst);
++      if(!error)
++      {
++          fsinst->installed = 1;
++      }
++      fsinst++;
++    }
++   
++    // Any errors? uninstall 
++    if(error)
++    {
++          fsinst = fs_to_install;
++          
++          while(fsinst->fst)
++          {
++            if(fsinst->installed)
++            {
++              unregister_filesystem(fsinst->fst);
++              fsinst->installed = 0;
++            }
++            fsinst++;
++          }
++    }
++    
++    return error;
++}
++
++static void __exit exit_yaffs_fs(void)
++{
++
++   struct file_system_to_install *fsinst;
++   
++   T(YAFFS_TRACE_ALWAYS,("yaffs " __DATE__ " " __TIME__ " removing. \n"));
++
++   remove_proc_entry("yaffs",&proc_root);
++    
++    fsinst = fs_to_install;
++    
++    while(fsinst->fst)
++    {
++      if(fsinst->installed)
++      {
++        unregister_filesystem(fsinst->fst);
++        fsinst->installed = 0;
++      }
++      fsinst++;
++    }
++
++}
++
++module_init(init_yaffs_fs)
++module_exit(exit_yaffs_fs)
++
++MODULE_DESCRIPTION("YAFFS2 - a NAND specific flash file system");
++MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002,2003,2004");
++MODULE_LICENSE("GPL");
++
++
+diff --git a/fs/yaffs/yaffs_guts.c b/fs/yaffs/yaffs_guts.c
+new file mode 100644
+index 0000000..bf13f91
+--- /dev/null
++++ b/fs/yaffs/yaffs_guts.c
+@@ -0,0 +1,6346 @@
++/*
++ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
++ *
++ * Copyright (C) 2002 Aleph One Ltd.
++ *   for Toby Churchill Ltd and Brightstar Engineering
++ *
++ * Created by Charles Manning <charles@aleph1.co.uk>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ */
++ //yaffs_guts.c
++
++const char *yaffs_guts_c_version="$Id: yaffs_guts.c,v 1.15 2005/08/02 04:24:22 charles Exp $";
++
++#include "yportenv.h"
++
++#include "yaffsinterface.h"
++#include "yaffs_guts.h"
++#include "yaffs_tagsvalidity.h"
++
++
++#include "yaffs_tagscompat.h"
++
++#ifdef CONFIG_YAFFS_WINCE
++void yfsd_LockYAFFS(BOOL fsLockOnly);
++void yfsd_UnlockYAFFS(BOOL fsLockOnly);
++#endif
++
++#define YAFFS_PASSIVE_GC_CHUNKS 2
++
++#if 0
++// Use Steven Hill's ECC struff instead
++// External functions for ECC on data
++void nand_calculate_ecc (const u_char *dat, u_char *ecc_code);
++int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc);
++#define yaffs_ECCCalculate(data,ecc) nand_calculate_ecc(data,ecc)
++#define yaffs_ECCCorrect(data,read_ecc,calc_ecc) nand_correct_ecc(data,read_ecc,calc_ecc)
++#else
++#include "yaffs_ecc.h"
++#endif
++
++#if 0
++// countBits is a quick way of counting the number of bits in a byte.
++// ie. countBits[n] holds the number of 1 bits in a byte with the value n.
++
++static const char yaffs_countBitsTable[256] =
++{
++0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,
++1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
++1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
++2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
++1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
++2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
++2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
++3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
++1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
++2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
++2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
++3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
++2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
++3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
++3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
++4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8
++};
++
++static int yaffs_CountBits(__u8 x)
++{
++      int retVal;
++      retVal = yaffs_countBitsTable[x];
++      return retVal;
++}
++
++#endif
++
++
++#if 0
++// Stuff using yea olde tags
++static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr);
++static void yaffs_GetTagsFromSpare(yaffs_Device *dev, yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr);
++
++static int yaffs_ReadChunkTagsFromNAND(yaffs_Device *dev,int chunkInNAND, yaffs_Tags *tags, int *chunkDeleted);
++static int yaffs_TagsMatch(const yaffs_Tags *tags, int objectId, int chunkInObject, int chunkDeleted);
++#else
++#endif
++
++// NAND access
++
++
++static Y_INLINE int yaffs_ReadChunkWithTagsFromNAND(yaffs_Device *dev,int chunkInNAND, __u8 *buffer, yaffs_ExtendedTags *tags);
++static Y_INLINE int yaffs_WriteChunkWithTagsToNAND(yaffs_Device *dev,int chunkInNAND, const __u8 *data, yaffs_ExtendedTags *tags);
++static Y_INLINE int yaffs_MarkBlockBad(yaffs_Device *dev, int blockNo);
++static Y_INLINE int yaffs_QueryInitialBlockState(yaffs_Device *dev,int blockNo, yaffs_BlockState *state,unsigned *sequenceNumber);
++// Local prototypes
++static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device *dev, const __u8 *buffer, yaffs_ExtendedTags *tags, int useReserve);
++#if 0
++static int yaffs_CheckObjectHashSanity(yaffs_Device *dev);
++#endif
++static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkInNAND, int inScan);
++
++static yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev,int number,yaffs_ObjectType type);
++static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj);
++static int yaffs_UpdateObjectHeader(yaffs_Object *in,const YCHAR *name, int force,int isShrink, int shadows);
++static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj);
++static int yaffs_CheckStructures(void);
++static int yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset,int *limit);
++static int yaffs_DoGenericObjectDeletion(yaffs_Object *in);
++
++static yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device *dev,int blockNo);
++
++static __u8 *yaffs_GetTempBuffer(yaffs_Device *dev,int lineNo);
++static void yaffs_ReleaseTempBuffer(yaffs_Device *dev, __u8 *buffer, int lineNo);
++
++
++// Robustification (if it ever comes about...)
++static void yaffs_RetireBlock(yaffs_Device *dev,int blockInNAND);
++#if 0
++static void yaffs_HandleReadDataError(yaffs_Device *dev,int chunkInNAND);
++#endif
++static void yaffs_HandleWriteChunkError(yaffs_Device *dev,int chunkInNAND);
++static void yaffs_HandleWriteChunkOk(yaffs_Device *dev,int chunkInNAND,const __u8 *data, const yaffs_ExtendedTags *tags);
++static void yaffs_HandleUpdateChunk(yaffs_Device *dev,int chunkInNAND,  const yaffs_ExtendedTags *tags);
++
++static int  yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND);
++
++static int yaffs_UnlinkWorker(yaffs_Object *obj);
++static void yaffs_DestroyObject(yaffs_Object *obj);
++
++#if 0
++static int yaffs_VerifyCompare(const __u8 *d0, const __u8 * d1, const yaffs_Spare *s0, const yaffs_Spare *s1,int dataSize);
++#endif
++
++static int yaffs_TagsMatch(const yaffs_ExtendedTags *tags, int objectId, int chunkInObject);
++
++
++loff_t yaffs_GetFileSize(yaffs_Object *obj);
++
++
++static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve);
++
++static void yaffs_VerifyFreeChunks(yaffs_Device *dev);
++
++#ifdef YAFFS_PARANOID
++static int yaffs_CheckFileSanity(yaffs_Object *in);
++#else
++#define yaffs_CheckFileSanity(in)
++#endif
++
++static void yaffs_InvalidateWholeChunkCache(yaffs_Object *in);
++static void yaffs_InvalidateChunkCache(yaffs_Object *object, int chunkId);
++
++static int yaffs_ReadChunkWithTagsFromNAND(yaffs_Device *dev,int chunkInNAND, __u8 *buffer, yaffs_ExtendedTags *tags)
++{
++      chunkInNAND -= dev->chunkOffset;
++      
++      if(dev->readChunkWithTagsFromNAND)
++              return dev->readChunkWithTagsFromNAND(dev,chunkInNAND,buffer,tags);
++      else
++              return yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(dev,chunkInNAND,buffer,tags);
++}
++
++static Y_INLINE int yaffs_WriteChunkWithTagsToNAND(yaffs_Device *dev,int chunkInNAND, const __u8 *buffer, yaffs_ExtendedTags *tags)
++{
++      chunkInNAND -= dev->chunkOffset;
++      
++      if(tags)
++      {
++              tags->sequenceNumber = dev->sequenceNumber;
++              tags->chunkUsed = 1;
++              if(!yaffs_ValidateTags(tags))
++              {
++                      T(YAFFS_TRACE_ERROR,(TSTR("Writing uninitialised tags" TENDSTR)));
++                      YBUG();
++              }
++              T(YAFFS_TRACE_WRITE,(TSTR("Writing chunk %d tags %d %d"TENDSTR),chunkInNAND,tags->objectId,tags->chunkId));
++      }
++      else
++      {
++                      T(YAFFS_TRACE_ERROR,(TSTR("Writing with no tags" TENDSTR)));
++                      YBUG();
++      }
++
++      if(dev->writeChunkWithTagsToNAND)
++              return dev->writeChunkWithTagsToNAND(dev,chunkInNAND,buffer,tags);
++      else
++              return yaffs_TagsCompatabilityWriteChunkWithTagsToNAND(dev,chunkInNAND,buffer,tags);
++}
++
++static Y_INLINE int yaffs_MarkBlockBad(yaffs_Device *dev, int blockNo)
++{
++      blockNo -= dev->blockOffset;
++      
++      if(dev->markNANDBlockBad)
++              return dev->markNANDBlockBad(dev,blockNo);
++      else
++              return yaffs_TagsCompatabilityMarkNANDBlockBad(dev,blockNo);
++}
++static Y_INLINE int yaffs_QueryInitialBlockState(yaffs_Device *dev,int blockNo, yaffs_BlockState *state, unsigned *sequenceNumber)
++{
++      blockNo -= dev->blockOffset;
++      
++      if(dev->queryNANDBlock)
++              return dev->queryNANDBlock(dev,blockNo,state,sequenceNumber);
++      else
++              return yaffs_TagsCompatabilityQueryNANDBlock(dev,blockNo,state,sequenceNumber);
++}
++
++static int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,int blockInNAND)
++{
++      int result;
++      
++      blockInNAND -= dev->blockOffset;
++
++      dev->nBlockErasures++;
++      result = dev->eraseBlockInNAND(dev,blockInNAND);
++
++      if(!result)result = dev->eraseBlockInNAND(dev,blockInNAND); // If at first we don't succeed, try again *once*.
++      return result;
++}
++
++static int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev)
++{
++      return dev->initialiseNAND(dev);
++}
++
++
++
++
++// Temporary buffer manipulations
++
++static __u8 *yaffs_GetTempBuffer(yaffs_Device *dev,int lineNo)
++{
++      int i,j;
++      for(i = 0; i < YAFFS_N_TEMP_BUFFERS; i++)
++      {
++              if(dev->tempBuffer[i].line == 0)
++              {
++                      dev->tempBuffer[i].line = lineNo;
++                      if((i+1) > dev->maxTemp)
++                      {
++                              dev->maxTemp = i + 1;
++                              for(j = 0; j <= i; j++)
++                                      dev->tempBuffer[j].maxLine = dev->tempBuffer[j].line;
++                      }       
++                      
++                      return dev->tempBuffer[i].buffer;
++              }
++      }
++
++      T(YAFFS_TRACE_BUFFERS,(TSTR("Out of temp buffers at line %d, other held by lines:"),lineNo));
++      for(i = 0; i < YAFFS_N_TEMP_BUFFERS; i++)
++      {
++              T(YAFFS_TRACE_BUFFERS,(TSTR(" %d "),dev->tempBuffer[i].line));
++      }
++      T(YAFFS_TRACE_BUFFERS,(TSTR(" "TENDSTR)));
++      
++      dev->unmanagedTempAllocations++;
++      // Get an unmanaged one
++      return YMALLOC(dev->nBytesPerChunk);    
++      
++      
++}
++
++static void yaffs_ReleaseTempBuffer(yaffs_Device *dev, __u8 *buffer, int lineNo)
++{
++      int i;
++      for(i = 0; i < YAFFS_N_TEMP_BUFFERS; i++)
++      {
++              if(dev->tempBuffer[i].buffer == buffer)
++              {
++                      dev->tempBuffer[i].line = 0;
++                      return;
++              }
++      }
++      
++      if(buffer)
++      {
++              // assume it is an unmanaged one.
++              T(YAFFS_TRACE_BUFFERS,(TSTR("Releasing unmanaged temp buffer in line %d" TENDSTR),lineNo));
++              YFREE(buffer);
++              dev->unmanagedTempDeallocations++;
++      }
++
++}
++
++
++// Chunk bitmap manipulations
++
++static Y_INLINE __u8 *yaffs_BlockBits(yaffs_Device *dev, int blk)
++{
++      if(blk < dev->internalStartBlock || blk > dev->internalEndBlock)
++      {
++              T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs: BlockBits block %d is not valid" TENDSTR),blk));
++              YBUG();
++      }
++      return dev->chunkBits + (dev->chunkBitmapStride * (blk - dev->internalStartBlock));
++}
++
++static Y_INLINE void yaffs_ClearChunkBits(yaffs_Device *dev,int blk)
++{
++      __u8 *blkBits = yaffs_BlockBits(dev,blk);
++
++       memset(blkBits,0,dev->chunkBitmapStride);
++}
++
++static Y_INLINE void yaffs_ClearChunkBit(yaffs_Device *dev,int blk,int chunk)
++{
++      __u8 *blkBits = yaffs_BlockBits(dev,blk);
++      
++      blkBits[chunk/8] &=  ~ (1<<(chunk & 7));
++}
++
++static Y_INLINE void yaffs_SetChunkBit(yaffs_Device *dev,int blk,int chunk)
++{
++      __u8 *blkBits = yaffs_BlockBits(dev,blk);
++
++      blkBits[chunk/8] |=   (1<<(chunk & 7));
++}
++
++static Y_INLINE int yaffs_CheckChunkBit(yaffs_Device *dev,int blk,int chunk)
++{
++      __u8 *blkBits = yaffs_BlockBits(dev,blk);
++      return (blkBits[chunk/8] &  (1<<(chunk & 7))) ? 1 :0;
++}
++
++static Y_INLINE int yaffs_StillSomeChunkBits(yaffs_Device *dev,int blk)
++{
++      __u8 *blkBits = yaffs_BlockBits(dev,blk);
++      int i;
++      for(i = 0; i < dev->chunkBitmapStride; i++)
++      {
++              if(*blkBits) return 1;
++              blkBits++;
++      }
++      return 0;
++}
++
++
++static  Y_INLINE int yaffs_HashFunction(int n)
++{
++      return (n % YAFFS_NOBJECT_BUCKETS);
++}
++
++
++yaffs_Object *yaffs_Root(yaffs_Device *dev)
++{
++      return dev->rootDir;
++}
++
++yaffs_Object *yaffs_LostNFound(yaffs_Device *dev)
++{
++      return dev->lostNFoundDir;
++}
++
++
++
++
++int yaffs_CheckFF(__u8 *buffer,int nBytes)
++{
++      //Horrible, slow implementation
++      while(nBytes--)
++      {
++              if(*buffer != 0xFF) return 0;
++              buffer++;
++      }
++      return 1;
++}
++
++static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND)
++{
++
++      int retval = YAFFS_OK;
++      __u8 *data = yaffs_GetTempBuffer(dev,__LINE__);
++      yaffs_ExtendedTags tags;
++
++// NCB        dev->readChunkWithTagsFromNAND(dev,chunkInNAND,data,&tags);
++      yaffs_ReadChunkWithTagsFromNAND(dev,chunkInNAND,data,&tags);
++      
++      if(!yaffs_CheckFF(data,dev->nBytesPerChunk) || tags.chunkUsed)
++      {
++              T(YAFFS_TRACE_NANDACCESS,(TSTR("Chunk %d not erased" TENDSTR),chunkInNAND));
++              retval = YAFFS_FAIL;
++      }
++
++      yaffs_ReleaseTempBuffer(dev,data,__LINE__);
++      
++      return retval;
++      
++}
++
++
++
++#if 1
++static int yaffs_WriteNewChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev, const __u8 *data, yaffs_ExtendedTags *tags,int useReserve)
++{
++      int chunk;
++      
++      int writeOk = 1;
++      int attempts = 0;
++      
++
++      
++      do{
++              chunk = yaffs_AllocateChunk(dev,useReserve);
++      
++              if(chunk >= 0)
++              {
++
++                      // First check this chunk is erased...
++#ifndef CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK
++                      writeOk = yaffs_CheckChunkErased(dev,chunk);
++#endif                
++                      if(!writeOk)
++                      {
++                              T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs chunk %d was not erased" TENDSTR),chunk));
++                      }
++                      else
++                      {
++                              writeOk =  yaffs_WriteChunkWithTagsToNAND(dev,chunk,data,tags);
++                      }
++                      attempts++;
++
++                      if(writeOk)
++                      {
++                              // Copy the data into the robustification buffer.
++                              // NB We do this at the end to prevent duplicates in the case of a write error.
++                              //Todo
++                              yaffs_HandleWriteChunkOk(dev,chunk,data,tags);
++                      }
++                      else
++                      {
++                              yaffs_HandleWriteChunkError(dev,chunk);
++                      }
++              }
++              
++      } while(chunk >= 0 && ! writeOk);
++      
++      if(attempts > 1)
++      {
++              T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs write required %d attempts" TENDSTR),attempts));
++              dev->nRetriedWrites+= (attempts - 1);   
++      }
++      
++
++      
++      return chunk;
++}
++#endif
++
++
++///
++// Functions for robustisizing
++//
++//
++
++static void yaffs_RetireBlock(yaffs_Device *dev,int blockInNAND)
++{
++      
++      yaffs_MarkBlockBad(dev,blockInNAND);
++      
++      yaffs_GetBlockInfo(dev,blockInNAND)->blockState = YAFFS_BLOCK_STATE_DEAD;
++
++      dev->nRetiredBlocks++;
++}
++
++
++#if 0
++static int yaffs_RewriteBufferedBlock(yaffs_Device *dev)
++{
++      dev->doingBufferedBlockRewrite = 1;
++      //
++      //      Remove erased chunks
++      //  Rewrite existing chunks to a new block
++      //      Set current write block to the new block
++      
++      dev->doingBufferedBlockRewrite = 0;
++      
++      return 1;
++}
++
++
++static void yaffs_HandleReadDataError(yaffs_Device *dev,int chunkInNAND)
++{
++      int blockInNAND = chunkInNAND/dev->nChunksPerBlock;
++
++      // Mark the block for retirement
++      yaffs_GetBlockInfo(dev,blockInNAND)->needsRetiring = 1;
++      T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>>Block %d marked for retirement" TENDSTR),blockInNAND));
++
++
++      //TODO  
++      // Just do a garbage collection on the affected block then retire the block
++      // NB recursion
++}
++
++
++static void yaffs_CheckWrittenBlock(yaffs_Device *dev,int chunkInNAND)
++{
++}
++
++#endif
++
++static void yaffs_HandleWriteChunkOk(yaffs_Device *dev,int chunkInNAND,const __u8 *data, const yaffs_ExtendedTags *tags)
++{
++}
++
++static void yaffs_HandleUpdateChunk(yaffs_Device *dev,int chunkInNAND, const yaffs_ExtendedTags *tags)
++{
++}
++
++static void yaffs_HandleWriteChunkError(yaffs_Device *dev,int chunkInNAND)
++{
++      int blockInNAND = chunkInNAND/dev->nChunksPerBlock;
++
++      // Mark the block for retirement
++      yaffs_GetBlockInfo(dev,blockInNAND)->needsRetiring = 1;
++      // Delete the chunk
++      yaffs_DeleteChunk(dev,chunkInNAND,1,__LINE__);
++}
++
++
++
++#if 0
++static int yaffs_VerifyCompare(const __u8 *d0, const __u8 * d1, const yaffs_Spare *s0, const yaffs_Spare *s1,int dataSize)
++{
++
++
++      if( memcmp(d0,d1,dataSize) != 0 ||
++              s0->tagByte0 != s1->tagByte0 ||
++              s0->tagByte1 != s1->tagByte1 ||
++              s0->tagByte2 != s1->tagByte2 ||
++              s0->tagByte3 != s1->tagByte3 ||
++              s0->tagByte4 != s1->tagByte4 ||
++              s0->tagByte5 != s1->tagByte5 ||
++              s0->tagByte6 != s1->tagByte6 ||
++              s0->tagByte7 != s1->tagByte7 ||
++              s0->ecc1[0]  != s1->ecc1[0]  ||
++              s0->ecc1[1]  != s1->ecc1[1]  ||
++              s0->ecc1[2]  != s1->ecc1[2]  ||
++              s0->ecc2[0]  != s1->ecc2[0]  ||
++              s0->ecc2[1]  != s1->ecc2[1]  ||
++              s0->ecc2[2]  != s1->ecc2[2] )
++              {
++                      return 0;
++              }
++      
++      return 1;
++}
++#endif
++
++
++///////////////////////// Object management //////////////////
++// List of spare objects
++// The list is hooked together using the first pointer
++// in the object
++
++// static yaffs_Object *yaffs_freeObjects = NULL;
++
++// static int yaffs_nFreeObjects;
++
++// static yaffs_ObjectList *yaffs_allocatedObjectList = NULL;
++
++// static yaffs_ObjectBucket yaffs_objectBucket[YAFFS_NOBJECT_BUCKETS];
++
++
++static __u16 yaffs_CalcNameSum(const YCHAR *name)
++{
++      __u16 sum = 0;
++      __u16 i = 1;
++      
++      YUCHAR *bname = (YUCHAR *)name;
++      if(bname)
++      {
++              while ((*bname) && (i <=YAFFS_MAX_NAME_LENGTH))
++              {
++
++#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
++                      sum += yaffs_toupper(*bname) * i;
++#else
++                      sum += (*bname) * i;
++#endif
++                      i++;
++                      bname++;
++              }
++      }
++      return sum;
++}
++
++static void yaffs_SetObjectName(yaffs_Object *obj, const YCHAR *name)
++{
++#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM
++                                      if(name && yaffs_strlen(name) <= YAFFS_SHORT_NAME_LENGTH)
++                                      {
++                                              yaffs_strcpy(obj->shortName,name);
++                                      }
++                                      else
++                                      {
++                                              obj->shortName[0]=_Y('\0');
++                                      }
++#endif
++                                      obj->sum = yaffs_CalcNameSum(name);
++}
++
++#if 0
++void yaffs_CalcECC(const __u8 *data, yaffs_Spare *spare)
++{
++      yaffs_ECCCalculate(data , spare->ecc1);
++      yaffs_ECCCalculate(&data[256] , spare->ecc2);
++}
++#endif
++
++
++///////////////////////// TNODES ///////////////////////
++
++// List of spare tnodes
++// The list is hooked together using the first pointer
++// in the tnode.
++
++//static yaffs_Tnode *yaffs_freeTnodes = NULL;
++
++// static int yaffs_nFreeTnodes;
++
++//static yaffs_TnodeList *yaffs_allocatedTnodeList = NULL;
++
++
++
++// yaffs_CreateTnodes creates a bunch more tnodes and
++// adds them to the tnode free list.
++// Don't use this function directly
++
++static int yaffs_CreateTnodes(yaffs_Device *dev,int nTnodes)
++{
++    int i;
++    yaffs_Tnode *newTnodes;
++    yaffs_TnodeList *tnl;
++    
++    if(nTnodes < 1) return YAFFS_OK;
++   
++      // make these things
++      
++    newTnodes = YMALLOC(nTnodes * sizeof(yaffs_Tnode));
++   
++    if (!newTnodes)
++    {
++              T(YAFFS_TRACE_ERROR,(TSTR("yaffs: Could not allocate Tnodes"TENDSTR)));
++              return YAFFS_FAIL;
++    }
++    
++    // Hook them into the free list
++    for(i = 0; i < nTnodes - 1; i++)
++    {
++      newTnodes[i].internal[0] = &newTnodes[i+1];
++#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
++      newTnodes[i].internal[YAFFS_NTNODES_INTERNAL] = (void *)1;
++#endif
++    }
++      
++      newTnodes[nTnodes - 1].internal[0] = dev->freeTnodes;
++#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
++      newTnodes[nTnodes - 1].internal[YAFFS_NTNODES_INTERNAL] = (void *)1;
++#endif
++      dev->freeTnodes = newTnodes;
++      dev->nFreeTnodes+= nTnodes;
++      dev->nTnodesCreated += nTnodes;
++
++      // Now add this bunch of tnodes to a list for freeing up.
++      // NB If we can't add this to the management list it isn't fatal
++      // but it just means we can't free this bunch of tnodes later.
++      tnl = YMALLOC(sizeof(yaffs_TnodeList));
++      if(!tnl)
++      {
++              T(YAFFS_TRACE_ERROR,(TSTR("yaffs: Could not add tnodes to management list" TENDSTR)));
++              
++      }
++      else
++      {
++              tnl->tnodes = newTnodes;
++              tnl->next = dev->allocatedTnodeList;
++              dev->allocatedTnodeList = tnl;
++      }
++
++
++      T(YAFFS_TRACE_ALLOCATE,(TSTR("yaffs: Tnodes added" TENDSTR)));
++
++
++      return YAFFS_OK;
++}
++
++
++// GetTnode gets us a clean tnode. Tries to make allocate more if we run out
++static yaffs_Tnode *yaffs_GetTnode(yaffs_Device *dev)
++{
++      yaffs_Tnode *tn = NULL;
++      
++      // If there are none left make more
++      if(!dev->freeTnodes)
++      {
++              yaffs_CreateTnodes(dev,YAFFS_ALLOCATION_NTNODES);
++      }
++      
++      if(dev->freeTnodes)
++      {
++              tn = dev->freeTnodes;
++#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
++      if(tn->internal[YAFFS_NTNODES_INTERNAL] != (void *)1)
++              {
++                      // Hoosterman, this thing looks like it isn't in the list
++                              T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: Tnode list bug 1" TENDSTR)));
++              }
++#endif
++              dev->freeTnodes = dev->freeTnodes->internal[0];
++              dev->nFreeTnodes--;
++              // zero out
++              memset(tn,0,sizeof(yaffs_Tnode));
++      }
++      
++
++      return tn;
++}
++
++
++// FreeTnode frees up a tnode and puts it back on the free list
++static void yaffs_FreeTnode(yaffs_Device*dev, yaffs_Tnode *tn)
++{
++      if(tn)
++      {
++#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
++      if(tn->internal[YAFFS_NTNODES_INTERNAL] != 0)
++              {
++                      // Hoosterman, this thing looks like it is already in the list
++                              T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: Tnode list bug 2" TENDSTR)));
++              }
++              tn->internal[YAFFS_NTNODES_INTERNAL] = (void *)1;
++#endif
++              tn->internal[0] = dev->freeTnodes;
++              dev->freeTnodes = tn;
++              dev->nFreeTnodes++;
++      }
++}
++
++
++static void yaffs_DeinitialiseTnodes(yaffs_Device*dev)
++{
++      // Free the list of allocated tnodes
++      yaffs_TnodeList *tmp;
++              
++      while(dev->allocatedTnodeList)
++      {
++              tmp =  dev->allocatedTnodeList->next;
++
++              YFREE(dev->allocatedTnodeList->tnodes);
++              YFREE(dev->allocatedTnodeList);
++              dev->allocatedTnodeList = tmp;
++              
++      }
++      
++      dev->freeTnodes = NULL;
++      dev->nFreeTnodes = 0;
++}
++
++static void yaffs_InitialiseTnodes(yaffs_Device*dev)
++{
++      dev->allocatedTnodeList = NULL;
++      dev->freeTnodes = NULL;
++      dev->nFreeTnodes = 0;
++      dev->nTnodesCreated = 0;
++
++}
++
++#if 0
++void yaffs_TnodeTest(yaffs_Device *dev)
++{
++
++      int i;
++      int j;
++      yaffs_Tnode *tn[1000];
++      
++      YINFO("Testing TNodes");
++      
++      for(j = 0; j < 50; j++)
++      {
++              for(i = 0; i < 1000; i++)
++              {
++                      tn[i] = yaffs_GetTnode(dev);
++                      if(!tn[i])
++                      {
++                              YALERT("Getting tnode failed");
++                      }
++              }
++              for(i = 0; i < 1000; i+=3)
++              {
++                      yaffs_FreeTnode(dev,tn[i]);
++                      tn[i] = NULL;
++              }
++              
++      }
++}
++#endif
++
++
++////////////////// END OF TNODE MANIPULATION ///////////////////////////
++
++/////////////// Functions to manipulate the look-up tree (made up of tnodes)
++// The look up tree is represented by the top tnode and the number of topLevel
++// in the tree. 0 means only the level 0 tnode is in the tree.
++
++
++// FindLevel0Tnode finds the level 0 tnode, if one exists.
++// Used when reading.....
++static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device *dev,yaffs_FileStructure *fStruct, __u32 chunkId)
++{
++      
++      yaffs_Tnode *tn = fStruct->top;
++      __u32 i;
++      int requiredTallness;   
++      int level = fStruct->topLevel;
++      
++      // Check sane level and chunk Id
++      if(level < 0 || level > YAFFS_TNODES_MAX_LEVEL)
++      {
++//            char str[50];
++//            sprintf(str,"Bad level %d",level);
++//            YALERT(str);
++              return NULL;
++      }
++      
++      if(chunkId > YAFFS_MAX_CHUNK_ID)
++      {
++//            char str[50];
++//            sprintf(str,"Bad chunkId %d",chunkId);
++//            YALERT(str);
++              return NULL;
++      }
++
++      // First check we're tall enough (ie enough topLevel)
++      
++      i = chunkId >> (/*dev->chunkGroupBits  + */YAFFS_TNODES_LEVEL0_BITS);
++      requiredTallness = 0;
++      while(i)
++      {
++              i >>= YAFFS_TNODES_INTERNAL_BITS;
++              requiredTallness++;
++      }
++      
++      
++      if(requiredTallness > fStruct->topLevel)
++      {
++              // Not tall enough, so we can't find it, return NULL.
++              return NULL;
++      }
++              
++      
++      // Traverse down to level 0
++      while (level > 0 && tn)
++      {
++          tn = tn->internal[(chunkId >>(/* dev->chunkGroupBits + */ YAFFS_TNODES_LEVEL0_BITS + (level-1) * YAFFS_TNODES_INTERNAL_BITS)) & 
++                             YAFFS_TNODES_INTERNAL_MASK]; 
++              level--;
++      
++      }
++      
++      return tn;              
++}
++
++// AddOrFindLevel0Tnode finds the level 0 tnode if it exists, otherwise first expands the tree.
++// This happens in two steps:
++//  1. If the tree isn't tall enough, then make it taller.
++//  2. Scan down the tree towards the level 0 tnode adding tnodes if required.
++//
++// Used when modifying the tree.
++//
++static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device *dev, yaffs_FileStructure *fStruct, __u32 chunkId)
++{
++      
++      yaffs_Tnode *tn; 
++      
++      int requiredTallness;
++      int i;
++      int l;
++      
++      __u32 x;
++              
++      
++      //T((TSTR("AddOrFind topLevel=%d, chunk=%d"),fStruct->topLevel,chunkId));
++      
++      // Check sane level and page Id
++      if(fStruct->topLevel < 0 || fStruct->topLevel > YAFFS_TNODES_MAX_LEVEL)
++      {
++//            char str[50];
++//            sprintf(str,"Bad level %d",fStruct->topLevel);
++//            YALERT(str);
++              return NULL;
++      }
++      
++      if(chunkId > YAFFS_MAX_CHUNK_ID)
++      {
++//            char str[50];
++//            sprintf(str,"Bad chunkId %d",chunkId);
++//            YALERT(str);
++              return NULL;
++      }
++      
++      // First check we're tall enough (ie enough topLevel)
++      
++      x = chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS);
++      requiredTallness = 0;
++      while(x)
++      {
++              x >>= YAFFS_TNODES_INTERNAL_BITS;
++              requiredTallness++;
++      }
++      
++      //T((TSTR(" required=%d"),requiredTallness));
++      
++      
++      if(requiredTallness > fStruct->topLevel)
++      {
++              // Not tall enough,gotta make the tree taller
++              for(i = fStruct->topLevel; i < requiredTallness; i++)
++              {
++                      //T((TSTR(" add new top")));
++                      
++                      tn = yaffs_GetTnode(dev);
++                      
++                      if(tn)
++                      {
++                              tn->internal[0] = fStruct->top;
++                              fStruct->top = tn;
++                      }
++                      else
++                      {
++                                      T(YAFFS_TRACE_ERROR,(TSTR("yaffs: no more tnodes" TENDSTR)));
++                      }
++              }
++              
++              fStruct->topLevel = requiredTallness;
++      }
++      
++      
++      // Traverse down to level 0, adding anything we need
++      
++      l = fStruct->topLevel;
++      tn = fStruct->top;
++      while (l > 0 && tn)
++      {
++              x = (chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS + (l-1) * YAFFS_TNODES_INTERNAL_BITS)) & 
++                             YAFFS_TNODES_INTERNAL_MASK;
++                             
++              //T((TSTR(" [%d:%d]"),l,i));
++              
++          if(!tn->internal[x])
++          {
++              //T((TSTR(" added")));
++              
++              tn->internal[x] = yaffs_GetTnode(dev);
++          }
++          
++          tn =        tn->internal[x];
++              l--;
++      
++      }
++      
++      //TSTR(TENDSTR)));
++      
++      return tn;              
++}
++
++
++static int yaffs_FindChunkInGroup(yaffs_Device *dev, int theChunk, yaffs_ExtendedTags *tags, int objectId, int chunkInInode)
++{
++      int j;
++      
++                                      
++      for(j = 0; theChunk && j < dev->chunkGroupSize; j++)
++      {
++              if(yaffs_CheckChunkBit(dev,theChunk / dev->nChunksPerBlock,theChunk % dev->nChunksPerBlock))
++              {
++                      yaffs_ReadChunkWithTagsFromNAND(dev,theChunk,NULL,tags);
++                      if(yaffs_TagsMatch(tags,objectId,chunkInInode))
++                      {
++                              // found it;
++                              return theChunk;
++                                      
++                      }
++              }
++              theChunk++;
++      }
++      return -1;
++}
++
++// DeleteWorker scans backwards through the tnode tree and deletes all the
++// chunks and tnodes in the file
++// Returns 1 if the tree was deleted. Returns 0 if it stopped early due to hitting the limit and the delete is incomplete.
++
++static int yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset,int *limit)
++{
++      int i;
++      int chunkInInode;
++      int theChunk;
++      yaffs_ExtendedTags tags;
++      int foundChunk;
++      yaffs_Device *dev = in->myDev;
++
++      int allDone = 1;
++      
++      
++      if(tn)
++      {
++              if(level > 0)
++              {
++              
++                      for(i = YAFFS_NTNODES_INTERNAL -1; allDone && i >= 0; i--)
++                      {
++                          if(tn->internal[i])
++                      {
++                                      if(limit && (*limit) < 0)
++                                      {
++                                              allDone = 0;
++                                      }
++                                      else
++                                      {
++                                              allDone = yaffs_DeleteWorker(in,tn->internal[i],level - 1,
++                                                                              (chunkOffset << YAFFS_TNODES_INTERNAL_BITS ) + i ,limit);
++                                      }
++                                      if(allDone)
++                                      {
++                                              yaffs_FreeTnode(dev,tn->internal[i]);
++                                      tn->internal[i] = NULL;
++                                      }
++                          }
++                  
++                      }
++                      return (allDone) ? 1 : 0;
++              }
++              else if(level == 0)
++              {
++                      int hitLimit = 0;
++                      
++                      for(i = YAFFS_NTNODES_LEVEL0 -1; i >= 0 && !hitLimit; i--)
++                      {
++                          if(tn->level0[i])
++                      {
++                                      
++                                      chunkInInode = (chunkOffset << YAFFS_TNODES_LEVEL0_BITS ) + i;
++                                      
++                                      theChunk =  tn->level0[i] << dev->chunkGroupBits;
++                                      
++                                      foundChunk = yaffs_FindChunkInGroup(dev,theChunk,&tags,in->objectId,chunkInInode);
++                                      
++                                      if(foundChunk > 0)
++                                      {
++                                              yaffs_DeleteChunk(dev,foundChunk,1,__LINE__);
++                                              in->nDataChunks--;
++                                              if(limit)
++                                              { 
++                                                      *limit = *limit-1;
++                                                      if(*limit <= 0) 
++                                                      { 
++                                                              hitLimit = 1;
++                                                      }
++                                              }
++                                      
++                                      }
++                                      
++                              tn->level0[i] = 0;
++                          }
++                  
++                      }
++                      return (i < 0) ? 1 : 0;
++
++                      
++              }
++              
++      }
++      
++      return 1;
++      
++}
++
++
++static void yaffs_SoftDeleteChunk(yaffs_Device *dev, int chunk)
++{
++
++      yaffs_BlockInfo *theBlock;                                      
++      
++      T(YAFFS_TRACE_DELETION,(TSTR("soft delete chunk %d" TENDSTR),chunk));
++                                              
++      theBlock =      yaffs_GetBlockInfo(dev,  chunk/dev->nChunksPerBlock);
++      if(theBlock)
++      {
++              theBlock->softDeletions++;
++              dev->nFreeChunks++;
++      }
++}
++
++// SoftDeleteWorker scans backwards through the tnode tree and soft deletes all the chunks in the file.
++// All soft deleting does is increment the block's softdelete count and pulls the chunk out
++// of the tnode.
++// THus, essentially this is the same as DeleteWorker except that the chunks are soft deleted.
++//
++static int yaffs_SoftDeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset)
++{
++      int i;
++      int theChunk;
++      int allDone = 1;
++      yaffs_Device *dev = in->myDev;
++      
++      
++      if(tn)
++      {
++              if(level > 0)
++              {
++              
++                      for(i = YAFFS_NTNODES_INTERNAL -1; allDone && i >= 0; i--)
++                      {
++                          if(tn->internal[i])
++                      {
++                                              allDone = yaffs_SoftDeleteWorker(in,tn->internal[i],level - 1,
++                                                                              (chunkOffset << YAFFS_TNODES_INTERNAL_BITS ) + i);
++                                      if(allDone)
++                                      {
++                                              yaffs_FreeTnode(dev,tn->internal[i]);
++                                      tn->internal[i] = NULL;
++                                      }
++                                      else
++                                      {
++                                              //Hoosterman... how could this happen.
++                                      }                           
++                              }                   
++                      }
++                      return (allDone) ? 1 : 0;
++              }
++              else if(level == 0)
++              {
++                      
++                      for(i = YAFFS_NTNODES_LEVEL0 -1; i >=0; i--)
++                      {
++                          if(tn->level0[i])
++                      {
++                                      // Note this does not find the real chunk, only the chunk group.
++                                      // We make an assumption that a chunk group is niot larger than a block.
++                                      theChunk =  (tn->level0[i] << dev->chunkGroupBits);
++                                      
++                                      yaffs_SoftDeleteChunk(dev,theChunk);
++                              tn->level0[i] = 0;
++                          }
++                  
++                      }
++                      return 1;
++                      
++              }
++              
++      }
++      
++      return 1;
++              
++}
++
++
++
++static void yaffs_SoftDeleteFile(yaffs_Object *obj)
++{
++      if(obj->deleted &&
++         obj->variantType == YAFFS_OBJECT_TYPE_FILE &&
++         !obj->softDeleted)
++      {
++              if(obj->nDataChunks <= 0)
++              {
++                              // Empty file with no duplicate object headers, just delete it immediately
++                              yaffs_FreeTnode(obj->myDev,obj->variant.fileVariant.top);
++                              obj->variant.fileVariant.top = NULL;
++                              T(YAFFS_TRACE_TRACING,(TSTR("yaffs: Deleting empty file %d" TENDSTR),obj->objectId));
++                              yaffs_DoGenericObjectDeletion(obj);     
++              }
++              else
++              {
++                      yaffs_SoftDeleteWorker(obj, obj->variant.fileVariant.top, obj->variant.fileVariant.topLevel, 0);
++                      obj->softDeleted = 1;
++              }
++      }
++}
++
++
++
++
++
++// Pruning removes any part of the file structure tree that is beyond the
++// bounds of the file (ie that does not point to chunks).
++//
++// A file should only get pruned when its size is reduced.
++//
++// Before pruning, the chunks must be pulled from the tree and the
++// level 0 tnode entries must be zeroed out.
++// Could also use this for file deletion, but that's probably better handled
++// by a special case.
++
++// yaffs_PruneWorker should only be called by yaffs_PruneFileStructure()
++
++static yaffs_Tnode *yaffs_PruneWorker(yaffs_Device *dev, yaffs_Tnode *tn, __u32 level, int del0)
++{
++      int i;
++      int hasData;
++      
++      if(tn)
++      {
++              hasData = 0;
++              
++              for(i = 0; i < YAFFS_NTNODES_INTERNAL; i++)
++              {
++                  if(tn->internal[i] && level > 0)
++                  {
++                      tn->internal[i] = yaffs_PruneWorker(dev,tn->internal[i],level - 1, ( i == 0) ? del0 : 1);
++                  }
++                  
++                  if(tn->internal[i])
++                  {
++                      hasData++;
++                      }
++              }
++              
++              if(hasData == 0 && del0)
++              {
++                      // Free and return NULL
++                      
++                      yaffs_FreeTnode(dev,tn);
++                      tn = NULL;
++              }
++              
++      }
++
++      return tn;
++      
++}
++
++static int yaffs_PruneFileStructure(yaffs_Device *dev, yaffs_FileStructure *fStruct)
++{
++      int i;
++      int hasData;
++      int done = 0;
++      yaffs_Tnode *tn;
++      
++      if(fStruct->topLevel > 0)
++      {
++              fStruct->top = yaffs_PruneWorker(dev,fStruct->top, fStruct->topLevel,0);
++              
++              // Now we have a tree with all the non-zero branches NULL but the height
++              // is the same as it was.
++              // Let's see if we can trim internal tnodes to shorten the tree.
++              // We can do this if only the 0th element in the tnode is in use 
++              // (ie all the non-zero are NULL)
++              
++              while(fStruct->topLevel && !done)
++              {
++                      tn = fStruct->top;
++                      
++                      hasData = 0;
++                      for(i = 1; i <YAFFS_NTNODES_INTERNAL; i++)
++                      {
++                              if(tn->internal[i])
++                      {
++                              hasData++;
++                              }
++                      }
++                      
++                      if(!hasData)
++                      {
++                              fStruct->top = tn->internal[0];
++                              fStruct->topLevel--;
++                              yaffs_FreeTnode(dev,tn);
++                      }
++                      else
++                      {
++                              done = 1;
++                      }
++              }
++      }
++      
++      return YAFFS_OK;
++}
++
++
++
++
++
++/////////////////////// End of File Structure functions. /////////////////
++
++// yaffs_CreateFreeObjects creates a bunch more objects and
++// adds them to the object free list.
++static int yaffs_CreateFreeObjects(yaffs_Device *dev, int nObjects)
++{
++    int i;
++    yaffs_Object *newObjects;
++    yaffs_ObjectList *list;
++    
++    if(nObjects < 1) return YAFFS_OK;
++   
++      // make these things
++      
++    newObjects = YMALLOC(nObjects * sizeof(yaffs_Object));
++   
++    if (!newObjects)
++    {
++              T(YAFFS_TRACE_ALLOCATE,(TSTR("yaffs: Could not allocate more objects" TENDSTR)));
++              return YAFFS_FAIL;
++    }
++    
++    // Hook them into the free list
++    for(i = 0; i < nObjects - 1; i++)
++    {
++      newObjects[i].siblings.next = (struct list_head *)(&newObjects[i+1]);
++    }
++      
++      newObjects[nObjects - 1].siblings.next = (void *)dev->freeObjects;
++      dev->freeObjects = newObjects;
++      dev->nFreeObjects+= nObjects;
++      dev->nObjectsCreated+= nObjects;
++      
++      // Now add this bunch of Objects to a list for freeing up.
++      
++      list = YMALLOC(sizeof(yaffs_ObjectList));
++      if(!list)
++      {
++              T(YAFFS_TRACE_ALLOCATE,(TSTR("Could not add objects to management list" TENDSTR)));
++      }
++      else
++      {
++              list->objects = newObjects;
++              list->next = dev->allocatedObjectList;
++              dev->allocatedObjectList = list;
++      }
++      
++      
++      
++      return YAFFS_OK;
++}
++
++
++// AllocateEmptyObject gets us a clean Object. Tries to make allocate more if we run out
++static yaffs_Object *yaffs_AllocateEmptyObject(yaffs_Device *dev)
++{
++      yaffs_Object *tn = NULL;
++      
++      // If there are none left make more
++      if(!dev->freeObjects)
++      {
++              yaffs_CreateFreeObjects(dev,YAFFS_ALLOCATION_NOBJECTS);
++      }
++      
++      if(dev->freeObjects)
++      {
++              tn = dev->freeObjects;
++              dev->freeObjects = (yaffs_Object *)(dev->freeObjects->siblings.next);
++              dev->nFreeObjects--;
++              
++              // Now sweeten it up...
++      
++              memset(tn,0,sizeof(yaffs_Object));
++              tn->myDev = dev;
++              tn->chunkId = -1;
++              tn->variantType = YAFFS_OBJECT_TYPE_UNKNOWN;
++              INIT_LIST_HEAD(&(tn->hardLinks));
++              INIT_LIST_HEAD(&(tn->hashLink));
++              INIT_LIST_HEAD(&tn->siblings);
++              
++              // Add it to the lost and found directory.
++              // NB Can't put root or lostNFound in lostNFound so
++              // check if lostNFound exists first
++              if(dev->lostNFoundDir)
++              {
++                      yaffs_AddObjectToDirectory(dev->lostNFoundDir,tn);      
++              }
++      }
++      
++
++      return tn;
++}
++
++static yaffs_Object *yaffs_CreateFakeDirectory(yaffs_Device *dev,int number,__u32 mode)
++{
++
++      yaffs_Object *obj = yaffs_CreateNewObject(dev,number,YAFFS_OBJECT_TYPE_DIRECTORY);              
++      if(obj)
++      {
++              obj->fake = 1;                  // it is fake so it has no NAND presence...
++              obj->renameAllowed= 0;  // ... and we're not allowed to rename it...
++              obj->unlinkAllowed= 0;  // ... or unlink it
++              obj->deleted = 0;
++              obj->unlinked = 0;
++              obj->yst_mode = mode;
++              obj->myDev = dev;
++              obj->chunkId = 0; // Not a valid chunk.
++      }
++      
++      return obj;
++      
++}
++
++
++static void yaffs_UnhashObject(yaffs_Object *tn)
++{
++      int bucket;
++      yaffs_Device *dev = tn->myDev;
++      
++      
++      // If it is still linked into the bucket list, free from the list
++      if(!list_empty(&tn->hashLink))
++      {
++              list_del_init(&tn->hashLink);
++              bucket =  yaffs_HashFunction(tn->objectId);
++              dev->objectBucket[bucket].count--;
++      }
++      
++}
++
++
++// FreeObject frees up a Object and puts it back on the free list
++static void yaffs_FreeObject(yaffs_Object *tn)
++{
++
++      yaffs_Device *dev = tn->myDev;
++      
++#ifdef  __KERNEL__
++      if(tn->myInode)
++      {
++              // We're still hooked up to a cached inode.
++              // Don't delete now, but mark for later deletion
++              tn->deferedFree = 1;
++              return;
++      }
++#endif
++      
++      yaffs_UnhashObject(tn);
++      
++      // Link into the free list.
++      tn->siblings.next = (struct list_head *)(dev->freeObjects);
++      dev->freeObjects = tn;
++      dev->nFreeObjects++;
++}
++
++
++
++#ifdef __KERNEL__
++
++void yaffs_HandleDeferedFree(yaffs_Object *obj)
++{
++      if(obj->deferedFree)
++      {
++         yaffs_FreeObject(obj);
++      }
++}
++
++#endif
++
++
++
++static void yaffs_DeinitialiseObjects(yaffs_Device *dev)
++{
++      // Free the list of allocated Objects
++      
++      yaffs_ObjectList *tmp;
++      
++      while( dev->allocatedObjectList)
++      {
++              tmp =  dev->allocatedObjectList->next;
++              YFREE(dev->allocatedObjectList->objects);
++              YFREE(dev->allocatedObjectList);
++              
++              dev->allocatedObjectList =  tmp;
++      }
++      
++      dev->freeObjects = NULL;
++      dev->nFreeObjects = 0;
++}
++
++static void yaffs_InitialiseObjects(yaffs_Device *dev)
++{
++      int i;
++      
++      dev->allocatedObjectList = NULL;
++      dev->freeObjects = NULL;
++      dev->nFreeObjects = 0;
++      
++      for(i = 0; i < YAFFS_NOBJECT_BUCKETS; i++)
++      {
++              INIT_LIST_HEAD(&dev->objectBucket[i].list);
++              dev->objectBucket[i].count = 0; 
++      }
++
++}
++
++
++
++
++
++
++static int yaffs_FindNiceObjectBucket(yaffs_Device *dev)
++{
++      static int x = 0;
++      int i;
++      int l = 999;
++      int lowest = 999999;
++
++              
++      // First let's see if we can find one that's empty.
++      
++      for(i = 0; i < 10 && lowest > 0; i++)
++       {
++              x++;
++              x %=  YAFFS_NOBJECT_BUCKETS;
++              if(dev->objectBucket[x].count < lowest)
++              {
++                      lowest = dev->objectBucket[x].count;
++                      l = x;
++              }
++              
++      }
++      
++      // If we didn't find an empty list, then try
++      // looking a bit further for a short one
++      
++      for(i = 0; i < 10 && lowest > 3; i++)
++       {
++              x++;
++              x %=  YAFFS_NOBJECT_BUCKETS;
++              if(dev->objectBucket[x].count < lowest)
++              {
++                      lowest = dev->objectBucket[x].count;
++                      l = x;
++              }
++              
++      }
++      
++      return l;
++}
++
++static int yaffs_CreateNewObjectNumber(yaffs_Device *dev)
++{
++      int bucket = yaffs_FindNiceObjectBucket(dev);
++      
++      // Now find an object value that has not already been taken
++      // by scanning the list.
++      
++      int found = 0;
++      struct list_head *i;
++      
++      __u32 n = (__u32)bucket;
++
++      //yaffs_CheckObjectHashSanity();        
++      
++      while(!found)
++      {
++              found = 1;
++              n +=  YAFFS_NOBJECT_BUCKETS;
++              if(1 ||dev->objectBucket[bucket].count > 0)
++              {
++                      list_for_each(i,&dev->objectBucket[bucket].list)
++                      {
++                              // If there is already one in the list
++                              if(i && list_entry(i, yaffs_Object,hashLink)->objectId == n)
++                              {
++                                      found = 0;
++                              }
++                      }
++              }
++      }
++      
++      //T(("bucket %d count %d inode %d\n",bucket,yaffs_objectBucket[bucket].count,n);
++      
++      return n;       
++}
++
++static void yaffs_HashObject(yaffs_Object *in)
++{
++      int bucket = yaffs_HashFunction(in->objectId);
++      yaffs_Device *dev = in->myDev;
++      
++      if(!list_empty(&in->hashLink))
++      {
++              //YINFO("!!!");
++      }
++
++      
++      list_add(&in->hashLink,&dev->objectBucket[bucket].list);
++      dev->objectBucket[bucket].count++;
++
++}
++
++yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device *dev,__u32 number)
++{
++      int bucket = yaffs_HashFunction(number);
++      struct list_head *i;
++      yaffs_Object *in;
++      
++      list_for_each(i,&dev->objectBucket[bucket].list)
++      {
++              // Look if it is in the list
++              if(i)
++              {
++                      in = list_entry(i, yaffs_Object,hashLink);
++                      if(in->objectId == number)
++                      {
++#ifdef __KERNEL__
++                              // Don't tell the VFS about this one if it is defered free
++                              if(in->deferedFree)
++                                return NULL;
++#endif
++                                
++                              return in;
++                      }
++              }
++      }
++      
++      return NULL;
++}
++
++
++
++yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev,int number,yaffs_ObjectType type)
++{
++              
++      yaffs_Object *theObject;
++
++      if(number < 0)
++      {
++              number = yaffs_CreateNewObjectNumber(dev);
++      }
++      
++      theObject = yaffs_AllocateEmptyObject(dev);
++      
++      if(theObject)
++      {
++              theObject->fake = 0;
++              theObject->renameAllowed = 1;
++              theObject->unlinkAllowed = 1;
++              theObject->objectId = number;
++              yaffs_HashObject(theObject);
++              theObject->variantType = type;
++#ifdef CONFIG_YAFFS_WINCE
++              yfsd_WinFileTimeNow(theObject->win_atime);
++              theObject->win_ctime[0] = theObject->win_mtime[0] = theObject->win_atime[0];
++              theObject->win_ctime[1] = theObject->win_mtime[1] = theObject->win_atime[1];
++
++#else
++
++              theObject->yst_atime = theObject->yst_mtime = theObject->yst_ctime = Y_CURRENT_TIME;            
++#endif
++              switch(type)
++              {
++                      case YAFFS_OBJECT_TYPE_FILE: 
++                              theObject->variant.fileVariant.fileSize = 0;
++                              theObject->variant.fileVariant.scannedFileSize = 0;
++                              theObject->variant.fileVariant.shrinkSize = 0xFFFFFFFF; // max __u32
++                              theObject->variant.fileVariant.topLevel = 0;
++                              theObject->variant.fileVariant.top  = yaffs_GetTnode(dev);
++                              break;
++                      case YAFFS_OBJECT_TYPE_DIRECTORY:
++                              INIT_LIST_HEAD(&theObject->variant.directoryVariant.children);
++                              break;
++                      case YAFFS_OBJECT_TYPE_SYMLINK:
++                              // No action required
++                              break;
++                      case YAFFS_OBJECT_TYPE_HARDLINK:
++                              // No action required
++                              break;
++                      case YAFFS_OBJECT_TYPE_SPECIAL:
++                              // No action required
++                              break;
++                      case YAFFS_OBJECT_TYPE_UNKNOWN:
++                              // todo this should not happen
++                              break;
++              }
++      }
++      
++      return theObject;
++}
++
++static yaffs_Object *yaffs_FindOrCreateObjectByNumber(yaffs_Device *dev, int number,yaffs_ObjectType type)
++{
++      yaffs_Object *theObject = NULL;
++      
++      if(number > 0)
++      {
++              theObject = yaffs_FindObjectByNumber(dev,number);
++      }
++      
++      if(!theObject)
++      {
++              theObject = yaffs_CreateNewObject(dev,number,type);
++      }
++      
++      return theObject;
++
++}
++
++static YCHAR *yaffs_CloneString(const YCHAR *str)
++{
++      YCHAR *newStr = NULL;
++      
++      if(str && *str)
++      {
++              newStr = YMALLOC((yaffs_strlen(str) + 1) * sizeof(YCHAR));
++              yaffs_strcpy(newStr,str);
++      }
++
++      return newStr;
++      
++}
++
++//
++// Mknod (create) a new object.
++// equivalentObject only has meaning for a hard link;
++// aliasString only has meaning for a sumlink.
++// rdev only has meaning for devices (a subset of special objects)
++static yaffs_Object *yaffs_MknodObject( yaffs_ObjectType type,
++                                                               yaffs_Object *parent,
++                                                               const YCHAR *name, 
++                                                               __u32 mode,
++                                                               __u32 uid,
++                                                               __u32 gid,
++                                                               yaffs_Object *equivalentObject,
++                                                               const YCHAR *aliasString,
++                                                               __u32 rdev)
++{
++      yaffs_Object *in;
++
++      yaffs_Device *dev = parent->myDev;
++      
++      // Check if the entry exists. If it does then fail the call since we don't want a dup.
++      if(yaffs_FindObjectByName(parent,name))
++      {
++              return NULL;
++      }
++      
++      in = yaffs_CreateNewObject(dev,-1,type);
++      
++      if(in)
++      {
++              in->chunkId = -1;
++              in->valid = 1;
++              in->variantType = type;
++
++              in->yst_mode  = mode;
++              
++#ifdef CONFIG_YAFFS_WINCE
++              yfsd_WinFileTimeNow(in->win_atime);
++              in->win_ctime[0] = in->win_mtime[0] = in->win_atime[0];
++              in->win_ctime[1] = in->win_mtime[1] = in->win_atime[1];
++              
++#else
++              in->yst_atime = in->yst_mtime = in->yst_ctime = Y_CURRENT_TIME;
++
++              in->yst_rdev  = rdev;
++              in->yst_uid   = uid;
++              in->yst_gid   = gid;
++#endif                
++              in->nDataChunks = 0;
++
++              yaffs_SetObjectName(in,name);
++              in->dirty = 1;
++              
++              yaffs_AddObjectToDirectory(parent,in);
++              
++              in->myDev = parent->myDev;
++              
++                              
++              switch(type)
++              {
++                      case YAFFS_OBJECT_TYPE_SYMLINK:
++                              in->variant.symLinkVariant.alias = yaffs_CloneString(aliasString);
++                              break;
++                      case YAFFS_OBJECT_TYPE_HARDLINK:
++                              in->variant.hardLinkVariant.equivalentObject = equivalentObject;
++                              in->variant.hardLinkVariant.equivalentObjectId = equivalentObject->objectId;
++                              list_add(&in->hardLinks,&equivalentObject->hardLinks);
++                              break;
++                      case YAFFS_OBJECT_TYPE_FILE: // do nothing
++                      case YAFFS_OBJECT_TYPE_DIRECTORY: // do nothing
++                      case YAFFS_OBJECT_TYPE_SPECIAL: // do nothing
++                      case YAFFS_OBJECT_TYPE_UNKNOWN:
++                              break;
++              }
++
++              if(/*yaffs_GetNumberOfFreeChunks(dev) <= 0 || */
++                 yaffs_UpdateObjectHeader(in,name,0,0,0) < 0)
++              {
++                      // Could not create the object header, fail the creation
++                      yaffs_DestroyObject(in);
++                      in = NULL;
++              }
++
++      }
++      
++      return in;
++}
++
++yaffs_Object *yaffs_MknodFile(yaffs_Object *parent,const YCHAR *name, __u32 mode, __u32 uid, __u32 gid)
++{
++      return yaffs_MknodObject(YAFFS_OBJECT_TYPE_FILE,parent,name,mode,uid,gid,NULL,NULL,0);
++}
++
++yaffs_Object *yaffs_MknodDirectory(yaffs_Object *parent,const YCHAR *name, __u32 mode, __u32 uid, __u32 gid)
++{
++      return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY,parent,name,mode,uid,gid,NULL,NULL,0);
++}
++
++yaffs_Object *yaffs_MknodSpecial(yaffs_Object *parent,const YCHAR *name, __u32 mode, __u32 uid, __u32 gid, __u32 rdev)
++{
++      return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SPECIAL,parent,name,mode,uid,gid,NULL,NULL,rdev);
++}
++
++yaffs_Object *yaffs_MknodSymLink(yaffs_Object *parent,const YCHAR *name, __u32 mode, __u32 uid, __u32 gid,const YCHAR *alias)
++{
++      return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SYMLINK,parent,name,mode,uid,gid,NULL,alias,0);
++}
++
++// NB yaffs_Link returns the object id of the equivalent object.
++yaffs_Object *yaffs_Link(yaffs_Object *parent, const YCHAR *name, yaffs_Object *equivalentObject)
++{
++      // Get the real object in case we were fed a hard link as an equivalent object
++      equivalentObject = yaffs_GetEquivalentObject(equivalentObject);
++      
++      if(yaffs_MknodObject(YAFFS_OBJECT_TYPE_HARDLINK,parent,name,0,0,0,equivalentObject,NULL,0))
++      {
++              return equivalentObject;
++      }
++      else
++      {
++              return NULL;
++      }
++      
++}
++
++
++static int yaffs_ChangeObjectName(yaffs_Object *obj, yaffs_Object *newDir, const YCHAR *newName,int force,int shadows)
++{
++      int unlinkOp;
++      int deleteOp;
++      
++      yaffs_Object * existingTarget;
++
++      if(newDir == NULL)
++      {
++              newDir = obj->parent; // use the old directory
++      }
++      
++      if(newDir->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
++      {
++              T(YAFFS_TRACE_ALWAYS,(TSTR("tragendy: yaffs_ChangeObjectName: newDir is not a directory"TENDSTR)));
++              YBUG();
++      }
++
++      // TODO: Do we need this different handling for YAFFS2 and YAFFS1??
++      if(obj->myDev->isYaffs2)
++      {
++              unlinkOp = (newDir == obj->myDev->unlinkedDir);
++      }
++      else
++      {
++              unlinkOp = (newDir == obj->myDev->unlinkedDir && obj->variantType == YAFFS_OBJECT_TYPE_FILE);
++      }
++
++      deleteOp = (newDir == obj->myDev->deletedDir);
++      
++      existingTarget = yaffs_FindObjectByName(newDir,newName);
++      
++      // If the object is a file going into the unlinked directory, then it is OK to just stuff it in since
++      // duplicate names are allowed.
++      // Otherwise only proceed if the new name does not exist and if we're putting it into a directory.
++      if( (unlinkOp|| 
++               deleteOp ||
++               force || 
++               (shadows > 0) ||
++               !existingTarget)  &&
++           newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY)
++      {
++              yaffs_SetObjectName(obj,newName);
++              obj->dirty = 1;
++              
++              yaffs_AddObjectToDirectory(newDir,obj);
++              
++              if(unlinkOp) obj->unlinked = 1;
++              
++              // If it is a deletion then we mark it as a shrink for gc purposes.
++              if(yaffs_UpdateObjectHeader(obj,newName,0,deleteOp,shadows) >= 0)
++              {
++                      return YAFFS_OK;
++              }
++      }
++      
++      return YAFFS_FAIL;
++}
++
++
++
++int yaffs_RenameObject(yaffs_Object *oldDir, const YCHAR *oldName, yaffs_Object *newDir, const YCHAR *newName)
++{
++      yaffs_Object *obj;
++      yaffs_Object *existingTarget;
++      int force = 0;
++      
++#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
++      // Special case for case insemsitive systems (eg. WinCE).
++      // While look-up is case insensitive, the name isn't.
++      // THerefore we might want to change x.txt to X.txt
++      if(oldDir == newDir && yaffs_strcmp(oldName,newName) == 0)
++      {
++              force = 1;
++      }       
++#endif
++      
++      obj = yaffs_FindObjectByName(oldDir,oldName);
++      
++      if(obj && obj->renameAllowed)
++      {
++      
++              // Now do the handling for an existing target, if there is one
++      
++              existingTarget = yaffs_FindObjectByName(newDir,newName);
++              if(existingTarget &&
++                 existingTarget->variantType == YAFFS_OBJECT_TYPE_DIRECTORY &&
++                 !list_empty(&existingTarget->variant.directoryVariant.children))
++               {
++                      // There is a target that is a non-empty directory, so we have to fail
++                      return YAFFS_FAIL; // EEXIST or ENOTEMPTY
++               }
++               else if(existingTarget)
++               {
++                      // Nuke the target first, using shadowing
++                       yaffs_ChangeObjectName(obj,newDir,newName,force,existingTarget->objectId);
++                       yaffs_Unlink(newDir,newName);
++               }
++              
++              
++              return yaffs_ChangeObjectName(obj,newDir,newName,force,0);
++      }
++      return YAFFS_FAIL;
++}
++
++
++#if 0
++
++static int yaffs_CheckObjectHashSanity(yaffs_Device *dev)
++{
++      // Scan the buckets and check that the lists 
++      // have as many members as the count says there are
++      int bucket;
++      int countEm;
++      struct list_head *j;
++      int ok = YAFFS_OK;
++      
++      for(bucket = 0; bucket < YAFFS_NOBJECT_BUCKETS; bucket++)
++      {
++              countEm = 0;
++              
++              list_for_each(j,&dev->objectBucket[bucket].list)
++              {
++                      countEm++;
++              }
++              
++              if(countEm != dev->objectBucket[bucket].count)
++              {
++                      T(YAFFS_TRACE_ERROR,(TSTR("Inode hash inconsistency" TENDSTR)));
++                      ok = YAFFS_FAIL;
++              }
++      }
++
++      return ok;
++}
++
++
++void yaffs_ObjectTest(yaffs_Device *dev)
++{
++      yaffs_Object *in[1000];
++      int inNo[1000];
++      yaffs_Object *inold[1000];
++      int i;
++      int j;
++      
++      memset(in,0,1000*sizeof(yaffs_Object *));
++      memset(inold,0,1000*sizeof(yaffs_Object *));
++      
++      yaffs_CheckObjectHashSanity(dev);
++      
++      for(j = 0; j < 10; j++)
++      {
++              //T(("%d\n",j));
++              
++              for(i = 0; i < 1000; i++)
++              {
++                      in[i] = yaffs_CreateNewObject(dev,-1,YAFFS_OBJECT_TYPE_FILE);
++                      if(!in[i])
++                      {
++                              YINFO("No more inodes");
++                      }
++                      else
++                      {
++                              inNo[i] = in[i]->objectId;
++                      }
++              }
++              
++              for(i = 0; i < 1000; i++)
++              {
++                      if(yaffs_FindObjectByNumber(dev,inNo[i]) != in[i])
++                      {
++                              //T(("Differnce in look up test\n"));
++                      }
++                      else
++                      {
++                              // T(("Look up ok\n"));
++                      }
++              }
++              
++              yaffs_CheckObjectHashSanity(dev);
++      
++              for(i = 0; i < 1000; i+=3)
++              {
++                      yaffs_FreeObject(in[i]);        
++                      in[i] = NULL;
++              }
++              
++      
++              yaffs_CheckObjectHashSanity(dev);
++      }
++              
++}
++
++#endif
++
++/////////////////////////// Block Management and Page Allocation ///////////////////
++
++
++static int yaffs_InitialiseBlocks(yaffs_Device *dev,int nBlocks)
++{
++      dev->allocationBlock = -1; // force it to get a new one
++      //Todo we're assuming the malloc will pass.
++      dev->blockInfo = YMALLOC(nBlocks * sizeof(yaffs_BlockInfo));
++      // Set up dynamic blockinfo stuff.
++      dev->chunkBitmapStride = (dev->nChunksPerBlock+7)/8;
++      dev->chunkBits = YMALLOC(dev->chunkBitmapStride * nBlocks);
++      if(dev->blockInfo && dev->chunkBits)
++      {
++              memset(dev->blockInfo,0,nBlocks * sizeof(yaffs_BlockInfo));
++              memset(dev->chunkBits,0,dev->chunkBitmapStride * nBlocks);
++              return YAFFS_OK;
++      }
++      
++      return YAFFS_FAIL;
++      
++}
++
++static void yaffs_DeinitialiseBlocks(yaffs_Device *dev)
++{
++      YFREE(dev->blockInfo);
++      dev->blockInfo = NULL;
++      YFREE(dev->chunkBits);
++      dev->chunkBits = NULL;
++}
++
++
++static int yaffs_BlockNotDisqualifiedFromGC(yaffs_Device *dev, yaffs_BlockInfo *bi)
++{
++      int i;
++      __u32 seq;
++      yaffs_BlockInfo *b;
++      
++      if(!dev->isYaffs2) return 1; // disqualification only applies to yaffs2.
++      
++      if(!bi->hasShrinkHeader) return 1; // can gc
++
++
++      // Find the oldest dirty sequence number if we don't know it and save it
++      // so we don't have to keep recomputing it.
++      if(!dev->oldestDirtySequence)
++      {
++              seq = dev->sequenceNumber;
++
++              for(i = dev->internalStartBlock; i <= dev->internalEndBlock; i++)
++              {
++                      b = yaffs_GetBlockInfo(dev,i);
++                      if(b->blockState == YAFFS_BLOCK_STATE_FULL &&
++                      (b->pagesInUse - b->softDeletions )< dev->nChunksPerBlock &&
++                      b->sequenceNumber < seq)
++                      {
++                              seq = b->sequenceNumber;
++                      }
++              }
++              dev->oldestDirtySequence = seq;
++      }
++
++
++      // Can't do gc of this block if there are any blocks older than this one that have
++      // discarded pages.
++      return (bi->sequenceNumber <= dev->oldestDirtySequence);
++      
++      
++      return 1;
++
++}
++
++// FindDiretiestBlock is used to select the dirtiest block (or close enough)
++// for garbage collection.
++//
++
++
++
++static int yaffs_FindBlockForGarbageCollection(yaffs_Device *dev,int aggressive)
++{
++
++      int b = dev->currentDirtyChecker;
++      
++      int i;
++      int iterations;
++      int dirtiest = -1;
++      int pagesInUse; 
++      yaffs_BlockInfo *bi;
++      static int  nonAggressiveSkip = 0;
++
++      // If we're doing aggressive GC then we are happy to take a less-dirty block, and
++      // search harder.
++      // else (we're doing a leasurely gc), then we only bother to do this if the
++      // block has only a few pages in use.
++      
++
++      nonAggressiveSkip--;
++
++      if(!aggressive &&(nonAggressiveSkip > 0))
++      {
++              return -1;
++      }
++
++      pagesInUse = (aggressive)? dev->nChunksPerBlock : YAFFS_PASSIVE_GC_CHUNKS + 1;
++
++      if(aggressive)
++      {
++              iterations = dev->internalEndBlock - dev->internalStartBlock + 1;
++      }
++      else
++      {
++              iterations = dev->internalEndBlock - dev->internalStartBlock + 1;
++              iterations = iterations / 16; 
++              if(iterations > 200)
++              {
++                      iterations = 200;
++              }
++      }
++      
++      for(i = 0; i <= iterations && pagesInUse > 0 ; i++)
++      {
++              b++;
++              if ( b < dev->internalStartBlock || b > dev->internalEndBlock)
++              {
++                      b =  dev->internalStartBlock;
++              }
++
++              if(b < dev->internalStartBlock || b > dev->internalEndBlock)
++              {
++                      T(YAFFS_TRACE_ERROR,(TSTR("**>> Block %d is not valid" TENDSTR),b));
++                      YBUG();
++              }
++              
++              bi = yaffs_GetBlockInfo(dev,b);
++              
++              if(bi->blockState == YAFFS_BLOCK_STATE_FULL &&
++                 (bi->pagesInUse - bi->softDeletions )< pagesInUse &&
++                 yaffs_BlockNotDisqualifiedFromGC(dev,bi))
++              {
++                      dirtiest = b;
++                      pagesInUse = (bi->pagesInUse - bi->softDeletions);
++              }
++      }
++      
++      dev->currentDirtyChecker = b;
++      
++      if(dirtiest > 0)
++      {
++              T(YAFFS_TRACE_GC,(TSTR("GC Selected block %d with %d free" TENDSTR),dirtiest,dev->nChunksPerBlock - pagesInUse));
++      }
++      
++      dev->oldestDirtySequence = 0; // clear this
++      
++      if(dirtiest > 0)
++      {
++              nonAggressiveSkip = 4;
++      }
++
++      return dirtiest;
++}
++
++
++static void yaffs_BlockBecameDirty(yaffs_Device *dev,int blockNo)
++{
++      yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,blockNo);
++      
++      int erasedOk = 0;
++      
++      // If the block is still healthy erase it and mark as clean.
++      // If the block has had a data failure, then retire it.
++      bi->blockState = YAFFS_BLOCK_STATE_DIRTY;
++
++      if(!bi->needsRetiring)
++      {
++              erasedOk = yaffs_EraseBlockInNAND(dev,blockNo);
++              if(!erasedOk)
++              {
++                      dev->nErasureFailures++;
++                      T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>> Erasure failed %d" TENDSTR),blockNo));
++              }
++      }
++
++      if(erasedOk && (yaffs_traceMask & YAFFS_TRACE_ERASE))
++      {
++                      int i;
++                      for(i = 0; i < dev->nChunksPerBlock; i++)
++                      {
++                                      if(!yaffs_CheckChunkErased(dev,blockNo * dev->nChunksPerBlock + i))
++                                      {
++                                              T(YAFFS_TRACE_ERROR,(TSTR(">>Block %d erasure supposedly OK, but chunk %d not erased" TENDSTR),blockNo,i));
++                                      }
++                      }
++      }
++      
++      if( erasedOk )
++      {
++              // Clean it up...
++              bi->blockState = YAFFS_BLOCK_STATE_EMPTY;
++              dev->nErasedBlocks++;
++              bi->pagesInUse = 0;
++              bi->softDeletions = 0;
++              bi->hasShrinkHeader=0;
++              yaffs_ClearChunkBits(dev,blockNo);
++      
++              T(YAFFS_TRACE_ERASE,(TSTR("Erased block %d" TENDSTR),blockNo));
++      }
++      else
++      {
++              dev->nFreeChunks -= dev->nChunksPerBlock; // We lost a block of free space
++              
++              yaffs_RetireBlock(dev,blockNo);
++              T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>> Block %d retired" TENDSTR),blockNo));
++      }
++}
++
++#if 0
++static void yaffs_DumpBlockStats(yaffs_Device *dev)
++{
++      int i,j;
++      yaffs_BlockInfo *bi;
++      
++      for(i= dev->internalStartBlock; i <=dev->internalEndBlock; i++)
++      {
++              bi = yaffs_GetBlockInfo(dev,i);
++              T(YAFFS_TRACE_ALLOCATE,(TSTR("%3d state %d shrink %d inuse %d/%d seq %d pages"),i,
++              bi->blockState,bi->hasShrinkHeader,bi->pagesInUse,bi->softDeletions,bi->sequenceNumber));       
++              
++              for(j = 0; j < dev->nChunksPerBlock; j++)
++              {
++                      if(yaffs_CheckChunkBit(dev,i,j))
++                      {
++                              T(YAFFS_TRACE_ALLOCATE,(TSTR(" %d"),j));
++
++                      }
++              }
++              T(YAFFS_TRACE_ALLOCATE,(TSTR(" " TENDSTR)));
++
++      }
++}
++#endif
++
++
++static int yaffs_FindBlockForAllocation(yaffs_Device *dev)
++{
++      int i;
++      
++      yaffs_BlockInfo *bi;
++      
++#if 0
++      static int j = 0;
++      j++;
++      if(j < 0 || j > 100)
++      {
++              j = 0;
++              yaffs_DumpBlockStats(dev);
++      }
++      
++#endif
++      
++      if(dev->nErasedBlocks < 1)
++      {
++              // Hoosterman we've got a problem.
++              // Can't get space to gc
++              T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: no more eraased blocks" TENDSTR)));
++
++              return -1;
++      }
++      
++      // Find an empty block.
++      
++      for(i = dev->internalStartBlock; i <= dev->internalEndBlock; i++)
++      {
++              dev->allocationBlockFinder++;
++              if(dev->allocationBlockFinder < dev->internalStartBlock || dev->allocationBlockFinder> dev->internalEndBlock) 
++              {
++                      dev->allocationBlockFinder = dev->internalStartBlock;
++              }
++              
++              bi = yaffs_GetBlockInfo(dev,dev->allocationBlockFinder);
++
++              if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY)
++              {
++                      bi->blockState = YAFFS_BLOCK_STATE_ALLOCATING;
++                      dev->sequenceNumber++;
++                      bi->sequenceNumber = dev->sequenceNumber;
++                      dev->nErasedBlocks--;           
++                      T(YAFFS_TRACE_ALLOCATE,(TSTR("Allocated block %d, seq  %d, %d left" TENDSTR),dev->allocationBlockFinder,dev->sequenceNumber, dev->nErasedBlocks));      
++                      return dev->allocationBlockFinder;
++              }
++      }
++      
++      
++      T(YAFFS_TRACE_ALWAYS, (TSTR("yaffs tragedy: no more eraased blocks, but there should have been %d" TENDSTR),dev->nErasedBlocks));
++      
++      
++      
++
++      
++      return -1;      
++}
++
++
++// To determine if we have enough space we just look at the 
++// number of erased blocks.
++
++static int yaffs_CheckSpaceForAllocation(yaffs_Device *dev)
++{
++      int reservedChunks = (dev->nReservedBlocks * dev->nChunksPerBlock);
++      return (dev->nFreeChunks > reservedChunks);
++}
++
++
++static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve)
++{
++      int retVal;
++      yaffs_BlockInfo *bi;
++      
++      if(dev->allocationBlock < 0)
++      {
++              // Get next block to allocate off
++              dev->allocationBlock = yaffs_FindBlockForAllocation(dev);
++              dev->allocationPage = 0;
++      }
++      
++      if(!useReserve &&  !yaffs_CheckSpaceForAllocation(dev))
++      {
++              // Not enough space to allocate unless we're allowed to use the reserve.
++              return -1;
++      }
++
++      if(dev->nErasedBlocks < dev->nReservedBlocks && dev->allocationPage == 0)
++      {
++              T(YAFFS_TRACE_ALLOCATE,(TSTR("Allocating reserve" TENDSTR)));   
++      }
++
++      
++      // Next page please....
++      if(dev->allocationBlock >= 0)
++      {
++              bi = yaffs_GetBlockInfo(dev,dev->allocationBlock);
++              
++              retVal = (dev->allocationBlock * dev->nChunksPerBlock) + 
++                                dev->allocationPage;
++              bi->pagesInUse++;
++              yaffs_SetChunkBit(dev,dev->allocationBlock,dev->allocationPage);
++
++              dev->allocationPage++;
++              
++              dev->nFreeChunks--;
++              
++              // If the block is full set the state to full
++              if(dev->allocationPage >= dev->nChunksPerBlock)
++              {
++                      bi->blockState = YAFFS_BLOCK_STATE_FULL;
++                      dev->allocationBlock = -1;
++              }
++
++
++              return retVal;
++              
++      }
++      T(YAFFS_TRACE_ERROR,(TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR)));
++
++      return -1;      
++}
++
++
++
++
++static int yaffs_GetErasedChunks(yaffs_Device *dev)
++{
++              int n;
++
++              n = dev->nErasedBlocks * dev->nChunksPerBlock;
++
++              if(dev->allocationBlock> 0)
++              {
++                      n += (dev->nChunksPerBlock - dev->allocationPage);
++              }
++
++              return n;
++
++}
++
++static int  yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)
++{
++      int oldChunk;
++      int newChunk;
++      int chunkInBlock;
++      int markNAND;
++      int retVal = YAFFS_OK;
++      int cleanups = 0;
++      int i;
++
++      int chunksBefore = yaffs_GetErasedChunks(dev);
++      int chunksAfter;
++
++      yaffs_ExtendedTags  tags;
++      
++      yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,block);
++      
++      yaffs_Object *object;
++      
++      bi->blockState = YAFFS_BLOCK_STATE_COLLECTING;
++
++      T(YAFFS_TRACE_TRACING,(TSTR("Collecting block %d, in use %d, shrink %d, " TENDSTR),block,bi->pagesInUse,bi->hasShrinkHeader));
++      //T(("Collecting block %d n %d bits %x\n",block, bi->pagesInUse, bi->pageBits));        
++      
++      //yaffs_VerifyFreeChunks(dev);
++
++      bi->hasShrinkHeader = 0; // clear the flag so that the block can erase
++      
++      dev->nFreeChunks -= bi->softDeletions;  // Take off the number of soft deleted entries because
++                                              // they're going to get really deleted during GC.
++
++      dev->isDoingGC = 1;
++
++      if(!yaffs_StillSomeChunkBits(dev,block))
++      {
++              T(YAFFS_TRACE_TRACING,(TSTR("Collecting block %d that has no chunks in use" TENDSTR),block));
++              yaffs_BlockBecameDirty(dev,block);
++      }
++      else
++      {
++
++      __u8  *buffer = yaffs_GetTempBuffer(dev,__LINE__);
++
++      for(chunkInBlock = 0,oldChunk = block * dev->nChunksPerBlock; 
++          chunkInBlock < dev->nChunksPerBlock && yaffs_StillSomeChunkBits(dev,block);
++          chunkInBlock++, oldChunk++ )
++      {
++              if(yaffs_CheckChunkBit(dev,block,chunkInBlock))
++              {
++                      
++                      // This page is in use and might need to be copied off
++                      
++                      markNAND = 1;
++                      
++                      //T(("copying page %x from %d to %d\n",mask,oldChunk,newChunk));
++                      
++                      yaffs_InitialiseTags(&tags);
++                      
++                      yaffs_ReadChunkWithTagsFromNAND(dev,oldChunk,buffer, &tags);
++
++                      object = yaffs_FindObjectByNumber(dev,tags.objectId);
++                      
++                      T(YAFFS_TRACE_GC_DETAIL,(TSTR("Collecting page %d, %d %d %d " TENDSTR),chunkInBlock,tags.objectId,tags.chunkId,tags.byteCount));
++                      
++                      if(!object)
++                      {
++                              T(YAFFS_TRACE_ERROR,(TSTR("page %d in gc has no object " TENDSTR),oldChunk));
++                      }
++                      
++                      if(object && object->deleted && tags.chunkId != 0)
++                      {
++                              // Data chunk in a deleted file, throw it away
++                              // It's a soft deleted data chunk,
++                              // No need to copy this, just forget about it and fix up the
++                              // object.
++                              
++                              //yaffs_PutChunkIntoFile(object, tags.chunkId, 0,0); 
++                              object->nDataChunks--;
++                              
++                              if(object->nDataChunks <= 0)
++                              {
++                                      // remeber to clean up the object
++                                      dev->gcCleanupList[cleanups] = tags.objectId;
++                                      cleanups++;
++                              }
++                              markNAND = 0;
++                      }
++                      else if( 0 /* Todo object && object->deleted && object->nDataChunks == 0 */)
++                      {
++                              // Deleted object header with no data chunks.
++                              // Can be discarded and the file deleted.
++                              object->chunkId = 0;
++                              yaffs_FreeTnode(object->myDev,object->variant.fileVariant.top);
++                              object->variant.fileVariant.top = NULL;
++                              yaffs_DoGenericObjectDeletion(object);
++                              
++                      }
++                      else if(object)
++                      {
++                              // It's either a data chunk in a live file or
++                              // an ObjectHeader, so we're interested in it.
++                              // NB Need to keep the ObjectHeaders of deleted files
++                              // until the whole file has been deleted off
++                              tags.serialNumber++;
++
++                              dev->nGCCopies++;
++                              
++                              if(tags.chunkId == 0)
++                              {
++                                      // It is an object Id,
++                                      // We need to nuke the shrinkheader flags first
++                                      // We no longer want the shrinkHeader flag since its work is done
++                                      // and if it is left in place it will mess up scanning.
++                                      
++                                      yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)buffer;
++                                      oh->isShrink = 0;
++                                      tags.extraIsShrinkHeader = 0;
++                              }
++
++                              newChunk = yaffs_WriteNewChunkWithTagsToNAND(dev, buffer, &tags,1);
++                      
++                              if(newChunk < 0)
++                              {
++                                      retVal =  YAFFS_FAIL;
++                              }
++                              else
++                              {
++                      
++                                      // Ok, now fix up the Tnodes etc.
++                      
++                                      if(tags.chunkId == 0)
++                                      {
++                                              // It's a header
++                                              object->chunkId = newChunk;
++                                              object->serial = tags.serialNumber;
++                                      }
++                                      else
++                                      {
++                                              // It's a data chunk
++                                              yaffs_PutChunkIntoFile(object, tags.chunkId, newChunk,0);
++                                      }
++                              }
++                      }
++                      
++                      yaffs_DeleteChunk(dev,oldChunk,markNAND,__LINE__);                      
++                      
++              }
++      }
++
++      yaffs_ReleaseTempBuffer(dev,buffer,__LINE__);
++
++      //yaffs_VerifyFreeChunks(dev);
++
++      // Do any required cleanups
++      for(i = 0; i < cleanups; i++)
++      {                                               
++              // Time to delete the file too
++              object =  yaffs_FindObjectByNumber(dev,dev->gcCleanupList[i]);
++              if(object)
++              {
++                      yaffs_FreeTnode(dev,object->variant.fileVariant.top);
++                      object->variant.fileVariant.top = NULL;
++                      T(YAFFS_TRACE_GC,(TSTR("yaffs: About to finally delete object %d" TENDSTR),object->objectId));
++                      yaffs_DoGenericObjectDeletion(object);
++              }
++
++      }
++
++      }
++
++      if(chunksBefore >= (chunksAfter = yaffs_GetErasedChunks(dev)))
++      {
++                      T(YAFFS_TRACE_GC,(TSTR("gc did not increase free chunks before %d after %d" TENDSTR),chunksBefore,chunksAfter));
++      }
++      
++      
++      dev->isDoingGC = 0;
++      
++      //yaffs_VerifyFreeChunks(dev);
++                      
++      return YAFFS_OK;
++}
++
++#if 0
++static yaffs_Object *yaffs_FindDeletedUnlinkedFile(yaffs_Device *dev)
++{
++      // find a file to delete
++      struct list_head *i;    
++      yaffs_Object *l;
++
++
++      //Scan the unlinked files looking for one to delete
++      list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children)
++      {
++              if(i)
++              {
++                      l = list_entry(i, yaffs_Object,siblings);
++                      if(l->deleted)
++                      {
++                              return l;                       
++                      }
++              }
++      }       
++      return NULL;
++}
++
++
++static void yaffs_DoUnlinkedFileDeletion(yaffs_Device *dev)
++{
++      // This does background deletion on unlinked files.. only deleted ones.
++      // If we don't have a file we're working on then find one
++      if(!dev->unlinkedDeletion && dev->nDeletedFiles > 0)
++      {
++              dev->unlinkedDeletion = yaffs_FindDeletedUnlinkedFile(dev);
++      }
++      
++      // OK, we're working on a file...
++      if(dev->unlinkedDeletion)
++      {
++              yaffs_Object *obj = dev->unlinkedDeletion;
++              int delresult;
++              int limit; // Number of chunks to delete in a file.
++                                 // NB this can be exceeded, but not by much.
++                                 
++              limit = -1;
++
++              delresult = yaffs_DeleteWorker(obj, obj->variant.fileVariant.top, obj->variant.fileVariant.topLevel, 0,&limit);
++              
++              if(obj->nDataChunks == 0)
++              {
++                      // Done all the deleting of data chunks.
++                      // Now dump the header and clean up
++                      yaffs_FreeTnode(dev,obj->variant.fileVariant.top);
++                      obj->variant.fileVariant.top = NULL;
++                      yaffs_DoGenericObjectDeletion(obj);
++                      dev->nDeletedFiles--;
++                      dev->nUnlinkedFiles--;
++                      dev->nBackgroundDeletions++;
++                      dev->unlinkedDeletion = NULL;   
++              }
++      }
++}
++
++#endif
++
++
++
++// New garbage collector
++// If we're very low on erased blocks then we do aggressive garbage collection
++// otherwise we do "leasurely" garbage collection.
++// Aggressive gc looks further (whole array) and will accept dirtier blocks.
++// Passive gc only inspects smaller areas and will only accept cleaner blocks.
++//
++// The idea is to help clear out space in a more spread-out manner.
++// Dunno if it really does anything useful.
++//
++static int yaffs_CheckGarbageCollection(yaffs_Device *dev)
++{
++      int block;
++      int aggressive; 
++      int gcOk = YAFFS_OK;
++      int maxTries = 0;
++      
++      //yaffs_VerifyFreeChunks(dev);
++      
++      if(dev->isDoingGC)
++      {
++              // Bail out so we don't get recursive gc
++              return YAFFS_OK;
++      }
++
++      // This loop should pass the first time.
++      // We'll only see looping here if the erase of the collected block fails.
++      
++      do{
++              maxTries++;
++              if(dev->nErasedBlocks < dev->nReservedBlocks)
++              {
++                      // We need a block soon...
++                      aggressive = 1;
++              }
++              else 
++              {
++                      // We're in no hurry
++                      aggressive = 0;
++              }
++      
++              block = yaffs_FindBlockForGarbageCollection(dev,aggressive);
++      
++              if(block > 0)
++              {
++                      dev->garbageCollections++;
++                      if(!aggressive)
++                      {
++                              dev->passiveGarbageCollections++;
++                      }
++
++                      T(YAFFS_TRACE_GC,(TSTR("yaffs: GC erasedBlocks %d aggressive %d" TENDSTR),dev->nErasedBlocks,aggressive));
++
++                      gcOk =  yaffs_GarbageCollectBlock(dev,block);
++              }
++
++              if(dev->nErasedBlocks < (dev->nReservedBlocks) && block > 0) 
++              {
++                      T(YAFFS_TRACE_GC,(TSTR("yaffs: GC !!!no reclaim!!! erasedBlocks %d after try %d block %d" TENDSTR),dev->nErasedBlocks,maxTries,block));
++              }
++      } while((dev->nErasedBlocks < dev->nReservedBlocks) && (block > 0) && (maxTries < 2));
++
++      return aggressive ? gcOk: YAFFS_OK;
++}
++
++
++//////////////////////////// TAGS ///////////////////////////////////////
++
++
++
++#if 0
++
++void yaffs_CalcTagsECC(yaffs_Tags *tags)
++{
++      // Calculate an ecc
++      
++      unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes;
++      unsigned  i,j;
++      unsigned  ecc = 0;
++      unsigned bit = 0;
++
++      tags->ecc = 0;
++      
++      for(i = 0; i < 8; i++)
++      {
++              for(j = 1; j &0xff; j<<=1)
++              {
++                      bit++;
++                      if(b[i] & j)
++                      {
++                              ecc ^= bit;
++                      }
++              }
++      }
++      
++      tags->ecc = ecc;
++      
++      
++}
++
++int  yaffs_CheckECCOnTags(yaffs_Tags *tags)
++{
++      unsigned ecc = tags->ecc;
++      
++      yaffs_CalcTagsECC(tags);
++      
++      ecc ^= tags->ecc;
++      
++      if(ecc && ecc <= 64)
++      {
++              // TODO: Handle the failure better. Retire?
++              unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes;
++
++              ecc--;
++                              
++              b[ecc / 8] ^= (1 << (ecc & 7));
++              
++              // Now recvalc the ecc
++              yaffs_CalcTagsECC(tags);
++              
++              return 1; // recovered error
++      }
++      else if(ecc)
++      {
++              // Wierd ecc failure value
++              // TODO Need to do somethiong here
++              return -1; //unrecovered error
++      }
++      
++      return 0;
++}
++
++static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr)
++{
++      yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr;
++      
++      yaffs_CalcTagsECC(tagsPtr);
++      
++      sparePtr->tagByte0 = tu->asBytes[0];
++      sparePtr->tagByte1 = tu->asBytes[1];
++      sparePtr->tagByte2 = tu->asBytes[2];
++      sparePtr->tagByte3 = tu->asBytes[3];
++      sparePtr->tagByte4 = tu->asBytes[4];
++      sparePtr->tagByte5 = tu->asBytes[5];
++      sparePtr->tagByte6 = tu->asBytes[6];
++      sparePtr->tagByte7 = tu->asBytes[7];
++}
++
++static void yaffs_GetTagsFromSpare(yaffs_Device *dev, yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr)
++{
++      yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr;
++      int result;
++
++      tu->asBytes[0]= sparePtr->tagByte0;
++      tu->asBytes[1]= sparePtr->tagByte1;
++      tu->asBytes[2]= sparePtr->tagByte2;
++      tu->asBytes[3]= sparePtr->tagByte3;
++      tu->asBytes[4]= sparePtr->tagByte4;
++      tu->asBytes[5]= sparePtr->tagByte5;
++      tu->asBytes[6]= sparePtr->tagByte6;
++      tu->asBytes[7]= sparePtr->tagByte7;
++      
++      result =  yaffs_CheckECCOnTags(tagsPtr);
++      if(result> 0)
++      {
++              dev->tagsEccFixed++;
++      }
++      else if(result <0)
++      {
++              dev->tagsEccUnfixed++;
++      }
++}
++
++static void yaffs_SpareInitialise(yaffs_Spare *spare)
++{
++      memset(spare,0xFF,sizeof(yaffs_Spare));
++}
++
++#endif
++
++#if 0
++static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device *dev, const __u8 *buffer, yaffs_ExtendedTags *tags, int useReserve)
++{
++      // NB There must be tags, data is optional
++      // If there is data, then an ECC is calculated on it.
++      
++      yaffs_Spare spare;
++      
++      if(!tags)
++      {
++              return YAFFS_FAIL;
++      }
++      
++      //yaffs_SpareInitialise(&spare);
++      
++      //if(!dev->useNANDECC && buffer)
++      //{
++      //      yaffs_CalcECC(buffer,&spare);
++      //}
++      
++      //yaffs_LoadTagsIntoSpare(&spare,tags);
++      
++      return yaffs_WriteNewChunkToNAND(dev,buffer,&spare,useReserve);
++      
++}
++#endif
++
++static int yaffs_TagsMatch(const yaffs_ExtendedTags *tags, int objectId, int chunkInObject)
++{
++      return  (  tags->chunkId == chunkInObject &&
++                         tags->objectId == objectId &&
++                         !tags->chunkDeleted) ? 1 : 0;
++      
++}
++
++/////////////////////////////////////////////////////////////////////////////////////////////////////////
++
++
++static int yaffs_FindChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_ExtendedTags *tags)
++{
++      //Get the Tnode, then get the level 0 offset chunk offset
++    yaffs_Tnode *tn;     
++    int theChunk = -1;
++    yaffs_ExtendedTags localTags;
++    int retVal = -1;
++    
++    yaffs_Device *dev = in->myDev;
++    
++    
++    if(!tags)
++    {
++      // Passed a NULL, so use our own tags space
++      tags = &localTags;
++    }
++    
++    tn = yaffs_FindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);
++    
++    if(tn)
++    {
++              theChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] << dev->chunkGroupBits;
++              
++              retVal = yaffs_FindChunkInGroup(dev,theChunk,tags,in->objectId,chunkInInode);
++    }
++    return retVal;
++}
++
++static int yaffs_FindAndDeleteChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_ExtendedTags *tags)
++{
++      //Get the Tnode, then get the level 0 offset chunk offset
++    yaffs_Tnode *tn;     
++    int theChunk = -1;
++    yaffs_ExtendedTags localTags;
++
++    yaffs_Device *dev = in->myDev;
++    int retVal = -1;
++    
++    if(!tags)
++    {
++      // Passed a NULL, so use our own tags space
++      tags = &localTags;
++    }
++    
++    tn = yaffs_FindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);
++    
++    if(tn)
++    {
++    
++              theChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] << dev->chunkGroupBits;
++              
++              retVal = yaffs_FindChunkInGroup(dev,theChunk,tags,in->objectId,chunkInInode);
++    
++              // Delete the entry in the filestructure (if found)
++              if(retVal != -1)
++              {
++                      tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] = 0;
++              }
++    }
++    else
++    {
++      //T(("No level 0 found for %d\n", chunkInInode));
++    }
++    
++    if(retVal == -1)
++    {
++      //T(("Could not find %d to delete\n",chunkInInode));
++    }
++    return retVal;
++}
++
++
++#ifdef YAFFS_PARANOID
++
++static int yaffs_CheckFileSanity(yaffs_Object *in)
++{
++      int chunk;
++      int nChunks;
++      int fSize;
++      int failed = 0;
++      int objId;
++      yaffs_Tnode *tn;
++    yaffs_Tags localTags;
++    yaffs_Tags *tags = &localTags;
++    int theChunk;
++    int chunkDeleted;
++    
++      
++      if(in->variantType != YAFFS_OBJECT_TYPE_FILE)
++      {
++              //T(("Object not a file\n"));
++              return YAFFS_FAIL;
++      }
++      
++      objId = in->objectId;
++      fSize  = in->variant.fileVariant.fileSize;
++      nChunks = (fSize + in->myDev->nBytesPerChunk -1)/in->myDev->nBytesPerChunk;
++      
++      for(chunk = 1; chunk <= nChunks; chunk++)
++      {
++              tn = yaffs_FindLevel0Tnode(in->myDev,&in->variant.fileVariant, chunk);
++    
++              if(tn)
++              {
++    
++                      theChunk = tn->level0[chunk & YAFFS_TNODES_LEVEL0_MASK] << in->myDev->chunkGroupBits;
++                      
++              if(yaffs_CheckChunkBits(dev,theChunk/dev->nChunksPerBlock,theChunk%dev->nChunksPerBlock))
++                      {
++
++
++                              yaffs_ReadChunkTagsFromNAND(in->myDev,theChunk,tags,&chunkDeleted);
++                              if(yaffs_TagsMatch(tags,in->objectId,chunk,chunkDeleted))
++                              {
++                                      // found it;
++                              
++                              }
++                      }
++                      else
++                      {
++                              //T(("File problem file [%d,%d] NAND %d  tags[%d,%d]\n",
++                              //              objId,chunk,theChunk,tags->chunkId,tags->objectId);
++                                              
++                              failed = 1;
++                                                      
++                      }
++    
++              }
++              else
++              {
++                      //T(("No level 0 found for %d\n", chunk));
++              }
++      }
++      
++      return failed ? YAFFS_FAIL : YAFFS_OK;
++}
++
++#endif
++
++static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkInNAND, int inScan)
++{
++      // NB inScan is zero unless scanning. For forward scanning, inScan is > 0; for backward scanning inScan is < 0
++      yaffs_Tnode *tn;
++      yaffs_Device *dev = in->myDev;
++      int existingChunk;
++      yaffs_ExtendedTags existingTags;
++      yaffs_ExtendedTags newTags;
++      unsigned existingSerial, newSerial;
++      
++      if(in->variantType != YAFFS_OBJECT_TYPE_FILE)
++      {
++              // Just ignore an attempt at putting a chunk into a non-file during scanning
++              // If it is not during Scanning then something went wrong!
++              if(!inScan)
++              {
++                      T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy:attempt to put data chunk into a non-file" TENDSTR)));
++                      YBUG();
++              }
++              
++              yaffs_DeleteChunk(dev,chunkInNAND,1,__LINE__);
++              return YAFFS_OK;
++      }
++              
++      tn = yaffs_AddOrFindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);
++      if(!tn)
++      {
++              return YAFFS_FAIL;
++      }
++
++      existingChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK];            
++      
++      if(inScan != 0)
++      {
++              // If we're scanning then we need to test for duplicates
++              // NB This does not need to be efficient since it should only ever 
++              // happen when the power fails during a write, then only one
++              // chunk should ever be affected.
++              //
++              // Correction for YAFFS2: This could happen quite a lot and we need to think about efficiency! TODO
++              // Update: For backward scanning we don't need to re-read tags so this is quite cheap.
++              
++      
++              
++              if(existingChunk != 0)
++              {
++                      // NB Right now existing chunk will not be real chunkId if the device >= 32MB
++                      //    thus we have to do a FindChunkInFile to get the real chunk id.
++                      //
++                      // We have a duplicate now we need to decide which one to use:
++                      //
++                      // Backwards scanning YAFFS2: The old one is what we use, dump the new one.
++                      // Forward scanning YAFFS2: The new one is what we use, dump the old one.
++                      // YAFFS1: Get both sets of tags and compare serial numbers.
++                      //
++                      //
++                      
++                      if(inScan > 0)
++                      {
++                              // Only do this for forward scanning
++                              yaffs_ReadChunkWithTagsFromNAND(dev,chunkInNAND, NULL,&newTags);
++                      
++                      
++                              // Do a proper find
++                              existingChunk = yaffs_FindChunkInFile(in,chunkInInode, &existingTags);
++                      }
++
++                      if(existingChunk <=0)
++                      {
++                              //Hoosterman - how did this happen?
++                              
++                              T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: existing chunk < 0 in scan" TENDSTR)));
++
++                      }
++
++                      
++                      // NB The deleted flags should be false, otherwise the chunks will 
++                      // not be loaded during a scan
++                      
++                      newSerial = newTags.serialNumber;
++                      existingSerial = existingTags.serialNumber;
++                      
++                      if( (inScan > 0) &&
++                          ( in->myDev->isYaffs2  ||
++                            existingChunk <= 0 ||
++                           ((existingSerial+1) & 3) == newSerial))
++                      {
++                              // Forward scanning.                            
++                              // Use new
++                              // Delete the old one and drop through to update the tnode
++                              yaffs_DeleteChunk(dev,existingChunk,1,__LINE__);
++                      }
++                      else
++                      {
++                              // Backward scanning or we want to use the existing one
++                              // Use existing.
++                              // Delete the new one and return early so that the tnode isn't changed
++                              yaffs_DeleteChunk(dev,chunkInNAND,1,__LINE__);
++                              return YAFFS_OK;
++                      }
++              }
++
++      }
++              
++      if(existingChunk == 0)
++      {
++              in->nDataChunks++;
++      }
++      
++      tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] = (chunkInNAND >> dev->chunkGroupBits);
++      
++      return YAFFS_OK;
++}
++
++
++
++static int yaffs_ReadChunkDataFromObject(yaffs_Object *in,int chunkInInode, __u8 *buffer)
++{
++    int chunkInNAND = yaffs_FindChunkInFile(in,chunkInInode,NULL);
++    
++    if(chunkInNAND >= 0)
++    {
++              return yaffs_ReadChunkWithTagsFromNAND(in->myDev,chunkInNAND,buffer,NULL);
++      }
++      else
++      {
++              T(YAFFS_TRACE_NANDACCESS,(TSTR("Chunk %d not found zero instead" TENDSTR),chunkInNAND));
++
++              memset(buffer,0,in->myDev->nBytesPerChunk); // get sane data if you read a hole
++              return 0;
++      }
++
++}
++
++
++void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId,int markNAND,int lyn)
++{
++      int block;
++      int page;
++      yaffs_ExtendedTags tags;
++      yaffs_BlockInfo *bi;
++      
++      if(chunkId <= 0) return;        
++      
++      dev->nDeletions++;
++      block = chunkId / dev->nChunksPerBlock;
++      page = chunkId % dev->nChunksPerBlock;
++      
++      bi = yaffs_GetBlockInfo(dev,block);
++      
++      T(YAFFS_TRACE_DELETION,(TSTR("line %d delete of chunk %d" TENDSTR),lyn,chunkId));
++      
++      if(markNAND && 
++        bi->blockState != YAFFS_BLOCK_STATE_COLLECTING &&
++        !dev->isYaffs2)
++      {
++//            yaffs_SpareInitialise(&spare);
++
++#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
++
++                //read data before write, to ensure correct ecc 
++                //if we're using MTD verification under Linux
++//                yaffs_ReadChunkFromNAND(dev,chunkId,NULL,&spare,0);
++#endif
++
++              yaffs_InitialiseTags(&tags);
++
++              tags.chunkDeleted = 1;
++
++      
++              yaffs_WriteChunkWithTagsToNAND(dev,chunkId,NULL,&tags);
++              yaffs_HandleUpdateChunk(dev,chunkId,&tags);
++      }
++      else
++      {
++                      dev->nUnmarkedDeletions++;
++      }       
++      
++      
++      // Pull out of the management area.
++      // If the whole block became dirty, this will kick off an erasure.
++      if(     bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING ||
++          bi->blockState == YAFFS_BLOCK_STATE_FULL ||     
++          bi->blockState == YAFFS_BLOCK_STATE_NEEDS_SCANNING ||
++          bi->blockState == YAFFS_BLOCK_STATE_COLLECTING)
++      {
++              dev->nFreeChunks++;
++
++              yaffs_ClearChunkBit(dev,block,page);
++              
++              bi->pagesInUse--;
++              
++              if(bi->pagesInUse == 0 &&
++                 !bi->hasShrinkHeader &&
++             bi->blockState != YAFFS_BLOCK_STATE_ALLOCATING &&
++             bi->blockState != YAFFS_BLOCK_STATE_NEEDS_SCANNING)
++          {
++              yaffs_BlockBecameDirty(dev,block);
++          }
++
++      }
++      else
++      {
++              // T(("Bad news deleting chunk %d\n",chunkId));
++      }
++      
++}
++
++
++
++
++static int yaffs_WriteChunkDataToObject(yaffs_Object *in,int chunkInInode, const __u8 *buffer,int nBytes,int useReserve)
++{
++      // Find old chunk Need to do this to get serial number
++      // Write new one and patch into tree.
++      // Invalidate old tags.
++
++    int prevChunkId;
++    yaffs_ExtendedTags prevTags;
++    
++    int newChunkId;
++    yaffs_ExtendedTags newTags;
++
++    yaffs_Device *dev = in->myDev;    
++
++      yaffs_CheckGarbageCollection(dev);
++
++      // Get the previous chunk at this location in the file if it exists
++    prevChunkId  = yaffs_FindChunkInFile(in,chunkInInode,&prevTags);
++    
++    // Set up new tags
++    yaffs_InitialiseTags(&newTags);
++    
++      newTags.chunkId = chunkInInode;
++      newTags.objectId = in->objectId;
++      newTags.serialNumber = (prevChunkId >= 0) ? prevTags.serialNumber + 1 : 1;
++      newTags.byteCount = nBytes;
++              
++//    yaffs_CalcTagsECC(&newTags);
++
++      newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,buffer,&newTags,useReserve);
++      
++      if(newChunkId >= 0)
++      {
++              yaffs_PutChunkIntoFile(in,chunkInInode,newChunkId,0);
++              
++              
++              if(prevChunkId >= 0)
++              {
++                      yaffs_DeleteChunk(dev,prevChunkId,1,__LINE__);
++      
++              }
++              
++              yaffs_CheckFileSanity(in);
++      }
++      return newChunkId;
++
++
++
++
++
++}
++
++
++// UpdateObjectHeader updates the header on NAND for an object.
++// If name is not NULL, then that new name is used.
++//
++int yaffs_UpdateObjectHeader(yaffs_Object *in,const YCHAR *name, int force,int isShrink,int shadows)
++{
++
++      yaffs_BlockInfo *bi;
++      
++      yaffs_Device *dev = in->myDev;
++      
++    int prevChunkId;
++    int retVal = 0;
++    
++    int newChunkId;
++    yaffs_ExtendedTags newTags;
++    
++    __u8 *buffer = NULL;
++    YCHAR oldName[YAFFS_MAX_NAME_LENGTH+1];
++    
++   // __u8 bufferOld[YAFFS_BYTES_PER_CHUNK];
++    
++    yaffs_ObjectHeader *oh = NULL;
++    // yaffs_ObjectHeader *ohOld = (yaffs_ObjectHeader *)bufferOld;
++
++    
++    if(!in->fake || force)
++    {
++  
++              yaffs_CheckGarbageCollection(dev);              
++    
++          buffer = yaffs_GetTempBuffer(in->myDev,__LINE__);
++          oh  = (yaffs_ObjectHeader *)buffer;
++    
++              prevChunkId = in->chunkId;
++    
++              if(prevChunkId >= 0)
++              {
++                      yaffs_ReadChunkWithTagsFromNAND(dev,prevChunkId,buffer,NULL);
++                      memcpy(oldName,oh->name,sizeof(oh->name));      
++              }
++
++              memset(buffer,0xFF,dev->nBytesPerChunk);
++
++              // Header data
++              oh->type = in->variantType;
++              
++              oh->yst_mode = in->yst_mode;
++              
++              // shadowing
++              oh->shadowsObject = shadows;
++
++#ifdef CONFIG_YAFFS_WINCE
++              oh->win_atime[0] = in->win_atime[0];
++              oh->win_ctime[0] = in->win_ctime[0];
++              oh->win_mtime[0] = in->win_mtime[0];
++              oh->win_atime[1] = in->win_atime[1];
++              oh->win_ctime[1] = in->win_ctime[1];
++              oh->win_mtime[1] = in->win_mtime[1];
++#else
++              oh->yst_uid = in->yst_uid;
++              oh->yst_gid = in->yst_gid;
++              oh->yst_atime = in->yst_atime;
++              oh->yst_mtime = in->yst_mtime;
++              oh->yst_ctime = in->yst_ctime;
++              oh->yst_rdev = in->yst_rdev;
++#endif        
++              if(in->parent)
++              {
++                      oh->parentObjectId = in->parent->objectId;
++              }
++              else
++              {
++                      oh->parentObjectId = 0;
++              }
++              
++              //oh->sum = in->sum;
++              if(name && *name)
++              {
++                      memset(oh->name,0,sizeof(oh->name));
++                      yaffs_strncpy(oh->name,name,YAFFS_MAX_NAME_LENGTH);
++              }
++              else if(prevChunkId)
++              {       
++                      memcpy(oh->name, oldName,sizeof(oh->name));
++              }
++              else
++              {
++                      memset(oh->name,0,sizeof(oh->name));    
++              }
++              
++              oh->isShrink = isShrink;
++      
++              switch(in->variantType)
++              {
++                      case YAFFS_OBJECT_TYPE_UNKNOWN:         
++                              // Should not happen
++                              break;
++                      case YAFFS_OBJECT_TYPE_FILE:
++                              oh->fileSize = (oh->parentObjectId == YAFFS_OBJECTID_DELETED ||
++                                              oh->parentObjectId == YAFFS_OBJECTID_UNLINKED) ? 0 : in->variant.fileVariant.fileSize;
++                              break;
++                      case YAFFS_OBJECT_TYPE_HARDLINK:
++                              oh->equivalentObjectId = in->variant.hardLinkVariant.equivalentObjectId;
++                              break;
++                      case YAFFS_OBJECT_TYPE_SPECIAL: 
++                              // Do nothing
++                              break;
++                      case YAFFS_OBJECT_TYPE_DIRECTORY:       
++                              // Do nothing
++                              break;
++                      case YAFFS_OBJECT_TYPE_SYMLINK:
++                              yaffs_strncpy(oh->alias,in->variant.symLinkVariant.alias,YAFFS_MAX_ALIAS_LENGTH);
++                              oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0;
++                              break;
++              }
++
++              // Tags
++              yaffs_InitialiseTags(&newTags);
++              in->serial++;
++              newTags.chunkId = 0;
++              newTags.objectId = in->objectId;
++              newTags.serialNumber = in->serial;
++              
++              // Add extra info for file header
++              
++              newTags.extraHeaderInfoAvailable = 1;
++              newTags.extraParentObjectId = oh->parentObjectId;
++              newTags.extraFileLength = oh->fileSize;
++              newTags.extraIsShrinkHeader = oh->isShrink;
++              newTags.extraEquivalentObjectId = oh->equivalentObjectId;
++              newTags.extraShadows = (oh->shadowsObject > 0) ? 1 : 0;
++              newTags.extraObjectType  = in->variantType;
++              
++              // Create new chunk in NAND
++              newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,buffer,&newTags, (prevChunkId >= 0) ? 1 : 0 );
++    
++              if(newChunkId >= 0)
++              {
++              
++                      in->chunkId = newChunkId;       
++              
++                      if(prevChunkId >= 0)
++                      {
++                              yaffs_DeleteChunk(dev,prevChunkId,1,__LINE__);
++                      }
++              
++                      in->dirty = 0;
++                      
++                      // If this was a shrink, then mark the block that the chunk lives on
++                      if(isShrink)
++                      {
++                              bi = yaffs_GetBlockInfo(in->myDev,newChunkId / in->myDev->nChunksPerBlock);
++                              bi->hasShrinkHeader = 1;
++                      }
++                      
++              }
++              
++              retVal =  newChunkId;
++
++    }
++    
++      if(buffer) 
++              yaffs_ReleaseTempBuffer(dev,buffer,__LINE__);
++      
++    return retVal;
++}
++
++
++/////////////////////// Short Operations Cache ////////////////////////////////
++//    In many siturations where there is no high level buffering (eg WinCE) a lot of
++//    reads might be short sequential reads, and a lot of writes may be short 
++//  sequential writes. eg. scanning/writing a jpeg file.
++//    In these cases, a short read/write cache can provide a huge perfomance benefit 
++//  with dumb-as-a-rock code.
++//  There are a limited number (~10) of cache chunks per device so that we don't
++//  need a very intelligent search.
++
++
++
++
++
++static void yaffs_FlushFilesChunkCache(yaffs_Object *obj)
++{
++      yaffs_Device *dev = obj->myDev;
++      int lowest = -99; // Stop compiler whining.
++      int i;
++      yaffs_ChunkCache *cache;
++      int chunkWritten = 0;
++      //int nBytes;
++      int nCaches = obj->myDev->nShortOpCaches;
++      
++      if  (nCaches > 0)
++      {
++              do{
++                      cache = NULL;
++              
++                      // Find the dirty cache for this object with the lowest chunk id.
++                      for(i = 0; i < nCaches; i++)
++                      {
++                              if(dev->srCache[i].object == obj &&
++                                 dev->srCache[i].dirty)
++                              {
++                                      if(!cache ||  dev->srCache[i].chunkId < lowest)
++                                      {
++                                              cache = &dev->srCache[i];
++                                              lowest = cache->chunkId;
++                                      }
++                              }
++                      }
++              
++                      if(cache && !cache->locked)
++                      {
++                              //Write it out and free it up
++
++#if 0
++                              nBytes = cache->object->variant.fileVariant.fileSize - ((cache->chunkId -1) * YAFFS_BYTES_PER_CHUNK);
++                      
++                              if(nBytes > YAFFS_BYTES_PER_CHUNK)
++                              {
++                                      nBytes= YAFFS_BYTES_PER_CHUNK;
++                              }
++#endif                        
++                              chunkWritten = yaffs_WriteChunkDataToObject(cache->object,
++                                                                                                                      cache->chunkId,
++                                                                                                                      cache->data,
++                                                                                                                      cache->nBytes,1);
++
++                              cache->dirty = 0;
++                              cache->object = NULL;
++                      }
++              
++              } while(cache && chunkWritten > 0);
++      
++              if(cache)
++              {
++                      //Hoosterman, disk full while writing cache out.
++                      T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: no space during cache write" TENDSTR)));
++
++              }
++      }       
++              
++}
++
++
++// Grab us a chunk for use.
++// First look for an empty one. 
++// Then look for the least recently used non-dirty one.
++// Then look for the least recently used dirty one...., flush and look again.
++static yaffs_ChunkCache *yaffs_GrabChunkCacheWorker(yaffs_Device *dev)
++{
++      int i;
++      int usage;
++      int theOne;
++      
++      if(dev->nShortOpCaches > 0)
++      {
++              for(i = 0; i < dev->nShortOpCaches; i++)
++              {
++                      if(!dev->srCache[i].object)
++                      {
++                              //T(("Grabbing empty %d\n",i));
++                              
++                              //printf("Grabbing empty %d\n",i);
++                      
++                              return &dev->srCache[i];
++                      }
++              }
++              
++              return NULL;
++      
++              theOne = -1; 
++              usage = 0; // just to stop the compiler grizzling
++      
++              for(i = 0; i < dev->nShortOpCaches; i++)
++              {
++                      if(!dev->srCache[i].dirty &&
++                      ((dev->srCache[i].lastUse < usage  && theOne >= 0)|| 
++                              theOne < 0))
++                      {
++                              usage = dev->srCache[i].lastUse;
++                              theOne = i;
++                      }
++              }
++      
++              //T(("Grabbing non-empty %d\n",theOne));
++              
++              //if(theOne >= 0) printf("Grabbed non-empty cache %d\n",theOne);
++              
++              return  theOne >= 0 ?  &dev->srCache[theOne] : NULL;
++      }
++      else
++      {
++              return NULL;
++      }
++      
++}
++
++
++static yaffs_ChunkCache *yaffs_GrabChunkCache(yaffs_Device *dev)
++{
++      yaffs_ChunkCache *cache;
++      yaffs_Object *theObj;
++      int usage;
++      int i;
++      int pushout;
++      
++      if(dev->nShortOpCaches > 0)
++      {
++              // Try find a non-dirty one...
++      
++              cache = yaffs_GrabChunkCacheWorker(dev);
++      
++              if(!cache)
++              {
++                      // They were all dirty, find the last recently used object and flush
++                      // its cache, then  find again.
++                      // NB what's here is not very accurate, we actually flush the object
++                      // the last recently used page.
++                      
++                      // With locking we can't assume we can use entry zero
++                      
++              
++                      theObj = NULL;
++                      usage = -1;
++                      cache = NULL;
++                      pushout = -1;
++      
++                      for(i = 0; i < dev->nShortOpCaches; i++)
++                      {
++                              if( dev->srCache[i].object && 
++                                      !dev->srCache[i].locked &&
++                                      (dev->srCache[i].lastUse < usage || !cache))
++                              {
++                                      usage  = dev->srCache[i].lastUse;
++                                      theObj = dev->srCache[i].object;
++                                      cache = &dev->srCache[i];
++                                      pushout = i;
++                              }
++                      }
++              
++                      if(!cache || cache->dirty)
++                      {
++                      
++                              //printf("Dirty ");
++                              yaffs_FlushFilesChunkCache(theObj);
++              
++                              // Try again
++                              cache = yaffs_GrabChunkCacheWorker(dev);
++                      }
++                      else
++                      {
++                              //printf(" pushout %d\n",pushout);
++                      }
++                      
++              }
++              return cache;
++      }
++      else
++              return NULL;
++
++}
++
++
++// Find a cached chunk
++static yaffs_ChunkCache *yaffs_FindChunkCache(const yaffs_Object *obj, int chunkId)
++{
++      yaffs_Device *dev = obj->myDev;
++      int i;
++      if(dev->nShortOpCaches > 0)
++      {
++              for(i = 0; i < dev->nShortOpCaches; i++)
++              {
++                      if(dev->srCache[i].object == obj && 
++                      dev->srCache[i].chunkId == chunkId)
++                      {
++                              dev->cacheHits++;
++                      
++                              return &dev->srCache[i];
++                      }
++              }
++      }
++      return NULL;
++}
++
++// Mark the chunk for the least recently used algorithym
++static void yaffs_UseChunkCache(yaffs_Device *dev, yaffs_ChunkCache *cache, int isAWrite)
++{
++
++      if(dev->nShortOpCaches > 0)
++      {
++              if( dev->srLastUse < 0 || 
++                      dev->srLastUse > 100000000)
++              {
++                      // Reset the cache usages
++                      int i;
++                      for(i = 1; i < dev->nShortOpCaches; i++)
++                      {
++                              dev->srCache[i].lastUse = 0;
++                      }
++                      dev->srLastUse = 0;
++              }
++
++              dev->srLastUse++;
++      
++              cache->lastUse = dev->srLastUse;
++
++              if(isAWrite)
++              {
++                      cache->dirty = 1;
++              }
++      }
++}
++
++// Invalidate a single cache page.
++// Do this when a whole page gets written,
++// ie the short cache for this page is no longer valid.
++static void yaffs_InvalidateChunkCache(yaffs_Object *object, int chunkId)
++{
++      if(object->myDev->nShortOpCaches > 0)
++      {
++              yaffs_ChunkCache *cache = yaffs_FindChunkCache(object,chunkId);
++
++              if(cache)
++              {
++                      cache->object = NULL;
++              }
++      }
++}
++
++
++// Invalidate all the cache pages associated with this object
++// Do this whenever ther file is deleted or resized.
++static void yaffs_InvalidateWholeChunkCache(yaffs_Object *in)
++{
++      int i;
++      yaffs_Device *dev = in->myDev;
++      
++      if(dev->nShortOpCaches > 0)
++      { 
++              // Now invalidate it.
++              for(i = 0; i < dev->nShortOpCaches; i++)
++              {
++                      if(dev->srCache[i].object == in)
++                      {
++                              dev->srCache[i].object = NULL;
++                      }
++              }
++      }
++}
++
++
++
++
++
++///////////////////////// File read/write ///////////////////////////////
++// Read and write have very similar structures.
++// In general the read/write has three parts to it
++// * An incomplete chunk to start with (if the read/write is not chunk-aligned)
++// * Some complete chunks
++// * An incomplete chunk to end off with
++//
++// Curve-balls: the first chunk might also be the last chunk.
++
++int yaffs_ReadDataFromFile(yaffs_Object *in, __u8 * buffer, __u32 offset, int nBytes)
++{
++      
++      
++      int chunk;
++      int start;
++      int nToCopy;
++      int n = nBytes;
++      int nDone = 0;
++      yaffs_ChunkCache *cache;
++      
++      yaffs_Device *dev;
++      
++      dev = in->myDev;
++      
++      while(n > 0)
++      {
++              chunk = offset / dev->nBytesPerChunk + 1; // The first chunk is 1
++              start = offset % dev->nBytesPerChunk;
++
++              // OK now check for the curveball where the start and end are in
++              // the same chunk.      
++              if(     (start + n) < dev->nBytesPerChunk)
++              {
++                      nToCopy = n;
++              }
++              else
++              {
++                      nToCopy = dev->nBytesPerChunk - start;
++              }
++      
++              cache = yaffs_FindChunkCache(in,chunk);
++              
++              // If the chunk is already in the cache or it is less than a whole chunk
++              // then use the cache (if there is caching)
++              // else bypass the cache.
++              if( cache || nToCopy != dev->nBytesPerChunk)
++              {
++                      if(dev->nShortOpCaches > 0)
++                      {
++                              
++                              // If we can't find the data in the cache, then load it up.
++                              
++                              if(!cache)
++                              {
++                                      cache = yaffs_GrabChunkCache(in->myDev);
++                                      cache->object = in;
++                                      cache->chunkId = chunk;
++                                      cache->dirty = 0;
++                                      cache->locked = 0;
++                                      yaffs_ReadChunkDataFromObject(in,chunk,cache->data);
++                                      cache->nBytes = 0;      
++                              }
++                      
++                              yaffs_UseChunkCache(dev,cache,0);
++
++                              cache->locked = 1;
++
++#ifdef CONFIG_YAFFS_WINCE
++                              yfsd_UnlockYAFFS(TRUE);
++#endif
++                              memcpy(buffer,&cache->data[start],nToCopy);
++
++#ifdef CONFIG_YAFFS_WINCE
++                              yfsd_LockYAFFS(TRUE);
++#endif
++                              cache->locked = 0;
++                      }
++                      else
++                      {
++                              // Read into the local buffer then copy...
++                              
++                              __u8 *localBuffer = yaffs_GetTempBuffer(dev,__LINE__);
++                              yaffs_ReadChunkDataFromObject(in,chunk,localBuffer);            
++#ifdef CONFIG_YAFFS_WINCE
++                              yfsd_UnlockYAFFS(TRUE);
++#endif
++                              memcpy(buffer,&localBuffer[start],nToCopy);
++
++#ifdef CONFIG_YAFFS_WINCE
++                              yfsd_LockYAFFS(TRUE);
++#endif
++                              yaffs_ReleaseTempBuffer(dev,localBuffer,__LINE__);
++                      }
++
++              }
++              else
++              {
++#ifdef CONFIG_YAFFS_WINCE
++                      __u8 *localBuffer = yaffs_GetTempBuffer(dev,__LINE__);
++                      
++                      // Under WinCE can't do direct transfer. Need to use a local buffer.
++                      // This is because we otherwise screw up WinCE's memory mapper
++                      yaffs_ReadChunkDataFromObject(in,chunk,localBuffer);
++
++#ifdef CONFIG_YAFFS_WINCE
++                              yfsd_UnlockYAFFS(TRUE);
++#endif
++                      memcpy(buffer,localBuffer,dev->nBytesPerChunk);
++
++#ifdef CONFIG_YAFFS_WINCE
++                              yfsd_LockYAFFS(TRUE);
++                              yaffs_ReleaseTempBuffer(dev,localBuffer,__LINE__);
++#endif
++
++#else
++                      // A full chunk. Read directly into the supplied buffer.
++                      yaffs_ReadChunkDataFromObject(in,chunk,buffer);
++#endif
++              }
++              
++              n -= nToCopy;
++              offset += nToCopy;
++              buffer += nToCopy;
++              nDone += nToCopy;
++              
++      }
++      
++      return nDone;
++}
++
++
++
++int yaffs_WriteDataToFile(yaffs_Object *in,const __u8 * buffer, __u32 offset, int nBytes, int writeThrough)
++{     
++      
++      int chunk;
++      int start;
++      int nToCopy;
++      int n = nBytes;
++      int nDone = 0;
++      int nToWriteBack;
++      int startOfWrite = offset;
++      int chunkWritten = 0;
++      int nBytesRead;
++      
++      yaffs_Device *dev;
++      
++      dev = in->myDev;
++      
++      
++      while(n > 0 && chunkWritten >= 0)
++      {
++              chunk = offset / dev->nBytesPerChunk + 1;
++              start = offset % dev->nBytesPerChunk;
++              
++
++              // OK now check for the curveball where the start and end are in
++              // the same chunk.
++              
++              if((start + n) < dev->nBytesPerChunk)
++              {
++                      nToCopy = n;
++                      
++                      // Now folks, to calculate how many bytes to write back....
++                      // If we're overwriting and not writing to then end of file then
++                      // we need to write back as much as was there before.
++                      
++                      nBytesRead = in->variant.fileVariant.fileSize - ((chunk -1) * dev->nBytesPerChunk);
++                      
++                      if(nBytesRead > dev->nBytesPerChunk)
++                      {
++                              nBytesRead = dev->nBytesPerChunk;
++                      }
++                      
++                      nToWriteBack = (nBytesRead > (start + n)) ? nBytesRead : (start +n);
++                      
++              }
++              else
++              {
++                      nToCopy = dev->nBytesPerChunk - start;
++                      nToWriteBack = dev->nBytesPerChunk;
++              }
++      
++              if(nToCopy != dev->nBytesPerChunk)
++              {
++                      // An incomplete start or end chunk (or maybe both start and end chunk)
++                      if(dev->nShortOpCaches > 0)
++                         {
++                              yaffs_ChunkCache *cache;
++                              // If we can't find the data in the cache, then load it up.
++                              cache = yaffs_FindChunkCache(in,chunk);
++                              if(!cache && yaffs_CheckSpaceForAllocation(in->myDev))
++                              {
++                                      cache = yaffs_GrabChunkCache(in->myDev);
++                                      cache->object = in;
++                                      cache->chunkId = chunk;
++                                      cache->dirty = 0;
++                                      cache->locked = 0;
++                                      yaffs_ReadChunkDataFromObject(in,chunk,cache->data);            
++                              }
++                      
++                              if(cache)
++                              {       
++                                      yaffs_UseChunkCache(dev,cache,1);
++                                      cache->locked = 1;
++#ifdef CONFIG_YAFFS_WINCE
++                              yfsd_UnlockYAFFS(TRUE);
++#endif
++      
++                                      memcpy(&cache->data[start],buffer,nToCopy);
++
++#ifdef CONFIG_YAFFS_WINCE
++                              yfsd_LockYAFFS(TRUE);
++#endif
++                                      cache->locked = 0;
++                                      cache->nBytes = nToWriteBack;
++                                      
++                                      if(writeThrough)
++                                      {
++                                              chunkWritten = yaffs_WriteChunkDataToObject(cache->object,
++                                                                                          cache->chunkId,
++                                                                                          cache->data,
++                                                                                          cache->nBytes,1);
++                                              cache->dirty = 0;
++                                      }
++
++                              }
++                              else
++                              {
++                                      chunkWritten = -1; // fail the write
++                              }
++                      }
++                      else
++                      {
++                              // An incomplete start or end chunk (or maybe both start and end chunk)
++                              // Read into the local buffer then copy, then copy over and write back.
++                              
++                              __u8 *localBuffer = yaffs_GetTempBuffer(dev,__LINE__);
++              
++                              yaffs_ReadChunkDataFromObject(in,chunk,localBuffer);
++                              
++#ifdef CONFIG_YAFFS_WINCE
++                              yfsd_UnlockYAFFS(TRUE);
++#endif
++                                      
++                              memcpy(&localBuffer[start],buffer,nToCopy);
++                      
++#ifdef CONFIG_YAFFS_WINCE
++                              yfsd_LockYAFFS(TRUE);
++#endif
++                              chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,localBuffer,nToWriteBack,0);
++                              
++                              yaffs_ReleaseTempBuffer(dev,localBuffer,__LINE__);
++                      
++                              //T(("Write with readback to chunk %d %d  start %d  copied %d wrote back %d\n",chunk,chunkWritten,start, nToCopy, nToWriteBack));
++                      }
++                      
++              }
++              else
++              {
++                      
++#ifdef CONFIG_YAFFS_WINCE
++                      // Under WinCE can't do direct transfer. Need to use a local buffer.
++                      // This is because we otherwise screw up WinCE's memory mapper
++                              __u8 *localBuffer = yaffs_GetTempBuffer(dev,__LINE__);
++#ifdef CONFIG_YAFFS_WINCE
++                              yfsd_UnlockYAFFS(TRUE);
++#endif
++                      memcpy(localBuffer,buffer,dev->nBytesPerChunk);
++#ifdef CONFIG_YAFFS_WINCE
++                              yfsd_LockYAFFS(TRUE);
++#endif
++                      chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,localBuffer,dev->nBytesPerChunk,0);
++                      yaffs_ReleaseTempBuffer(dev,localBuffer,__LINE__);
++#else
++                      // A full chunk. Write directly from the supplied buffer.
++                      chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,buffer,dev->nBytesPerChunk,0);
++#endif
++                      // Since we've overwritten the cached data, we better invalidate it.
++                      yaffs_InvalidateChunkCache(in,chunk);
++                      //T(("Write to chunk %d %d\n",chunk,chunkWritten));
++              }
++              
++              if(chunkWritten >= 0)
++              {
++                      n -= nToCopy;
++                      offset += nToCopy;
++                      buffer += nToCopy;
++                      nDone += nToCopy;
++              }
++              
++      }
++      
++      // Update file object
++      
++      if((startOfWrite + nDone) > in->variant.fileVariant.fileSize)
++      {
++              in->variant.fileVariant.fileSize = (startOfWrite + nDone);
++      }
++      
++      in->dirty = 1;
++      
++      return nDone;
++}
++
++static void yaffs_PruneResizedChunks(yaffs_Object *in, int newSize)
++{
++
++      yaffs_Device *dev = in->myDev;
++      int oldFileSize = in->variant.fileVariant.fileSize;
++
++      
++      int lastDel = 1 + (oldFileSize-1)/dev->nBytesPerChunk;
++              
++      int startDel = 1 + (newSize + dev->nBytesPerChunk - 1)/
++                                              dev->nBytesPerChunk;
++      int i;
++      int chunkId;
++
++      // Delete backwards so that we don't end up with holes if
++      // power is lost part-way through the operation.
++      for(i = lastDel; i >= startDel; i--)
++      {
++              // NB this could be optimised somewhat,
++              // eg. could retrieve the tags and write them without
++              // using yaffs_DeleteChunk
++
++              chunkId = yaffs_FindAndDeleteChunkInFile(in,i,NULL);
++              if(chunkId > 0)
++              {
++                      if(chunkId < (dev->internalStartBlock * dev->nChunksPerBlock) || 
++                     chunkId >= ((dev->internalEndBlock+1) * dev->nChunksPerBlock))
++                      {
++                              T(YAFFS_TRACE_ALWAYS,(TSTR("Found daft chunkId %d for %d"TENDSTR),chunkId,i));
++                      }
++                      else
++                      {
++                              in->nDataChunks--;
++                              yaffs_DeleteChunk(dev,chunkId,1,__LINE__);
++                      }
++              }
++      }
++              
++}
++
++int yaffs_ResizeFile(yaffs_Object *in, int newSize)
++{
++
++      int oldFileSize = in->variant.fileVariant.fileSize;
++      int sizeOfPartialChunk;
++      yaffs_Device *dev = in->myDev;
++      
++       sizeOfPartialChunk  = newSize % dev->nBytesPerChunk;
++              
++
++      yaffs_FlushFilesChunkCache(in); 
++      yaffs_InvalidateWholeChunkCache(in);
++
++      yaffs_CheckGarbageCollection(dev);
++      
++      if(in->variantType != YAFFS_OBJECT_TYPE_FILE)
++      {
++              return yaffs_GetFileSize(in);
++      }
++      
++      if(newSize < oldFileSize)
++      {
++
++              yaffs_PruneResizedChunks(in,newSize);
++              
++              if(sizeOfPartialChunk != 0)
++              {
++                      int lastChunk = 1+ newSize/dev->nBytesPerChunk;
++                      __u8 *localBuffer = yaffs_GetTempBuffer(dev,__LINE__);
++                      
++                      // Got to read and rewrite the last chunk with its new size and zero pad
++                      yaffs_ReadChunkDataFromObject(in,lastChunk,localBuffer);
++                      
++                      memset(localBuffer + sizeOfPartialChunk,0, dev->nBytesPerChunk - sizeOfPartialChunk);
++                      
++                      yaffs_WriteChunkDataToObject(in,lastChunk,localBuffer,sizeOfPartialChunk,1);
++                              
++                      yaffs_ReleaseTempBuffer(dev,localBuffer,__LINE__);
++              }
++              
++              in->variant.fileVariant.fileSize = newSize;
++              
++              yaffs_PruneFileStructure(dev,&in->variant.fileVariant);
++              
++              // Write a new object header to show we've shrunk the file
++              // Do this only if the file is not in the deleted directories.
++              if(in->parent->objectId != YAFFS_OBJECTID_UNLINKED &&
++                 in->parent->objectId != YAFFS_OBJECTID_DELETED
++                )
++              {
++                      yaffs_UpdateObjectHeader(in,NULL, 0, 1,0);
++              }
++
++              
++              return newSize;
++              
++      }
++      else
++      {
++              return oldFileSize;
++      }
++}
++
++
++loff_t yaffs_GetFileSize(yaffs_Object *obj)
++{
++      obj = yaffs_GetEquivalentObject(obj);
++      
++      switch(obj->variantType)
++      {
++              case YAFFS_OBJECT_TYPE_FILE: 
++                      return obj->variant.fileVariant.fileSize;
++              case YAFFS_OBJECT_TYPE_SYMLINK:
++                      return yaffs_strlen(obj->variant.symLinkVariant.alias);
++              default:
++                      return 0;
++      }
++}
++
++
++
++// yaffs_FlushFile() updates the file's
++// objectId in NAND
++
++int yaffs_FlushFile(yaffs_Object *in, int updateTime)
++{
++      int retVal;
++      if(in->dirty)
++      {
++              //T(("flushing object header\n"));
++              
++              yaffs_FlushFilesChunkCache(in);
++              if(updateTime)
++              {
++#ifdef CONFIG_YAFFS_WINCE
++                      yfsd_WinFileTimeNow(in->win_mtime);
++#else
++
++                      in->yst_mtime = Y_CURRENT_TIME;
++
++#endif
++              }
++
++              retVal = (yaffs_UpdateObjectHeader(in,NULL,0,0,0) >= 0)? YAFFS_OK : YAFFS_FAIL;
++      }
++      else
++      {
++              retVal = YAFFS_OK;
++      }
++      
++      return retVal;
++      
++}
++
++
++static int yaffs_DoGenericObjectDeletion(yaffs_Object *in)
++{
++
++      // First off, invalidate the file's data in the cache, without flushing.
++      yaffs_InvalidateWholeChunkCache(in);
++
++      if(in->myDev->isYaffs2 && (in->parent != in->myDev->deletedDir))
++      {
++              // Move to the unlinked directory so we have a record that it was deleted.
++              yaffs_ChangeObjectName(in, in->myDev->deletedDir,NULL,0,0);
++
++      }
++
++      
++      yaffs_RemoveObjectFromDirectory(in);
++      yaffs_DeleteChunk(in->myDev,in->chunkId,1,__LINE__);
++      in->chunkId = -1;
++#if 0
++#ifdef __KERNEL__
++      if(in->myInode)
++      {
++              in->myInode->u.generic_ip = NULL;
++              in->myInode = 0;
++      }
++#endif
++#endif
++
++      yaffs_FreeObject(in);
++      return YAFFS_OK;
++
++}
++
++// yaffs_DeleteFile deletes the whole file data
++// and the inode associated with the file.
++// It does not delete the links associated with the file.
++static int yaffs_UnlinkFile(yaffs_Object *in)
++{
++
++#ifdef CONFIG_YAFFS_DISABLE_BACKGROUND_DELETION
++
++      // Delete the file data & tnodes
++
++       yaffs_DeleteWorker(in, in->variant.fileVariant.top, in->variant.fileVariant.topLevel, 0,NULL);
++       
++
++      yaffs_FreeTnode(in->myDev,in->variant.fileVariant.top);
++      
++      return  yaffs_DoGenericObjectDeletion(in);
++#else
++      int retVal;
++      int immediateDeletion=0;
++
++      if(1)
++      {
++              //in->unlinked = 1;
++              //in->myDev->nUnlinkedFiles++;
++              //in->renameAllowed = 0;
++#ifdef __KERNEL__
++              if(!in->myInode)
++              {
++                      immediateDeletion = 1;
++
++              }
++#else
++              if(in->inUse <= 0)
++              {
++                      immediateDeletion = 1;
++
++              }
++#endif
++              if(immediateDeletion)
++              {
++                      retVal = yaffs_ChangeObjectName(in, in->myDev->deletedDir,NULL,0,0);
++                      T(YAFFS_TRACE_TRACING,(TSTR("yaffs: immediate deletion of file %d" TENDSTR),in->objectId));
++                      in->deleted=1;
++                      in->myDev->nDeletedFiles++;
++                      if( 0 && in->myDev->isYaffs2)
++                      {
++                              yaffs_ResizeFile(in,0);
++                      }
++                      yaffs_SoftDeleteFile(in);
++              }
++              else
++              {
++                      retVal = yaffs_ChangeObjectName(in, in->myDev->unlinkedDir,NULL,0,0);
++              }
++      
++      }
++      return retVal;
++
++      
++#endif
++}
++
++int yaffs_DeleteFile(yaffs_Object *in)
++{
++      int retVal = YAFFS_OK;
++      
++      if(in->nDataChunks > 0)
++      {
++              // Use soft deletion
++              if(!in->unlinked)
++              {
++                      retVal = yaffs_UnlinkFile(in);
++              }
++              if(retVal == YAFFS_OK && 
++              in->unlinked &&
++              !in->deleted)
++              {
++                      in->deleted = 1;
++                      in->myDev->nDeletedFiles++;
++                      yaffs_SoftDeleteFile(in);
++              }
++              return in->deleted ? YAFFS_OK : YAFFS_FAIL;     
++      }
++      else
++      {
++              // The file has no data chunks so we toss it immediately
++              yaffs_FreeTnode(in->myDev,in->variant.fileVariant.top);
++              in->variant.fileVariant.top = NULL;
++              yaffs_DoGenericObjectDeletion(in);      
++              
++              return YAFFS_OK;        
++      }
++}
++
++static int yaffs_DeleteDirectory(yaffs_Object *in)
++{
++      //First check that the directory is empty.
++      if(list_empty(&in->variant.directoryVariant.children))
++      {
++              return  yaffs_DoGenericObjectDeletion(in);
++      }
++      
++      return YAFFS_FAIL;
++      
++}
++
++static int yaffs_DeleteSymLink(yaffs_Object *in)
++{
++      YFREE(in->variant.symLinkVariant.alias);
++
++      return  yaffs_DoGenericObjectDeletion(in);
++}
++
++static int yaffs_DeleteHardLink(yaffs_Object *in)
++{
++      // remove this hardlink from the list assocaited with the equivalent
++      // object
++      list_del(&in->hardLinks);
++      return  yaffs_DoGenericObjectDeletion(in);      
++}
++
++
++static void yaffs_DestroyObject(yaffs_Object *obj)
++{
++      switch(obj->variantType)
++      {
++              case YAFFS_OBJECT_TYPE_FILE: yaffs_DeleteFile(obj); break;
++              case YAFFS_OBJECT_TYPE_DIRECTORY: yaffs_DeleteDirectory(obj); break;
++              case YAFFS_OBJECT_TYPE_SYMLINK: yaffs_DeleteSymLink(obj); break;
++              case YAFFS_OBJECT_TYPE_HARDLINK: yaffs_DeleteHardLink(obj); break;
++              case YAFFS_OBJECT_TYPE_SPECIAL: yaffs_DoGenericObjectDeletion(obj); break;
++              case YAFFS_OBJECT_TYPE_UNKNOWN: break; // should not happen.
++      }
++}
++
++
++static int yaffs_UnlinkWorker(yaffs_Object *obj)
++{
++
++      
++      if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK)
++      {
++              return  yaffs_DeleteHardLink(obj);
++      }
++      else if(!list_empty(&obj->hardLinks))
++      {       
++              // Curve ball: We're unlinking an object that has a hardlink.
++              //
++              //      This problem arises because we are not strictly following
++              //  The Linux link/inode model.
++              //
++              // We can't really delete the object.
++              // Instead, we do the following:
++              // - Select a hardlink.
++              // - Unhook it from the hard links
++              // - Unhook it from its parent directory (so that the rename can work)
++              // - Rename the object to the hardlink's name.
++              // - Delete the hardlink
++              
++              
++              yaffs_Object *hl;
++              int retVal;
++              YCHAR name[YAFFS_MAX_NAME_LENGTH+1];
++              
++              hl = list_entry(obj->hardLinks.next,yaffs_Object,hardLinks);
++              
++              list_del_init(&hl->hardLinks);
++              list_del_init(&hl->siblings);
++              
++              yaffs_GetObjectName(hl,name,YAFFS_MAX_NAME_LENGTH+1);
++              
++              retVal = yaffs_ChangeObjectName(obj, hl->parent, name,0,0);
++              
++              if(retVal == YAFFS_OK)
++              {
++                      retVal = yaffs_DoGenericObjectDeletion(hl);
++              }
++              return retVal;
++                              
++      }
++      else
++      {
++              switch(obj->variantType)
++              {
++                      case YAFFS_OBJECT_TYPE_FILE:
++                              return yaffs_UnlinkFile(obj);
++                              break;
++                      case YAFFS_OBJECT_TYPE_DIRECTORY:
++                              return yaffs_DeleteDirectory(obj);
++                              break;
++                      case YAFFS_OBJECT_TYPE_SYMLINK:
++                              return yaffs_DeleteSymLink(obj);
++                              break;
++                      case YAFFS_OBJECT_TYPE_SPECIAL:
++                              return yaffs_DoGenericObjectDeletion(obj);
++                              break;
++                      case YAFFS_OBJECT_TYPE_HARDLINK:
++                      case YAFFS_OBJECT_TYPE_UNKNOWN:
++                      default:
++                              return YAFFS_FAIL;
++              }
++      }
++}
++
++int yaffs_Unlink(yaffs_Object *dir, const YCHAR *name)
++{
++      yaffs_Object *obj;
++      
++       obj = yaffs_FindObjectByName(dir,name);
++       
++       if(obj && obj->unlinkAllowed)
++       {
++              return yaffs_UnlinkWorker(obj);
++       }
++       
++       return YAFFS_FAIL;
++      
++}
++
++//////////////// Initialisation Scanning /////////////////
++
++
++void yaffs_HandleShadowedObject(yaffs_Device *dev, int objId, int backwardScanning)
++{
++      //Todo
++}
++
++#if 0
++// For now we use the SmartMedia check.
++// We look at the blockStatus byte in the first two chunks
++// These must be 0xFF to pass as OK.
++// todo: this function needs to be modifyable foir different NAND types
++// and different chunk sizes.  Suggest make this into a per-device configurable
++// function.
++static int yaffs_IsBlockBad(yaffs_Device *dev, int blk)
++{
++      yaffsExtendedTags *tags;
++      
++      yaffs_ReadChunkFromNAND(dev,blk * dev->nChunksPerBlock,NULL,&tags,1);
++#if 1
++      if(yaffs_CountBits(spare.blockStatus) < 7)
++      {
++              return 1;
++      }
++#else
++      if(spare.blockStatus != 0xFF)
++      {
++              return 1;
++      }
++#endif
++      yaffs_ReadChunkFromNAND(dev,blk * dev->nChunksPerBlock + 1,NULL,&spare,1);
++
++#if 1
++      if(yaffs_CountBits(spare.blockStatus) < 7)
++      {
++              return 1;
++      }
++#else
++      if(spare.blockStatus != 0xFF)
++      {
++              return 1;
++      }
++#endif
++      
++      return 0;
++      
++}
++
++#endif
++
++
++typedef struct 
++{
++      int seq;
++      int block;
++} yaffs_BlockIndex;
++
++
++
++static int yaffs_Scan(yaffs_Device *dev)
++{
++      yaffs_ExtendedTags tags;
++      int blk;
++      int blockIterator;
++      int startIterator;
++      int endIterator;
++      int nBlocksToScan = 0;
++      
++      int chunk;
++      int c;
++      int deleted;
++      yaffs_BlockState state;
++      yaffs_Object *hardList = NULL;
++      yaffs_Object *hl;
++      yaffs_BlockInfo *bi;
++      int sequenceNumber;     
++      yaffs_ObjectHeader *oh;
++      yaffs_Object *in;
++      yaffs_Object *parent;
++      int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1;
++      
++      __u8 *chunkData;
++
++      yaffs_BlockIndex *blockIndex = NULL;
++
++      T(YAFFS_TRACE_SCAN,(TSTR("yaffs_Scan starts  intstartblk %d intendblk %d..." TENDSTR),dev->internalStartBlock,dev->internalEndBlock));
++      
++      chunkData = yaffs_GetTempBuffer(dev,__LINE__);
++      
++      
++      dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER;
++      
++      if(dev->isYaffs2)
++      {
++              blockIndex = YMALLOC(nBlocks * sizeof(yaffs_BlockIndex));               
++      }
++      
++      
++      // Scan all the blocks to determine their state
++      for(blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++)
++      {
++              bi = yaffs_GetBlockInfo(dev,blk);
++              yaffs_ClearChunkBits(dev,blk);
++              bi->pagesInUse = 0;
++              bi->softDeletions = 0;
++                              
++              yaffs_QueryInitialBlockState(dev,blk,&state,&sequenceNumber);
++              
++              bi->blockState = state;
++              bi->sequenceNumber = sequenceNumber;
++
++              T(YAFFS_TRACE_SCAN_DEBUG,(TSTR("Block scanning block %d state %d seq %d" TENDSTR),blk,state,sequenceNumber));
++              
++              if(state == YAFFS_BLOCK_STATE_DEAD)
++              {
++                      T(YAFFS_TRACE_BAD_BLOCKS,(TSTR("block %d is bad" TENDSTR),blk));
++              }
++              else if(state == YAFFS_BLOCK_STATE_EMPTY)
++              {
++                      T(YAFFS_TRACE_SCAN_DEBUG,(TSTR("Block empty " TENDSTR)));
++                      dev->nErasedBlocks++;
++                      dev->nFreeChunks += dev->nChunksPerBlock;
++              }
++              else if(state == YAFFS_BLOCK_STATE_NEEDS_SCANNING)
++              {
++                                      
++                      // Determine the highest sequence number
++                      if( dev->isYaffs2 &&
++                          sequenceNumber >= YAFFS_LOWEST_SEQUENCE_NUMBER &&
++                          sequenceNumber < YAFFS_HIGHEST_SEQUENCE_NUMBER)
++                       {
++                              
++                              blockIndex[nBlocksToScan].seq = sequenceNumber;
++                              blockIndex[nBlocksToScan].block = blk;
++                              
++                              nBlocksToScan++;
++
++                              if(sequenceNumber >= dev->sequenceNumber)
++                              {
++                                      dev->sequenceNumber = sequenceNumber;
++                              }
++                      }
++                      else if(dev->isYaffs2)
++                      {
++                              // TODO: Nasty sequence number!
++                              T(YAFFS_TRACE_SCAN,(TSTR("Block scanning block %d has bad sequence number %d" TENDSTR),blk,sequenceNumber));
++
++                      }
++              }
++      }
++      
++      // Sort the blocks
++      // Dungy old bubble sort for now...
++      if(dev->isYaffs2)
++      {
++              yaffs_BlockIndex temp;
++              int i;
++              int j;
++              
++              for(i = 0; i < nBlocksToScan; i++)
++                      for(j = i+1; j < nBlocksToScan; j++)
++                       if(blockIndex[i].seq > blockIndex[j].seq)
++                       {
++                              temp = blockIndex[j];
++                              blockIndex[j] = blockIndex[i];
++                              blockIndex[i] = temp;
++                       }
++      }
++      
++      
++      // Now scan the blocks looking at the data.
++      if(dev->isYaffs2)
++      {
++              startIterator = 0;
++              endIterator = nBlocksToScan-1;
++              T(YAFFS_TRACE_SCAN_DEBUG,(TSTR("%d blocks to be scanned" TENDSTR),nBlocksToScan));
++      }
++      else
++      {
++              startIterator = dev->internalStartBlock;
++              endIterator = dev->internalEndBlock;
++      }
++      
++      // For each block....
++      for(blockIterator = startIterator; blockIterator <= endIterator; blockIterator++)
++      {
++      
++              if(dev->isYaffs2)
++              {
++                      // get the block to scan in the correct order
++                      blk = blockIndex[blockIterator].block;
++              }
++              else
++              {
++                      blk = blockIterator;
++              }
++
++
++              bi = yaffs_GetBlockInfo(dev,blk);
++              state = bi->blockState;
++              
++              deleted = 0;
++              
++              // For each chunk in each block that needs scanning....
++              for(c = 0; c < dev->nChunksPerBlock && 
++                                 state == YAFFS_BLOCK_STATE_NEEDS_SCANNING; c++)
++              {
++                      // Read the tags and decide what to do
++                      chunk = blk * dev->nChunksPerBlock + c;
++                      
++                      yaffs_ReadChunkWithTagsFromNAND(dev,chunk,NULL,&tags);
++
++                      // Let's have a good look at this chunk...
++      
++                      
++                      if(!dev->isYaffs2 && tags.chunkDeleted)
++                      {
++                              // YAFFS1 only...
++                              // A deleted chunk
++                              deleted++;
++                              dev->nFreeChunks ++;
++                              //T((" %d %d deleted\n",blk,c));
++                      }
++                      else if(!tags.chunkUsed)
++                      {
++                              // An unassigned chunk in the block
++                              // This means that either the block is empty or 
++                              // this is the one being allocated from
++                              
++                              if(c == 0)
++                              {
++                                      // We're looking at the first chunk in the block so the block is unused
++                                      state = YAFFS_BLOCK_STATE_EMPTY;
++                                      dev->nErasedBlocks++;
++                              }
++                              else
++                              {
++                                      // this is the block being allocated from
++                                      T(YAFFS_TRACE_SCAN,(TSTR(" Allocating from %d %d" TENDSTR),blk,c));
++                                      state = YAFFS_BLOCK_STATE_ALLOCATING;
++                                      dev->allocationBlock = blk;
++                                      dev->allocationPage = c;
++                                      dev->allocationBlockFinder = blk; // Set it to here to encourage the allocator to
++                                                                                                        // go forth from here.
++                                      //Yaffs2 sanity check:
++                                      // This should be the one with the highest sequence number
++                                      if(dev->isYaffs2 && (dev->sequenceNumber != bi->sequenceNumber))
++                                      {
++                                              T(YAFFS_TRACE_ALWAYS,
++                                                              (TSTR("yaffs: Allocation block %d was not highest sequence id: block seq = %d, dev seq = %d" TENDSTR),
++                                                              blk,bi->sequenceNumber,dev->sequenceNumber));
++                                      }
++                              }
++
++                              dev->nFreeChunks += (dev->nChunksPerBlock - c);
++                      }
++                      else if(tags.chunkId > 0)
++                      {
++                              // chunkId > 0 so it is a data chunk...
++                              unsigned int endpos;
++
++                              yaffs_SetChunkBit(dev,blk,c);
++                              bi->pagesInUse++;
++                                                              
++                              in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,YAFFS_OBJECT_TYPE_FILE);
++                              // PutChunkIntoFile checks for a clash (two data chunks with
++                              // the same chunkId).
++                              yaffs_PutChunkIntoFile(in,tags.chunkId,chunk,1);
++                              endpos = (tags.chunkId - 1)* dev->nBytesPerChunk + tags.byteCount;
++                              if(in->variantType == YAFFS_OBJECT_TYPE_FILE && in->variant.fileVariant.scannedFileSize <endpos)
++                              {
++                                      in->variant.fileVariant.scannedFileSize = endpos;
++                                      if(!dev->useHeaderFileSize)
++                                      {       
++                                                      in->variant.fileVariant.fileSize = in->variant.fileVariant.scannedFileSize;
++                                      }
++
++                              }
++                              //T((" %d %d data %d %d\n",blk,c,tags.objectId,tags.chunkId));  
++                      }
++                      else
++                      {
++                              // chunkId == 0, so it is an ObjectHeader.
++                              // Thus, we read in the object header and make the object
++                              yaffs_SetChunkBit(dev,blk,c);
++                              bi->pagesInUse++;
++                                                      
++                              yaffs_ReadChunkWithTagsFromNAND(dev,chunk,chunkData,NULL);
++                              
++                              oh = (yaffs_ObjectHeader *)chunkData;
++                              
++                              in = yaffs_FindObjectByNumber(dev,tags.objectId);
++                              if(in && in->variantType != oh->type)
++                              {
++                                      // This should not happen, but somehow
++                                      // Wev'e ended up with an objectId that has been reused but not yet 
++                                      // deleted, and worse still it has changed type. Delete the old object.
++                                      
++                                      yaffs_DestroyObject(in);
++                                      
++                                      in = 0;
++                              }
++                              
++                              in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,oh->type);
++                              
++                              if(oh->shadowsObject > 0)
++                              {
++                                      yaffs_HandleShadowedObject(dev,oh->shadowsObject,0);
++                              }
++                              
++                              if(in->valid)
++                              {
++                                      // We have already filled this one. We have a duplicate and need to resolve it.
++                                      
++                                      unsigned existingSerial = in->serial;
++                                      unsigned newSerial = tags.serialNumber;
++                                      
++                                      if( dev->isYaffs2 ||
++                                          ((existingSerial+1) & 3) == newSerial)
++                                      {
++                                              // Use new one - destroy the exisiting one
++                                              yaffs_DeleteChunk(dev,in->chunkId,1,__LINE__);
++                                              in->valid = 0;
++                                      }
++                                      else
++                                      {
++                                              // Use existing - destroy this one.
++                                              yaffs_DeleteChunk(dev,chunk,1,__LINE__);
++                                      }
++                              }
++                              
++                              if(!in->valid &&
++                                 (tags.objectId == YAFFS_OBJECTID_ROOT ||
++                                  tags.objectId == YAFFS_OBJECTID_LOSTNFOUND))
++                              {
++                                      // We only load some info, don't fiddle with directory structure
++                                      in->valid = 1;
++                                      in->variantType = oh->type;
++      
++                                      in->yst_mode  = oh->yst_mode;
++#ifdef CONFIG_YAFFS_WINCE
++                                      in->win_atime[0] = oh->win_atime[0];
++                                      in->win_ctime[0] = oh->win_ctime[0];
++                                      in->win_mtime[0] = oh->win_mtime[0];
++                                      in->win_atime[1] = oh->win_atime[1];
++                                      in->win_ctime[1] = oh->win_ctime[1];
++                                      in->win_mtime[1] = oh->win_mtime[1];
++#else
++                                      in->yst_uid   = oh->yst_uid;
++                                      in->yst_gid   = oh->yst_gid;
++                                      in->yst_atime = oh->yst_atime;
++                                      in->yst_mtime = oh->yst_mtime;
++                                      in->yst_ctime = oh->yst_ctime;
++                                      in->yst_rdev = oh->yst_rdev;
++#endif
++                                      in->chunkId  = chunk;
++
++                              }
++                              else if(!in->valid)
++                              {
++                                      // we need to load this info
++                              
++                                      in->valid = 1;
++                                      in->variantType = oh->type;
++      
++                                      in->yst_mode  = oh->yst_mode;
++#ifdef CONFIG_YAFFS_WINCE
++                                      in->win_atime[0] = oh->win_atime[0];
++                                      in->win_ctime[0] = oh->win_ctime[0];
++                                      in->win_mtime[0] = oh->win_mtime[0];
++                                      in->win_atime[1] = oh->win_atime[1];
++                                      in->win_ctime[1] = oh->win_ctime[1];
++                                      in->win_mtime[1] = oh->win_mtime[1];
++#else
++                                      in->yst_uid   = oh->yst_uid;
++                                      in->yst_gid   = oh->yst_gid;
++                                      in->yst_atime = oh->yst_atime;
++                                      in->yst_mtime = oh->yst_mtime;
++                                      in->yst_ctime = oh->yst_ctime;
++                                      in->yst_rdev = oh->yst_rdev;
++#endif
++                                      in->chunkId  = chunk;
++
++                                      yaffs_SetObjectName(in,oh->name);
++                                      in->dirty = 0;
++                                                      
++                                      // directory stuff...
++                                      // hook up to parent
++      
++                                      parent = yaffs_FindOrCreateObjectByNumber(dev,oh->parentObjectId,YAFFS_OBJECT_TYPE_DIRECTORY);
++                                      if(parent->variantType == YAFFS_OBJECT_TYPE_UNKNOWN)
++                                      {
++                                              // Set up as a directory
++                                              parent->variantType = YAFFS_OBJECT_TYPE_DIRECTORY;
++                                              INIT_LIST_HEAD(&parent->variant.directoryVariant.children);
++                                      }
++                                      else if(parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
++                                      {
++                                              // Hoosterman, another problem....
++                                              // We're trying to use a non-directory as a directory
++
++                                              T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." TENDSTR)));
++                                              parent = dev->lostNFoundDir;
++                                      }
++                              
++                                      yaffs_AddObjectToDirectory(parent,in);
++
++                                      if(0 && (parent == dev->deletedDir ||
++                                         parent == dev->unlinkedDir))
++                                      {
++                                              in->deleted = 1; // If it is unlinked at start up then it wants deleting
++                                              dev->nDeletedFiles++;
++                                      }
++                              
++                                      // Note re hardlinks.
++                                      // Since we might scan a hardlink before its equivalent object is scanned
++                                      // we put them all in a list.
++                                      // After scanning is complete, we should have all the objects, so we run through this
++                                      // list and fix up all the chains.              
++      
++                                      switch(in->variantType)
++                                      {
++                                              case YAFFS_OBJECT_TYPE_UNKNOWN:         // Todo got a problem
++                                                      break;
++                                              case YAFFS_OBJECT_TYPE_FILE:
++                                                      if(dev->isYaffs2 && oh->isShrink)
++                                                      {
++                                                              // Prune back the shrunken chunks
++                                                              yaffs_PruneResizedChunks(in,oh->fileSize);
++                                                              // Mark the block as having a shrinkHeader
++                                                              bi->hasShrinkHeader = 1;
++                                                      }
++                                                      
++                                                      if(dev->useHeaderFileSize)
++                                              
++                                                              in->variant.fileVariant.fileSize = oh->fileSize;
++                                                              
++                                                      break;
++                                              case YAFFS_OBJECT_TYPE_HARDLINK:
++                                                      in->variant.hardLinkVariant.equivalentObjectId = oh->equivalentObjectId;
++                                                      in->hardLinks.next = (struct list_head *)hardList;
++                                                      hardList = in;
++                                                      break;
++                                              case YAFFS_OBJECT_TYPE_DIRECTORY:       // Do nothing
++                                                      break;
++                                              case YAFFS_OBJECT_TYPE_SPECIAL: // Do nothing
++                                                      break;
++                                              case YAFFS_OBJECT_TYPE_SYMLINK:         // Do nothing
++                                                      in->variant.symLinkVariant.alias = yaffs_CloneString(oh->alias);
++                                                      break;
++                                      }
++
++                                      if(parent == dev->deletedDir)
++                                      {
++                                              yaffs_DestroyObject(in);
++                                              bi->hasShrinkHeader = 1;
++                                      }
++                                      //T((" %d %d header %d \"%s\" type %d\n",blk,c,tags.objectId,oh->name,in->variantType));        
++                              }
++                      }
++              }
++              
++              if(state == YAFFS_BLOCK_STATE_NEEDS_SCANNING)
++              {
++                      // If we got this far while scanning, then the block is fully allocated.
++                      state = YAFFS_BLOCK_STATE_FULL; 
++              }
++              
++              bi->blockState = state;
++              
++              // Now let's see if it was dirty
++              if(     bi->pagesInUse == 0 &&
++                      !bi->hasShrinkHeader &&
++              bi->blockState == YAFFS_BLOCK_STATE_FULL)
++          {
++              yaffs_BlockBecameDirty(dev,blk);
++          }
++
++      }
++      
++      if(blockIndex)
++      {
++              YFREE(blockIndex);
++      }
++      
++      // Ok, we've done all the scanning.
++      
++      // Fix up the hard link chains.
++      // We should now have scanned all the objects, now it's time to add these 
++      // hardlinks.
++      while(hardList)
++      {
++              hl = hardList;
++              hardList = (yaffs_Object *)(hardList->hardLinks.next);
++              
++              in = yaffs_FindObjectByNumber(dev,hl->variant.hardLinkVariant.equivalentObjectId);
++              
++              if(in)
++              {
++                      // Add the hardlink pointers
++                      hl->variant.hardLinkVariant.equivalentObject=in;
++                      list_add(&hl->hardLinks,&in->hardLinks);
++              }
++              else
++              {
++                      //Todo Need to report/handle this better.
++                      // Got a problem... hardlink to a non-existant object
++                      hl->variant.hardLinkVariant.equivalentObject=NULL;
++                      INIT_LIST_HEAD(&hl->hardLinks);
++                      
++              }
++              
++      }
++      
++      // Handle the unlinked files. Since they were left in an unlinked state we should
++      // just delete them.
++      {
++              struct list_head *i;    
++              struct list_head *n;
++                      
++              yaffs_Object *l;
++              // Soft delete all the unlinked files
++              list_for_each_safe(i,n,&dev->unlinkedDir->variant.directoryVariant.children)
++              {
++                      if(i)
++                      {
++                              l = list_entry(i, yaffs_Object,siblings);
++                              yaffs_DestroyObject(l);         
++                      }
++              }       
++      }
++      
++      yaffs_ReleaseTempBuffer(dev,chunkData,__LINE__);
++
++      T(YAFFS_TRACE_SCAN,(TSTR("yaffs_Scan ends" TENDSTR)));
++
++      return YAFFS_OK;
++}
++
++
++static int yaffs_ScanBackwards(yaffs_Device *dev)
++{
++      yaffs_ExtendedTags tags;
++      int blk;
++      int blockIterator;
++      int startIterator;
++      int endIterator;
++      int nBlocksToScan = 0;
++      
++      int chunk;
++      int c;
++      int deleted;
++      yaffs_BlockState state;
++      yaffs_Object *hardList = NULL;
++      yaffs_Object *hl;
++      yaffs_BlockInfo *bi;
++      int sequenceNumber;     
++      yaffs_ObjectHeader *oh;
++      yaffs_Object *in;
++      yaffs_Object *parent;
++      int nBlocks = dev->internalEndBlock - dev->internalStartBlock + 1;
++      
++      __u8 *chunkData;
++
++      yaffs_BlockIndex *blockIndex = NULL;
++
++
++      if(!dev->isYaffs2)
++      {
++              T(YAFFS_TRACE_SCAN,(TSTR("yaffs_ScanBackwards is only for YAFFS2!" TENDSTR)));
++              return YAFFS_FAIL;
++      }
++      
++      T(YAFFS_TRACE_SCAN,(TSTR("yaffs_ScanBackwards starts  intstartblk %d intendblk %d..." TENDSTR),dev->internalStartBlock,dev->internalEndBlock));
++              
++      chunkData = yaffs_GetTempBuffer(dev,__LINE__);
++      
++      
++      dev->sequenceNumber = YAFFS_LOWEST_SEQUENCE_NUMBER;
++      
++      blockIndex = YMALLOC(nBlocks * sizeof(yaffs_BlockIndex));               
++      
++      
++      // Scan all the blocks to determine their state
++      for(blk = dev->internalStartBlock; blk <= dev->internalEndBlock; blk++)
++      {
++              bi = yaffs_GetBlockInfo(dev,blk);
++              yaffs_ClearChunkBits(dev,blk);
++              bi->pagesInUse = 0;
++              bi->softDeletions = 0;
++                              
++              yaffs_QueryInitialBlockState(dev,blk,&state,&sequenceNumber);
++              
++              bi->blockState = state;
++              bi->sequenceNumber = sequenceNumber;
++
++              T(YAFFS_TRACE_SCAN_DEBUG,(TSTR("Block scanning block %d state %d seq %d" TENDSTR),blk,state,sequenceNumber));
++              
++              if(state == YAFFS_BLOCK_STATE_DEAD)
++              {
++                      T(YAFFS_TRACE_BAD_BLOCKS,(TSTR("block %d is bad" TENDSTR),blk));
++              }
++              else if(state == YAFFS_BLOCK_STATE_EMPTY)
++              {
++                      T(YAFFS_TRACE_SCAN_DEBUG,(TSTR("Block empty " TENDSTR)));
++                      dev->nErasedBlocks++;
++                      dev->nFreeChunks += dev->nChunksPerBlock;
++              }
++              else if(state == YAFFS_BLOCK_STATE_NEEDS_SCANNING)
++              {
++                                      
++                      // Determine the highest sequence number
++                      if( dev->isYaffs2 &&
++                          sequenceNumber >= YAFFS_LOWEST_SEQUENCE_NUMBER &&
++                          sequenceNumber < YAFFS_HIGHEST_SEQUENCE_NUMBER)
++                       {
++                              
++                              blockIndex[nBlocksToScan].seq = sequenceNumber;
++                              blockIndex[nBlocksToScan].block = blk;
++                              
++                              nBlocksToScan++;
++
++                              if(sequenceNumber >= dev->sequenceNumber)
++                              {
++                                      dev->sequenceNumber = sequenceNumber;
++                              }
++                      }
++                      else if(dev->isYaffs2)
++                      {
++                              // TODO: Nasty sequence number!
++                              T(YAFFS_TRACE_SCAN,(TSTR("Block scanning block %d has bad sequence number %d" TENDSTR),blk,sequenceNumber));
++
++                      }
++              }
++      }
++      
++      // Sort the blocks
++      // Dungy old bubble sort for now...
++      {
++              yaffs_BlockIndex temp;
++              int i;
++              int j;
++              
++              for(i = 0; i < nBlocksToScan; i++)
++                      for(j = i+1; j < nBlocksToScan; j++)
++                       if(blockIndex[i].seq > blockIndex[j].seq)
++                       {
++                              temp = blockIndex[j];
++                              blockIndex[j] = blockIndex[i];
++                              blockIndex[i] = temp;
++                       }
++      }
++      
++      
++      // Now scan the blocks looking at the data.
++      startIterator = 0;
++      endIterator = nBlocksToScan-1;
++      T(YAFFS_TRACE_SCAN_DEBUG,(TSTR("%d blocks to be scanned" TENDSTR),nBlocksToScan));
++
++      
++      // For each block.... backwards
++      for(blockIterator = endIterator; blockIterator >= startIterator; blockIterator--)
++      {
++      
++              // get the block to scan in the correct order
++              blk = blockIndex[blockIterator].block;
++
++
++              bi = yaffs_GetBlockInfo(dev,blk);
++              state = bi->blockState;
++              
++              deleted = 0;
++              
++              if( 0 && // Disable since this is redundant.
++                  state == YAFFS_BLOCK_STATE_NEEDS_SCANNING)
++              {
++                      // Let's look at the first chunk in the block
++                      chunk = blk * dev->nChunksPerBlock;
++                      
++                      yaffs_ReadChunkWithTagsFromNAND(dev,chunk,NULL,&tags);
++
++                      // Let's have a good look at this chunk...
++      
++                      if(!tags.chunkUsed)
++                      {
++                              // An unassigned chunk in the block
++                              // This means that either the block is empty or 
++                              // this is the one being allocated from
++                              
++                              // We're looking at the first chunk in the block so the block is unused
++                              state = YAFFS_BLOCK_STATE_EMPTY;
++                              dev->nErasedBlocks++;
++                              dev->nFreeChunks += dev->nChunksPerBlock;
++                      }
++              
++              }
++              
++              // For each chunk in each block that needs scanning....
++              for(c = dev->nChunksPerBlock-1; c >= 0 && 
++                                (state == YAFFS_BLOCK_STATE_NEEDS_SCANNING ||
++                                 state == YAFFS_BLOCK_STATE_ALLOCATING); c--)
++              {
++                      // Scan backwards... 
++                      // Read the tags and decide what to do
++                      chunk = blk * dev->nChunksPerBlock + c;
++                      
++                      yaffs_ReadChunkWithTagsFromNAND(dev,chunk,NULL,&tags);
++
++                      // Let's have a good look at this chunk...
++      
++                      if(!tags.chunkUsed)
++                      {
++                              // An unassigned chunk in the block
++                              // This means that either the block is empty or 
++                              // this is the one being allocated from
++                              
++                              if(c == 0)
++                              {
++                                      // We're looking at the first chunk in the block so the block is unused
++                                      state = YAFFS_BLOCK_STATE_EMPTY;
++                                      dev->nErasedBlocks++;
++                              }
++                              else
++                              {
++                                      // this is the block being allocated from
++                                      if(state == YAFFS_BLOCK_STATE_NEEDS_SCANNING)
++                                      {
++                                        T(YAFFS_TRACE_SCAN,(TSTR(" Allocating from %d %d" TENDSTR),blk,c));
++                                      }
++                                      state = YAFFS_BLOCK_STATE_ALLOCATING;
++                                      dev->allocationBlock = blk;
++                                      dev->allocationPage = c;
++                                      dev->allocationBlockFinder = blk; // Set it to here to encourage the allocator to
++                                                                                                        // go forth from here.
++                                      //Yaffs2 sanity check:
++                                      // This should be the one with the highest sequence number
++                                      if(dev->isYaffs2 && (dev->sequenceNumber != bi->sequenceNumber))
++                                      {
++                                              T(YAFFS_TRACE_ALWAYS,
++                                                              (TSTR("yaffs: Allocation block %d was not highest sequence id: block seq = %d, dev seq = %d" TENDSTR),
++                                                              blk,bi->sequenceNumber,dev->sequenceNumber));
++                                      }
++                              }
++
++                              dev->nFreeChunks ++;
++                      }
++                      else if(tags.chunkId > 0)
++                      {
++                              // chunkId > 0 so it is a data chunk...
++                              unsigned int endpos;
++                              
++                              __u32 chunkBase = (tags.chunkId - 1)* dev->nBytesPerChunk;
++
++                              yaffs_SetChunkBit(dev,blk,c);
++                              bi->pagesInUse++;
++                                                              
++                              in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,YAFFS_OBJECT_TYPE_FILE);
++                              if(in->variantType == YAFFS_OBJECT_TYPE_FILE &&
++                                 chunkBase < in->variant.fileVariant.shrinkSize)
++                              {
++                                      // This has not been invalidated by a resize
++                                      yaffs_PutChunkIntoFile(in,tags.chunkId,chunk,-1);
++                              
++                              
++                                      // File size is calculated by looking at the data chunks if we have not 
++                                      // seen an object header yet. Stop this practice once we find an object header.
++                                      endpos = (tags.chunkId - 1)* dev->nBytesPerChunk + tags.byteCount;
++                                      if(!in->valid && // have not got an object header yet
++                                         in->variant.fileVariant.scannedFileSize <endpos)
++                                      {
++                                              in->variant.fileVariant.scannedFileSize = endpos;
++                                              in->variant.fileVariant.fileSize = in->variant.fileVariant.scannedFileSize;
++                                      }
++
++                              }
++                              else
++                              {
++                                      // This chunk has been invalidated by a resize, so delete
++                                      yaffs_DeleteChunk(dev,chunk,1,__LINE__);
++                                      
++                                      
++                              }
++                              //T((" %d %d data %d %d\n",blk,c,tags.objectId,tags.chunkId));  
++                      }
++                      else
++                      {
++                              // chunkId == 0, so it is an ObjectHeader.
++                              // Thus, we read in the object header and make the object
++                              yaffs_SetChunkBit(dev,blk,c);
++                              bi->pagesInUse++;
++