--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML>
+<HEAD>
+ <META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=iso-8859-1">
+ <TITLE></TITLE>
+ <META NAME="GENERATOR" CONTENT="StarOffice/5.2 (Linux)">
+ <META NAME="CREATED" CONTENT="20020517;23103700">
+ <META NAME="CHANGEDBY" CONTENT=" ">
+ <META NAME="CHANGED" CONTENT="20020518;7200400">
+</HEAD>
+<BODY>
+<H2 ALIGN=CENTER>Running up YAFFS using the MTD interface</H2>
+<P>Here are the steps required to get YAFFS going with the
+NANDemulation MTD that I have written.</P>
+<P><FONT COLOR="#800000"><FONT SIZE=4 STYLE="font-size: 16pt"><I><U><B>Warning:
+This is experimental stuff that plugs into the kernel. It has only
+been lightly tested. Don't play with this on a box you are not
+prepared to reboot.</B></U></I></FONT></FONT></P>
+<P>There are a few things you need to do. This document assumes that
+you're working from the 2.4.18 kernel code base.</P>
+<H3>Preparing the kernel</H3>
+<P>First off, you need to patch the mtdcore services.</P>
+<OL>
+ <LI><P>Replace the devices/mtd/mtdpart.c file with the one enclosed.
+ This patches the problem where special NAND functions are not being
+ copied through the partition handler. The changes are marked with a
+ comment containing my initials <B><FONT FACE="Courier, monospace">cdhm</FONT></B>.</P>
+ <LI><P>Build the kernel including the following configurations to
+ support mtd.</P>
+</OL>
+<P STYLE="margin-left: 2cm">CONFIG_MTD=y Turn on MTD support</P>
+<P STYLE="margin-left: 2cm">CONFIG_MTD_PARTITIONS=y Turn on partition
+support</P>
+<P STYLE="margin-left: 2cm">CONFIG_MTD_CHAR=y Need char drivers to
+access the data from user space.</P>
+<P STYLE="margin-left: 2cm">CONFIG_MTD_BLOCK=y Block driver interface
+used only to find the device for mounting</P>
+<P STYLE="margin-left: 2cm">may as well also set:</P>
+<P STYLE="margin-left: 2cm">CONFIG_MTD_DEBUG=y</P>
+<OL>
+ <P>CONFIG_MTD_DEBUG_VERBOSE=3</P>
+ <H3 ALIGN=LEFT></H3>
+</OL>
+<H3 ALIGN=LEFT>Setting up yaffs</H3>
+<OL>
+ <LI><P>Run the mtd utility MAKEDEV to make the /dev/mtdxxx entries.</P>
+ <LI><P>Load up the NANDemul MTD by typing<BR><FONT FACE="Courier, monospace">#/sbin/insmod
+ mtdemul/nandemul.o</FONT><BR>You should now be able to see the
+ device in the mtd list by typing <BR>#<FONT FACE="Courier, monospace">cat
+ /proc/mtd</FONT><BR>If all is well, the device will now be
+ accessible as <FONT FACE="Courier, monospace">/dev/mtd0</FONT> and
+ <FONT FACE="Courier, monospace">/dev/mtdblock0</FONT> (or whatever).</P>
+ <LI><P>Now load up the yaffs filesystem module by
+ typing<BR><FONT FACE="Courier, monospace">#/sbin/insmod
+ mtdemul/yaffs.o<BR><FONT FACE="Times, serif">You should now be able
+ to see the yaffs file systems by typing</FONT><BR>#cat
+ /proc/filesystems</FONT></P>
+ <LI><P><FONT FACE="Times, serif">Now create a mount point:<BR><FONT FACE="Courier, monospace">#mkdir
+ /mnt/y</FONT></FONT></P>
+ <LI><P><FONT FACE="Times, serif">Mount the file system:<BR><FONT FACE="Courier, monospace">#mount
+ -t yaffs /dev/mtdblock0 /mnt/y</FONT></FONT></P>
+ <LI><P><FONT FACE="Times, serif">Well if that all worked you have
+ YAFFS running on the /mnt/y mount point using the NANDemul mtd.</FONT></P>
+</OL>
+<H3>What about real NAND?</H3>
+<P><FONT FACE="Times, serif">You might want to try getting going on a
+real NAND device.</FONT></P>
+<P><FONT FACE="Times, serif">I have not yet done this, but the
+NANDemul tests out the mtd interface so in theory it should work on
+real NAND too.</FONT></P>
+<P><FONT FACE="Times, serif">Note though that since YAFFS applies the
+ECC, it does not expect the NAND device to be applying ECC. You
+probably want to configure the NAND driver with ECC disabled.</FONT></P>
+<P><BR><BR>
+</P>
+</BODY>
+</HTML>
\ No newline at end of file
--- /dev/null
+#Makefile for NANDemul MTD
+#
+# NB this is not yet suitable for putting into the kernel tree.
+# 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.
+
+## Change or override KERNELDIR to your kernel
+## comment out USE_xxxx if you don't want these features.
+
+KERNELDIR = /usr/src/kernel-headers-2.4.18
+
+CFLAGS = -D__KERNEL__ -DMODULE -I$(KERNELDIR)/include -O2 -Wall
+
+
+OBJS = nandemul.o
+
+
+
+$(OBJS): %.o: %.c
+ gcc -c $(CFLAGS) $< -o $@
+
--- /dev/null
+/*
+ * 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.
+ *
+ */
+
+#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/pagemap.h>
+#include <linux/mtd/mtd.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+
+#include <asm/uaccess.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+
+#define T(x) printk x
+#define ALLOCATE(x) kmalloc(x,GFP_KERNEL)
+#define FREE(x) kfree(x)
+
+#define DEFAULT_SIZE_IN_MB 16
+
+#define NAND_SHIFT 9
+
+
+static struct mtd_info nandemul_mtd;
+
+typedef struct
+{
+ __u8 data[528]; // Data + spare
+ int count[3]; // The programming count for each area of
+ // the page (0..255,256..511,512..527
+ int empty; // is this empty?
+} nandemul_Page;
+
+typedef struct
+{
+ nandemul_Page page[32]; // The pages in the block
+ __u8 damaged; // Is the block damaged?
+
+} nandemul_Block;
+
+
+
+typedef struct
+{
+ nandemul_Block **block;
+ int nBlocks;
+} nandemul_Device;
+
+nandemul_Device device;
+
+int sizeInMB = DEFAULT_SIZE_IN_MB;
+
+int nandemul_CalcNBlocks(void)
+{
+ switch(sizeInMB)
+ {
+ case 8:
+ case 16:
+ case 32:
+ case 64:
+ case 128:
+ case 256:
+ case 512:
+ break;
+ default:
+ sizeInMB = DEFAULT_SIZE_IN_MB;
+ }
+ return sizeInMB * 64;
+}
+
+
+static void nandemul_ReallyEraseBlock(int blockNumber)
+{
+ int i;
+
+ nandemul_Block *theBlock = device.block[blockNumber];
+
+ for(i = 0; i < 32; i++)
+ {
+ memset(theBlock->page[i].data,0xff,528);
+ theBlock->page[i].count[0] = 0;
+ theBlock->page[i].count[1] = 0;
+ theBlock->page[i].count[2] = 0;
+ theBlock->page[i].empty = 1;
+ }
+
+}
+
+static int nandemul_DoErase(int blockNumber)
+{
+ if(blockNumber < 0 || nandemul_CalcNBlocks())
+ {
+ T(("Attempt to erase non-existant block %d\n",blockNumber));
+ }
+ else if(device.block[blockNumber]->damaged)
+ {
+ T(("Attempt to erase damaged block %d\n",blockNumber));
+ }
+ else
+ {
+ nandemul_ReallyEraseBlock(blockNumber);
+ }
+
+ return 1;
+
+}
+
+
+int nandemul_Initialise(void)
+{
+ int i;
+ int fail = 0;
+ int nBlocks = nandemul_CalcNBlocks();
+ int nAllocated = 0;
+
+ device.block = ALLOCATE (sizeof(nandemul_Block *) * nBlocks);
+
+ if(!device.block) return 0;
+
+ for(i=0; i <nBlocks; i++)
+ {
+ device.block[i] = NULL;
+ }
+
+ for(i=0; i <nBlocks && !fail; i++)
+ {
+ if((device.block[i] = ALLOCATE(sizeof(nandemul_Block))) == 0)
+ {
+ fail = 1;
+ }
+ else
+ {
+ nandemul_ReallyEraseBlock(i);
+ device.block[i]->damaged = 0;
+ nAllocated++;
+ }
+ }
+
+ if(fail)
+ {
+ for(i = 0; i < nAllocated; i++)
+ {
+ FREE(device.block[i]);
+ }
+ FREE(device.block);
+
+ T(("Allocation failed, could only allocate %dMB of %dMB requested.\n",
+ nAllocated/64,sizeInMB));
+ return 0;
+ }
+
+ device.nBlocks = nBlocks;
+ return 1;
+}
+
+int nandemul_DeInitialise(void)
+{
+ int i;
+ for(i = 0; i < device.nBlocks; i++)
+ {
+ FREE(device.block[i]);
+ device.block[i] = NULL;
+ }
+
+ FREE(device.block);
+ device.block = NULL;
+ return 1;
+}
+
+int nandemul_GetNumberOfBlocks(__u32 *nBlocks)
+{
+ *nBlocks = device.nBlocks;
+
+ return 1;
+}
+
+int nandemul_Reset(void)
+{
+ // Do nothing
+ return 1;
+}
+
+int nandemul_Read(__u8 *buffer, __u32 pageAddress,__u32 pageOffset, __u32 nBytes)
+{
+ unsigned blockN, pageN;
+
+ blockN = pageAddress/32;
+ pageN = pageAddress % 32;
+
+ // TODO: Do tests for valid blockN, pageN, pageOffset
+
+ memcpy(buffer,&device.block[blockN]->page[pageN].data[pageOffset],nBytes);
+
+ return 1;
+
+}
+
+int nandemul_Program(const __u8 *buffer, __u32 pageAddress,__u32 pageOffset, __u32 nBytes)
+{
+ unsigned blockN, pageN, pageO;
+
+ int p0, p1, p2;
+ int i;
+
+ blockN = pageAddress/32;
+ pageN = pageAddress % 32;
+ p0 = 0;
+ p1 = 0;
+ p2 = 0;
+
+ // TODO: Do tests for valid blockN, pageN, pageOffset
+
+
+ for(i = 0,pageO = pageOffset; i < nBytes; i++, pageO++)
+ {
+ device.block[blockN]->page[pageN].data[pageO] &= buffer[i];
+
+ if(pageO < 256) p0 = 1;
+ else if(pageO <512) p1 = 1;
+ else p2 = 1;
+ }
+
+ device.block[blockN]->page[pageN].empty = 0;
+ device.block[blockN]->page[pageN].count[0] += p0;
+ device.block[blockN]->page[pageN].count[1] += p1;
+ device.block[blockN]->page[pageN].count[2] += p2;
+
+ if(device.block[blockN]->page[pageN].count[0] > 1)
+ {
+ T(("block %d page %d first half programmed %d times\n",
+ blockN,pageN,device.block[blockN]->page[pageN].count[0]));
+ }
+ if(device.block[blockN]->page[pageN].count[1] > 1)
+ {
+ T(("block %d page %d second half programmed %d times\n",
+ blockN,pageN,device.block[blockN]->page[pageN].count[1]));
+ }
+ if(device.block[blockN]->page[pageN].count[2] > 3)
+ {
+ T(("block %d page %d spare programmed %d times\n",
+ blockN,pageN,device.block[blockN]->page[pageN].count[2]));
+ }
+
+ return 1;
+
+}
+
+int nandemul_CauseBitErrors( __u32 pageAddress, __u32 pageOffset, __u8 xorPattern)
+{
+ unsigned blockN, pageN;
+
+
+ blockN = pageAddress/32;
+ pageN = pageAddress % 32;
+
+
+ // TODO: Do tests for valid blockN, pageN, pageOffset
+
+ device.block[blockN]->page[pageN].data[pageOffset] ^= xorPattern;
+
+
+ return 1;
+
+}
+
+
+int nandemul_BlockErase(__u32 pageAddress)
+{
+ unsigned blockN;
+
+ blockN = pageAddress/32;
+
+ // TODO: Do tests for valid blockN
+ // TODO: Test that the block has not failed
+
+ return nandemul_DoErase(blockN);
+
+}
+
+int nandemul_FailBlock(__u32 pageAddress)
+{
+ unsigned blockN;
+
+ blockN = pageAddress/32;
+
+ // TODO: Do tests for valid blockN
+ // TODO: Test that the block has not failed
+
+ nandemul_DoErase(blockN);
+ return 1;
+}
+
+int nandemul_ReadId(__u8 *vendorId, __u8 *deviceId)
+{
+ *vendorId = 0xEC;
+ *deviceId = 0x75;
+
+ return 1;
+}
+
+int nandemul_CopyPage(__u32 fromPageAddress, __u32 toPageAddress)
+{
+ __u8 copyBuffer[528];
+
+ // TODO: Check the bitplane issue.
+ nandemul_Read(copyBuffer, fromPageAddress,0,528);
+ nandemul_Program(copyBuffer, toPageAddress,0,528);
+
+ return 1;
+}
+
+int nandemul_ReadStatus(__u8 *status)
+{
+ *status = 0;
+ return 1;
+}
+
+
+#ifdef CONFIG_MTD_NAND_ECC
+#include <linux/mtd/nand_ecc.h>
+#endif
+
+/*
+ * NAND low-level MTD interface functions
+ */
+static int nand_read (struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf);
+static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf, u_char *ecc_code);
+static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf);
+static int nand_write (struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf);
+static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf,
+ u_char *ecc_code);
+static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf);
+static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs,
+ unsigned long count, loff_t to, size_t *retlen);
+static int nand_erase (struct mtd_info *mtd, struct erase_info *instr);
+static void nand_sync (struct mtd_info *mtd);
+
+
+
+/*
+ * NAND read
+ */
+static int nand_read (struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ return nand_read_ecc (mtd, from, len, retlen, buf, NULL);
+}
+
+/*
+ * NAND read with ECC
+ */
+static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf, u_char *ecc_code)
+{
+ int start, page;
+ int n = len;
+ int nToCopy;
+
+
+
+ /* Do not allow reads past end of device */
+ if ((from + len) > mtd->size) {
+ *retlen = 0;
+ return -EINVAL;
+ }
+
+
+ /* Initialize return value */
+ *retlen = 0;
+
+ while(n > 0)
+ {
+
+ /* First we calculate the starting page */
+ page = from >> NAND_SHIFT;
+
+ /* Get raw starting column */
+
+ start = from & (mtd->oobblock-1);
+
+ // OK now check for the curveball where the start and end are in
+ // the same page
+ if((start + n) < mtd->oobblock)
+ {
+ nToCopy = n;
+ }
+ else
+ {
+ nToCopy = mtd->oobblock - start;
+ }
+
+ nandemul_Read(buf, page, start, nToCopy);
+
+ n -= nToCopy;
+ from += nToCopy;
+ buf += nToCopy;
+ *retlen += nToCopy;
+
+ }
+
+
+ return 0;
+}
+
+/*
+ * NAND read out-of-band
+ */
+static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ int col, page;
+
+ DEBUG (MTD_DEBUG_LEVEL3,
+ "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from,
+ (int) len);
+
+ /* Shift to get page */
+ page = ((int) from) >> NAND_SHIFT;
+
+ /* Mask to get column */
+ col = from & 0x0f;
+
+ /* Initialize return length value */
+ *retlen = 0;
+
+ /* Do not allow reads past end of device */
+ if ((from + len) > mtd->size) {
+ DEBUG (MTD_DEBUG_LEVEL0,
+ "nand_read_oob: Attempt read beyond end of device\n");
+ *retlen = 0;
+ return -EINVAL;
+ }
+
+ nandemul_Read(buf,page,512 + col,len);
+
+ /* Return happy */
+ *retlen = len;
+ return 0;
+}
+
+/*
+ * NAND write
+ */
+static int nand_write (struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ return nand_write_ecc (mtd, to, len, retlen, buf, NULL);
+}
+
+/*
+ * NAND write with ECC
+ */
+static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf,
+ u_char *ecc_code)
+{
+
+ int start, page;
+ int n = len;
+ int nToCopy;
+
+
+
+ /* Do not allow reads past end of device */
+ if ((to + len) > mtd->size) {
+ *retlen = 0;
+ return -EINVAL;
+ }
+
+
+ /* Initialize return value */
+ *retlen = 0;
+
+ while(n > 0)
+ {
+
+ /* First we calculate the starting page */
+ page = to >> NAND_SHIFT;
+
+ /* Get raw starting column */
+
+ start = to & (mtd->oobblock - 1);
+
+ // OK now check for the curveball where the start and end are in
+ // the same page
+ if((start + n) < mtd->oobblock)
+ {
+ nToCopy = n;
+ }
+ else
+ {
+ nToCopy = mtd->oobblock - start;
+ }
+
+ nandemul_Program(buf, page, start, nToCopy);
+
+ n -= nToCopy;
+ to += nToCopy;
+ buf += nToCopy;
+ *retlen += nToCopy;
+
+ }
+
+
+ return 0;
+}
+
+/*
+ * NAND write out-of-band
+ */
+static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ int col, page;
+
+
+ DEBUG (MTD_DEBUG_LEVEL3,
+ "nand_read_oob: to = 0x%08x, len = %i\n", (unsigned int) to,
+ (int) len);
+
+ /* Shift to get page */
+ page = ((int) to) >> NAND_SHIFT;
+
+ /* Mask to get column */
+ col = to & 0x0f;
+
+ /* Initialize return length value */
+ *retlen = 0;
+
+ /* Do not allow reads past end of device */
+ if ((to + len) > mtd->size) {
+ DEBUG (MTD_DEBUG_LEVEL0,
+ "nand_read_oob: Attempt read beyond end of device\n");
+ *retlen = 0;
+ return -EINVAL;
+ }
+
+ nandemul_Program(buf,page,512 + col,len);
+
+ /* Return happy */
+ *retlen = len;
+ return 0;
+
+}
+
+/*
+ * NAND write with iovec
+ */
+static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs,
+ unsigned long count, loff_t to, size_t *retlen)
+{
+ return -EINVAL;
+}
+
+/*
+ * NAND erase a block
+ */
+static int nand_erase (struct mtd_info *mtd, struct erase_info *instr)
+{
+ int i, nBlocks,block;
+
+ DEBUG (MTD_DEBUG_LEVEL3,
+ "nand_erase: start = 0x%08x, len = %i\n",
+ (unsigned int) instr->addr, (unsigned int) instr->len);
+
+ /* Start address must align on block boundary */
+ if (instr->addr & (mtd->erasesize - 1)) {
+ DEBUG (MTD_DEBUG_LEVEL0,
+ "nand_erase: Unaligned address\n");
+ return -EINVAL;
+ }
+
+ /* Length must align on block boundary */
+ if (instr->len & (mtd->erasesize - 1)) {
+ DEBUG (MTD_DEBUG_LEVEL0,
+ "nand_erase: Length not block aligned\n");
+ return -EINVAL;
+ }
+
+ /* Do not allow erase past end of device */
+ if ((instr->len + instr->addr) > mtd->size) {
+ DEBUG (MTD_DEBUG_LEVEL0,
+ "nand_erase: Erase past end of device\n");
+ return -EINVAL;
+ }
+
+ nBlocks = instr->len >> (NAND_SHIFT + 5);
+ block = instr->addr >> (NAND_SHIFT + 5);
+
+ for(i = 0; i < nBlocks; i++)
+ {
+ nandemul_DoErase(block);
+ block++;
+ }
+
+
+
+ return 0;
+
+
+}
+
+/*
+ * NAND sync
+ */
+static void nand_sync (struct mtd_info *mtd)
+{
+ DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n");
+
+}
+
+/*
+ * Scan for the NAND device
+ */
+int nand_scan (struct mtd_info *mtd)
+{
+ mtd->oobblock = 512;
+ mtd->oobsize = 16;
+ mtd->erasesize = 512 * 32;
+ mtd->size = sizeInMB * 1024*1024;
+
+
+
+ /* Fill in remaining MTD driver data */
+ mtd->type = MTD_NANDFLASH;
+ mtd->flags = MTD_CAP_NANDFLASH;
+ mtd->module = THIS_MODULE;
+ mtd->ecctype = MTD_ECC_NONE;
+ mtd->erase = nand_erase;
+ mtd->point = NULL;
+ mtd->unpoint = NULL;
+ mtd->read = nand_read;
+ mtd->write = nand_write;
+ mtd->read_ecc = nand_read_ecc;
+ mtd->write_ecc = nand_write_ecc;
+ mtd->read_oob = nand_read_oob;
+ mtd->write_oob = nand_write_oob;
+ mtd->readv = NULL;
+ mtd->writev = nand_writev;
+ mtd->sync = nand_sync;
+ mtd->lock = NULL;
+ mtd->unlock = NULL;
+ mtd->suspend = NULL;
+ mtd->resume = NULL;
+
+ /* Return happy */
+ return 0;
+}
+
+#if 0
+#ifdef MODULE
+MODULE_PARM(sizeInMB, "i");
+
+__setup("sizeInMB=",sizeInMB);
+#endif
+#endif
+
+/*
+ * Define partitions for flash devices
+ */
+
+static struct mtd_partition nandemul_partition[] =
+{
+ { name: "NANDemul partition 1",
+ offset: 0,
+ size: 0 },
+};
+
+static int nPartitions = sizeof(nandemul_partition)/sizeof(nandemul_partition[0]);
+
+/*
+ * Main initialization routine
+ */
+int __init nandemul_init (void)
+{
+
+ // Do the nand init
+
+ nand_scan(&nandemul_mtd);
+
+ nandemul_Initialise();
+
+ // Build the partition table
+
+ nandemul_partition[0].size = sizeInMB * 1024 * 1024;
+
+ // Register the partition
+ add_mtd_partitions(&nandemul_mtd,nandemul_partition,nPartitions);
+
+ return 0;
+
+}
+
+module_init(nandemul_init);
+
+/*
+ * Clean up routine
+ */
+#ifdef MODULE
+static void __exit nandemul_cleanup (void)
+{
+
+ nandemul_DeInitialise();
+
+ /* Unregister partitions */
+ del_mtd_partitions(&nandemul_mtd);
+
+ /* Unregister the device */
+ del_mtd_device (&nandemul_mtd);
+
+}
+module_exit(nandemul_cleanup);
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Charles Manning <manningc@aleph1.co.uk>");
+MODULE_DESCRIPTION("NAND emulated in RAM");
+
+
+
+
--- /dev/null
+#ifndef __NANDEMUL_H__
+#define __NANDEMUL_H__
+
+typedef unsigned char __u8;
+typedef unsigned short __u16;
+typedef unsigned __u32;
+
+int nandemul_Initialise(void);
+int nandemul_DeInitialise(void);
+
+int nandemul_GetNumberOfBlocks(__u32 *nBlocks);
+int nandemul_Reset(void);
+int nandemul_Read(__u8 *buffer, __u32 pageAddress, __u32 pageOffset, __u32 nBytes);
+int nandemul_Program(__u8 *buffer, __u32 pageAddress, __u32 pageOffset, __u32 nBytes);
+
+
+
+int nandemul_BlockErase(__u32 address);
+
+int nandemul_ReadId(__u8 *vendorId, __u8 *deviceId);
+
+int nandemul_CopyPage(__u32 fromAddress, __u32 toAddress);
+
+int nandemul_ReadStatus(__u8 *status);
+
+int nandemul_FailBlock(__u32 pageAddress);
+int nandemul_CauseBitErrors( __u32 pageAddress, __u32 pageOffset, __u8 xorPattern);
+
+#endif
+
+
+
--- /dev/null
+/*
+ * Simple MTD partitioning layer
+ *
+ * (C) 2000 Nicolas Pitre <nico@cam.org>
+ *
+ * This code is GPL
+ *
+ * $Id: mtdpart.c,v 1.1 2002-05-22 12:38:56 wookey Exp $
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+
+/* Our partition linked list */
+static LIST_HEAD(mtd_partitions);
+
+/* Our partition node structure */
+struct mtd_part {
+ struct mtd_info mtd;
+ struct mtd_info *master;
+ u_int32_t offset;
+ int index;
+ struct list_head list;
+};
+
+/*
+ * Given a pointer to the MTD object in the mtd_part structure, we can retrieve
+ * the pointer to that structure with this macro.
+ */
+#define PART(x) ((struct mtd_part *)(x))
+
+
+/*
+ * MTD methods which simply translate the effective address and pass through
+ * to the _real_ device.
+ */
+
+static int part_read (struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct mtd_part *part = PART(mtd);
+ if (from >= mtd->size)
+ len = 0;
+ else if (from + len > mtd->size)
+ len = mtd->size - from;
+ return part->master->read (part->master, from + part->offset,
+ len, retlen, buf);
+}
+
+static int part_write (struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct mtd_part *part = PART(mtd);
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+ if (to >= mtd->size)
+ len = 0;
+ else if (to + len > mtd->size)
+ len = mtd->size - to;
+ return part->master->write (part->master, to + part->offset,
+ len, retlen, buf);
+}
+
+static int part_writev (struct mtd_info *mtd, const struct iovec *vecs,
+ unsigned long count, loff_t to, size_t *retlen)
+{
+ struct mtd_part *part = PART(mtd);
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+ return part->master->writev (part->master, vecs, count,
+ to + part->offset, retlen);
+}
+
+static int part_readv (struct mtd_info *mtd, struct iovec *vecs,
+ unsigned long count, loff_t from, size_t *retlen)
+{
+ struct mtd_part *part = PART(mtd);
+ return part->master->readv (part->master, vecs, count,
+ from + part->offset, retlen);
+}
+
+static int part_erase (struct mtd_info *mtd, struct erase_info *instr)
+{
+ struct mtd_part *part = PART(mtd);
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+ if (instr->addr >= mtd->size)
+ return -EINVAL;
+ instr->addr += part->offset;
+ return part->master->erase(part->master, instr);
+}
+
+static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+ struct mtd_part *part = PART(mtd);
+ if ((len + ofs) > mtd->size)
+ return -EINVAL;
+ return part->master->lock(part->master, ofs + part->offset, len);
+}
+
+static int part_unlock (struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+ struct mtd_part *part = PART(mtd);
+ if ((len + ofs) > mtd->size)
+ return -EINVAL;
+ return part->master->unlock(part->master, ofs + part->offset, len);
+}
+
+static void part_sync(struct mtd_info *mtd)
+{
+ struct mtd_part *part = PART(mtd);
+ part->master->sync(part->master);
+}
+
+static int part_suspend(struct mtd_info *mtd)
+{
+ struct mtd_part *part = PART(mtd);
+ return part->master->suspend(part->master);
+}
+
+static void part_resume(struct mtd_info *mtd)
+{
+ struct mtd_part *part = PART(mtd);
+ part->master->resume(part->master);
+}
+
+/*
+ * This function unregisters and destroy all slave MTD objects which are
+ * attached to the given master MTD object.
+ */
+
+int del_mtd_partitions(struct mtd_info *master)
+{
+ struct list_head *node;
+ struct mtd_part *slave;
+
+ for (node = mtd_partitions.next;
+ node != &mtd_partitions;
+ node = node->next) {
+ slave = list_entry(node, struct mtd_part, list);
+ if (slave->master == master) {
+ struct list_head *prev = node->prev;
+ __list_del(prev, node->next);
+ del_mtd_device(&slave->mtd);
+ kfree(slave);
+ node = prev;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * This function, given a master MTD object and a partition table, creates
+ * and registers slave MTD objects which are bound to the master according to
+ * the partition definitions.
+ * (Q: should we register the master MTD object as well?)
+ */
+
+int add_mtd_partitions(struct mtd_info *master,
+ struct mtd_partition *parts,
+ int nbparts)
+{
+ struct mtd_part *slave;
+ u_int32_t cur_offset = 0;
+ int i;
+
+ printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
+
+ for (i = 0; i < nbparts; i++) {
+
+ /* allocate the partition structure */
+ slave = kmalloc (sizeof(*slave), GFP_KERNEL);
+ if (!slave) {
+ printk ("memory allocation error while creating partitions for \"%s\"\n",
+ master->name);
+ del_mtd_partitions(master);
+ return -ENOMEM;
+ }
+ memset(slave, 0, sizeof(*slave));
+ list_add(&slave->list, &mtd_partitions);
+
+ /* set up the MTD object for this partition */
+ slave->mtd.type = master->type;
+ slave->mtd.flags = master->flags & ~parts[i].mask_flags;
+ slave->mtd.size = parts[i].size;
+ slave->mtd.oobblock = master->oobblock;
+ slave->mtd.oobsize = master->oobsize;
+ slave->mtd.ecctype = master->ecctype;
+ slave->mtd.eccsize = master->eccsize;
+
+ slave->mtd.name = parts[i].name;
+ slave->mtd.bank_size = master->bank_size;
+
+ slave->mtd.module = master->module;
+
+ slave->mtd.read = part_read;
+ slave->mtd.write = part_write;
+ if (master->sync)
+ slave->mtd.sync = part_sync;
+ if (!i && master->suspend && master->resume) {
+ slave->mtd.suspend = part_suspend;
+ slave->mtd.resume = part_resume;
+ }
+
+ if (master->writev)
+ slave->mtd.writev = part_writev;
+ if (master->readv)
+ slave->mtd.readv = part_readv;
+ if (master->lock)
+ slave->mtd.lock = part_lock;
+ if (master->unlock)
+ slave->mtd.unlock = part_unlock;
+/* cdhm: fix add oob functions to partitions */
+ if (master->read_oob)
+ slave->mtd.read_oob = master->read_oob;
+ if (master->write_oob)
+ slave->mtd.write_oob = master->write_oob;
+/* cdhm: end of fix */
+ slave->mtd.erase = part_erase;
+ slave->master = master;
+ slave->offset = parts[i].offset;
+ slave->index = i;
+
+ if (slave->offset == MTDPART_OFS_APPEND)
+ slave->offset = cur_offset;
+ if (slave->mtd.size == MTDPART_SIZ_FULL)
+ slave->mtd.size = master->size - slave->offset;
+ cur_offset = slave->offset + slave->mtd.size;
+
+ printk (KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset,
+ slave->offset + slave->mtd.size, slave->mtd.name);
+
+ /* let's do some sanity checks */
+ if (slave->offset >= master->size) {
+ /* let's register it anyway to preserve ordering */
+ slave->offset = 0;
+ slave->mtd.size = 0;
+ printk ("mtd: partition \"%s\" is out of reach -- disabled\n",
+ parts[i].name);
+ }
+ if (slave->offset + slave->mtd.size > master->size) {
+ slave->mtd.size = master->size - slave->offset;
+ printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n",
+ parts[i].name, master->name, slave->mtd.size);
+ }
+ if (master->numeraseregions>1) {
+ /* Deal with variable erase size stuff */
+ int i;
+ struct mtd_erase_region_info *regions = master->eraseregions;
+
+ /* Find the first erase regions which is part of this partition. */
+ for (i=0; i < master->numeraseregions && slave->offset >= regions[i].offset; i++)
+ ;
+
+ for (i--; i < master->numeraseregions && slave->offset + slave->mtd.size > regions[i].offset; i++) {
+ if (slave->mtd.erasesize < regions[i].erasesize) {
+ slave->mtd.erasesize = regions[i].erasesize;
+ }
+ }
+ } else {
+ /* Single erase size */
+ slave->mtd.erasesize = master->erasesize;
+ }
+
+ if ((slave->mtd.flags & MTD_WRITEABLE) &&
+ (slave->offset % slave->mtd.erasesize)) {
+ /* Doesn't start on a boundary of major erase size */
+ /* FIXME: Let it be writable if it is on a boundary of _minor_ erase size though */
+ slave->mtd.flags &= ~MTD_WRITEABLE;
+ printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
+ parts[i].name);
+ }
+ if ((slave->mtd.flags & MTD_WRITEABLE) &&
+ (slave->mtd.size % slave->mtd.erasesize)) {
+ slave->mtd.flags &= ~MTD_WRITEABLE;
+ printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
+ parts[i].name);
+ }
+
+ /* register our partition */
+ add_mtd_device(&slave->mtd);
+ }
+
+ return 0;
+}
+
+EXPORT_SYMBOL(add_mtd_partitions);
+EXPORT_SYMBOL(del_mtd_partitions);
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>");
+MODULE_DESCRIPTION("Generic support for partitioning of MTD devices");
+
* This is the file system front-end to YAFFS that hooks it up to
* the VFS.
*
- * Special notes:
+ * Special notes:
* >> sb->u.generic_sbp points to the yaffs_Device associated with this superblock
* >> inode->u.generic_ip points to the associated yaffs_Object.
*/
#include "yaffs_guts.h"
#ifdef YAFFS_RAM_ENABLED
-#include "yaffs_nandemul.h"
+#include "yaffs_nandemul.h"
// 2 MB of RAM for emulation
#define YAFFS_RAM_EMULATION_SIZE 0x200000
#endif // YAFFS_RAM_ENABLED
create: yaffs_create,
lookup: yaffs_lookup,
link: yaffs_link,
- unlink: yaffs_unlink,
+ unlink: yaffs_unlink,
symlink: yaffs_symlink,
mkdir: yaffs_mkdir,
rmdir: yaffs_unlink,
{
yaffs_Object *obj;
struct inode *inode;
-
-
+
+
T((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);
-
+
if(obj)
{
T((KERN_DEBUG"yaffs_lookup found %d\n",obj->objectId));
-
+
inode = yaffs_get_inode(dir->i_sb, obj->st_mode,0,obj);
-
+
if(inode)
{
T((KERN_DEBUG"yaffs_loookup looks good\n"));
else
{
T((KERN_DEBUG"yaffs_lookup not found\n"));
-
+
}
return NULL;
-
+
}
// For now put inode is just for debugging
static void yaffs_FillInodeFromObject(struct inode *inode, yaffs_Object *obj)
{
- if (inode && obj)
+ if (inode && obj)
{
inode->i_ino = obj->objectId;
inode->i_mode = obj->st_mode;
inode->i_ctime = obj->st_ctime;
inode->i_size = yaffs_GetObjectFileLength(obj);
inode->i_nlink = yaffs_GetObjectLinkCount(obj);
-
+
T((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->st_mode & S_IFMT)
+
+ switch (obj->st_mode & S_IFMT)
{
default:
// init_special_inode(inode, mode, dev);
break;
- case S_IFREG: // file
+ case S_IFREG: // file
inode->i_op = &yaffs_file_inode_operations;
inode->i_fop = &yaffs_file_operations;
break;
inode->i_op = &page_symlink_inode_operations;
break;
}
-
-
+
+
inode->u.generic_ip = obj;
-
+
}
else
{
struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev,yaffs_Object *obj)
{
struct inode * inode;
-
+
T((KERN_DEBUG"yaffs_get_inode for object %d\n",obj->objectId));
inode = iget(sb,obj->objectId);
yaffs_Object *obj;
int nRead,ipos;
struct inode *inode;
-
+
T((KERN_DEBUG"yaffs_file_read\n"));
obj = yaffs_DentryToObject(f->f_dentry);
inode = f->f_dentry->d_inode;
-
- if (*pos < inode->i_size)
+
+ if (*pos < inode->i_size)
{
if (*pos + n > inode->i_size)
{
{
n = 0;
}
-
+
nRead = yaffs_ReadDataFromFile(obj,buf,*pos,n);
if(nRead > 0)
{
ipos = *pos;
T((KERN_DEBUG"yaffs_file_read read %d bytes, %d read at %d\n",n,nRead,ipos));
return nRead;
-
+
}
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;
-
+
obj = yaffs_DentryToObject(f->f_dentry);
inode = f->f_dentry->d_inode;
nWritten = yaffs_WriteDataToFile(obj,buf,*pos,n);
inode->i_size = *pos;
T((KERN_DEBUG"yaffs_file_write size updated to %d\n",ipos));
}
-
+
}
- return nWritten;
+ return nWritten;
}
yaffs_Object *obj;
struct inode *inode = f->f_dentry->d_inode;
unsigned long offset, curoffs;
- struct list_head *i;
+ struct list_head *i;
yaffs_Object *l;
-
+
char name[YAFFS_MAX_NAME_LENGTH +1];
-
+
obj = yaffs_DentryToObject(f->f_dentry);
-
+
offset = f->f_pos;
-
+
T(("yaffs_readdir: starting at %d\n",(int)offset));
-
+
if(offset == 0)
{
T((KERN_DEBUG"yaffs_readdir: entry . ino %d \n",(int)inode->i_ino));
offset++;
f->f_pos++;
}
-
+
curoffs = 1;
-
+
//down(&obj->sem);
-
+
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);
+
+ yaffs_GetObjectName(l,name,YAFFS_MAX_NAME_LENGTH+1);
T((KERN_DEBUG"yaffs_readdir: %s inode %d\n",name,yaffs_GetObjectInode(l)));
-
+
if(filldir(dirent,
name,
strlen(name),
{
goto up_and_out;
}
-
+
offset++;
- f->f_pos++;
+ f->f_pos++;
}
}
up_and_out:
//up(&obj->sem);
-
+
out:
return 0;
}
static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev)
{
struct inode *inode;
-
+
yaffs_Object *obj = NULL;
yaffs_Object *parent = yaffs_InodeToObject(dir);
-
+
int error = -ENOSPC;
if(parent)
T((KERN_DEBUG"yaffs_mknod: could not get parent object\n"));
return -EPERM;
}
-
+
T(("yaffs_mknod: making oject for %s, mode %x\n",
dentry->d_name.name, mode));
- switch (mode & S_IFMT)
+ switch (mode & S_IFMT)
{
default:
-
+
break;
- case S_IFREG: // file
+ case S_IFREG: // file
T((KERN_DEBUG"yaffs_mknod: making file\n"));
obj = yaffs_MknodFile(parent,dentry->d_name.name,mode,current->uid, current->gid);
break;
obj = NULL; // Todo
break;
}
-
+
if(obj)
{
inode = yaffs_get_inode(dir->i_sb, mode, dev, obj);
T((KERN_DEBUG"yaffs_mknod failed making object\n"));
error = -ENOMEM;
}
-
+
return error;
}
static int yaffs_unlink(struct inode * dir, struct dentry *dentry)
{
-
+
T((KERN_DEBUG"yaffs_unlink\n"));
-
+
if(yaffs_Unlink(yaffs_InodeToObject(dir),dentry->d_name.name) == YAFFS_OK)
{
return 0;
static int yaffs_link(struct dentry *old_dentry, struct inode * dir, struct dentry * dentry)
{
struct inode *inode = old_dentry->d_inode;
-
+
T((KERN_DEBUG"yaffs_link\n"));
-
+
return -EPERM; //Todo
-
+
if (S_ISDIR(inode->i_mode))
return -EPERM;
static int yaffs_symlink(struct inode * dir, struct dentry *dentry, const char * symname)
{
int error;
-
+
T((KERN_DEBUG"yaffs_symlink\n"));
-
+
return -ENOMEM; //Todo
error = yaffs_mknod(dir, dentry, S_IFLNK | S_IRWXUGO, 0);
*/
static int yaffs_rename(struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir,struct dentry *new_dentry)
{
-
-
+
+
if( yaffs_RenameObject(yaffs_InodeToObject(old_dir),old_dentry->d_name.name,
yaffs_InodeToObject(new_dir),new_dentry->d_name.name) == YAFFS_OK)
{
{
return -ENOTEMPTY;
}
-
+
}
{
struct inode *inode = dentry->d_inode;
int error;
-
+
T((KERN_DEBUG"yaffs_setattr\n"));
-
+
if((error = inode_change_ok(inode,attr)) == 0)
{
-
+
if(yaffs_SetAttributes(yaffs_InodeToObject(inode),attr) == YAFFS_OK)
{
error = 0;
static void yaffs_read_inode (struct inode *inode)
{
- yaffs_Object *obj ;
-
+ yaffs_Object *obj ;
+
T((KERN_DEBUG"yaffs_read_inode for %d\n",(int)inode->i_ino));
obj = yaffs_FindObjectByNumber(yaffs_SuperToDevice(inode->i_sb),inode->i_ino);
-
+
yaffs_FillInodeFromObject(inode,obj);
}
struct inode * inode;
struct dentry * root;
yaffs_Device *dev;
+
-
- T(("yaffs_read_super:\n"));
+ T(("yaffs_read_super: %s\n", useRam ? "RAM" : "MTD"));
sb->s_blocksize = YAFFS_BYTES_PER_CHUNK;
sb->s_blocksize_bits = YAFFS_CHUNK_SIZE_SHIFT;
sb->s_magic = YAFFS_MAGIC;
sb->s_op = &yaffs_super_ops;
-
+
if(!sb)
- printk(KERN_INFO"sb is NULL\n");
+ printk(KERN_INFO"yaffs: sb is NULL\n");
else if(!sb->s_dev)
- printk(KERN_INFO"sb->s_dev is NULL\n");
+ printk(KERN_INFO"yaffs: sb->s_dev is NULL\n");
else if(! kdevname(sb->s_dev))
- printk(KERN_INFO"kdevname is NULL\n");
+ printk(KERN_INFO"yaffs: kdevname is NULL\n");
else
- printk(KERN_INFO"dev is %d name is \"%s\"\n", sb->s_dev, kdevname(sb->s_dev));
-
+ printk(KERN_INFO"yaffs: dev is %d name is \"%s\"\n", sb->s_dev, kdevname(sb->s_dev));
+
+
if(useRam)
{
}
else
- {
+ {
#ifdef YAFFS_MTD_ENABLED
struct mtd_info *mtd;
-
+
+ printk(KERN_DEBUG "yaffs: Attempting MTD mount on %u.%u, \"%s\"\n",
+ MAJOR(sb->s_dev),MINOR(sb->s_dev),kdevname(sb->s_dev));
+
// Hope it's a NAND mtd
mtd = get_mtd_device(NULL, MINOR(sb->s_dev));
- if (!mtd)
+ if (!mtd)
{
printk(KERN_DEBUG "yaffs: MTD device #%u doesn't appear to exist\n", MINOR(sb->s_dev));
return NULL;
}
+
if(mtd->type != MTD_NANDFLASH)
{
printk(KERN_DEBUG "yaffs: MTD device is not NAND it's type %d\n", mtd->type);
return NULL;
}
+ printk(KERN_DEBUG" erase %x\n",mtd->erase);
+ printk(KERN_DEBUG" read %x\n",mtd->read);
+ printk(KERN_DEBUG" write %x\n",mtd->write);
+ printk(KERN_DEBUG" readoob %x\n",mtd->read_oob);
+ printk(KERN_DEBUG" writeoob %x\n",mtd->write_oob);
+ printk(KERN_DEBUG" oobblock %x\n",mtd->oobblock);
+ printk(KERN_DEBUG" oobsize %x\n",mtd->oobsize);
+
+
if(!mtd->erase ||
!mtd->read ||
!mtd->write ||
printk(KERN_DEBUG "yaffs: MTD device does not support required functions\n");
return NULL;
}
-
+
if(mtd->oobblock != YAFFS_BYTES_PER_CHUNK ||
mtd->oobsize != YAFFS_BYTES_PER_SPARE)
{
printk(KERN_DEBUG "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
+ // 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 ram emulation
}
memset(dev,0,sizeof(yaffs_Device));
- dev->genericDevice = mtd;
+ dev->genericDevice = mtd;
// Set up the memory size parameters....
-
- dev->nBlocks = mtd->size / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK);
+
+ dev->nBlocks = YAFFS_RAM_EMULATION_SIZE / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK);
dev->startBlock = 1; // Don't use block 0
dev->endBlock = dev->nBlocks - 1;
dev->readChunkFromNAND = nandmtd_ReadChunkFromNAND;
dev->eraseBlockInNAND = nandmtd_EraseBlockInNAND;
dev->initialiseNAND = nandmtd_InitialiseNAND;
-
+
#endif
}
return NULL;
T(("yaffs_read_super: got root inode\n"));
-
+
root = d_alloc_root(inode);
if (offset > 0) return 0;
/* Fill the buffer and get its length */
- sprintf( my_buffer,
+ sprintf( my_buffer,
"YAFFS built:"__DATE__ " "__TIME__"\n"
-
+
);
strcpy(page,my_buffer);
static int __init init_yaffs_fs(void)
{
int error = 0;
-
+
printk(KERN_DEBUG "yaffs " __DATE__ " " __TIME__ " Initialisation\n");
/* Install the proc_fs entry */
my_proc_entry = create_proc_read_entry("yaffs",
printk(KERN_DEBUG "yaffs " __DATE__ " " __TIME__ " Clean up\n");
remove_proc_entry("yaffs",&proc_root);
-
+
#ifdef YAFFS_RAM_ENABLED
- unregister_filesystem(&yaffs_ram_fs_type);
+ unregister_filesystem(&yaffs_fs_type);
#endif
#ifdef YAFFS_MTD_ENABLED
- unregister_filesystem(&yaffs_fs_type);
+ unregister_filesystem(&yaffs_ram_fs_type);
#endif
}