CFLAGS = -D__KERNEL__ -DMODULE $(USE_RAM_FOR_TEST) $(USE_MTD) -I$(KERNELDIR)/include -O2 -Wall
-OBJS = yaffs_fs.o yaffs_guts.o yaffs_ramem.o yaffs_mtdif.o
+OBJS = yaffs_fs.o yaffs_guts.o yaffs_ramem.o yaffs_mtdif.o nand_ecc.o
all: yaffs.o
--- /dev/null
+/*
+ * drivers/mtd/nand_ecc.c
+ *
+ * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
+ * Toshiba America Electronics Components, Inc.
+ *
+ * $Id: nand_ecc.c,v 1.1 2002-05-27 18:57:25 charles Exp $
+ *
+ * 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 file contains an ECC algorithm from Toshiba that detects and
+ * corrects 1 bit errors in a 256 byte block of data.
+ *
+ *
+ * Slightly hacked to fit in with YAFFS by Charles Manning.
+ */
+#if 0
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#endif
+
+#include "yportenv.h"
+
+/*
+ * Pre-calculated 256-way 1 byte column parity
+ */
+static const u_char nand_ecc_precalc_table[] = {
+ 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
+ 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
+ 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
+ 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
+ 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
+ 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
+ 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
+ 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
+ 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
+ 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
+ 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
+ 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
+ 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
+ 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
+ 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
+ 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
+};
+
+
+/*
+ * Creates non-inverted ECC code from line parity
+ */
+static void nand_trans_result(u_char reg2, u_char reg3,
+ u_char *ecc_code)
+{
+ u_char a, b, i, tmp1, tmp2;
+
+ /* Initialize variables */
+ a = b = 0x80;
+ tmp1 = tmp2 = 0;
+
+ /* Calculate first ECC byte */
+ for (i = 0; i < 4; i++) {
+ if (reg3 & a) /* LP15,13,11,9 --> ecc_code[0] */
+ tmp1 |= b;
+ b >>= 1;
+ if (reg2 & a) /* LP14,12,10,8 --> ecc_code[0] */
+ tmp1 |= b;
+ b >>= 1;
+ a >>= 1;
+ }
+
+ /* Calculate second ECC byte */
+ b = 0x80;
+ for (i = 0; i < 4; i++) {
+ if (reg3 & a) /* LP7,5,3,1 --> ecc_code[1] */
+ tmp2 |= b;
+ b >>= 1;
+ if (reg2 & a) /* LP6,4,2,0 --> ecc_code[1] */
+ tmp2 |= b;
+ b >>= 1;
+ a >>= 1;
+ }
+
+ /* Store two of the ECC bytes */
+ ecc_code[0] = tmp1;
+ ecc_code[1] = tmp2;
+}
+
+/*
+ * Calculate 3 byte ECC code for 256 byte block
+ */
+void nand_calculate_ecc (const u_char *dat, u_char *ecc_code)
+{
+ u_char idx, reg1, reg2, reg3;
+ int j;
+
+ /* Initialize variables */
+ reg1 = reg2 = reg3 = 0;
+ ecc_code[0] = ecc_code[1] = ecc_code[2] = 0;
+
+ /* Build up column parity */
+ for(j = 0; j < 256; j++) {
+
+ /* Get CP0 - CP5 from table */
+ idx = nand_ecc_precalc_table[dat[j]];
+ reg1 ^= (idx & 0x3f);
+
+ /* All bit XOR = 1 ? */
+ if (idx & 0x40) {
+ reg3 ^= (u_char) j;
+ reg2 ^= ~((u_char) j);
+ }
+ }
+
+ /* Create non-inverted ECC code from line parity */
+ nand_trans_result(reg2, reg3, ecc_code);
+
+ /* Calculate final ECC code */
+ ecc_code[0] = ~ecc_code[0];
+ ecc_code[1] = ~ecc_code[1];
+ ecc_code[2] = ((~reg1) << 2) | 0x03;
+}
+
+/*
+ * Detect and correct a 1 bit error for 256 byte block
+ */
+int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc)
+{
+ u_char a, b, c, d1, d2, d3, add, bit, i;
+
+ /* Do error detection */
+ d1 = calc_ecc[0] ^ read_ecc[0];
+ d2 = calc_ecc[1] ^ read_ecc[1];
+ d3 = calc_ecc[2] ^ read_ecc[2];
+
+ if ((d1 | d2 | d3) == 0) {
+ /* No errors */
+ return 0;
+ }
+ else {
+ a = (d1 ^ (d1 >> 1)) & 0x55;
+ b = (d2 ^ (d2 >> 1)) & 0x55;
+ c = (d3 ^ (d3 >> 1)) & 0x54;
+
+ /* Found and will correct single bit error in the data */
+ if ((a == 0x55) && (b == 0x55) && (c == 0x54)) {
+ c = 0x80;
+ add = 0;
+ a = 0x80;
+ for (i=0; i<4; i++) {
+ if (d1 & c)
+ add |= a;
+ c >>= 2;
+ a >>= 1;
+ }
+ c = 0x80;
+ for (i=0; i<4; i++) {
+ if (d2 & c)
+ add |= a;
+ c >>= 2;
+ a >>= 1;
+ }
+ bit = 0;
+ b = 0x04;
+ c = 0x80;
+ for (i=0; i<3; i++) {
+ if (d3 & c)
+ bit |= b;
+ c >>= 2;
+ b >>= 1;
+ }
+ b = 0x01;
+ a = dat[add];
+ a ^= (b << bit);
+ dat[add] = a;
+ return 1;
+ }
+ else {
+ i = 0;
+ while (d1) {
+ if (d1 & 0x01)
+ ++i;
+ d1 >>= 1;
+ }
+ while (d2) {
+ if (d2 & 0x01)
+ ++i;
+ d2 >>= 1;
+ }
+ while (d3) {
+ if (d3 & 0x01)
+ ++i;
+ d3 >>= 1;
+ }
+ if (i == 1) {
+ /* ECC Code Error Correction */
+ read_ecc[0] = calc_ecc[0];
+ read_ecc[1] = calc_ecc[1];
+ read_ecc[2] = calc_ecc[2];
+ return 2;
+ }
+ else {
+ /* Uncorrectable Error */
+ return -1;
+ }
+ }
+ }
+
+ /* Should never happen */
+ return -1;
+}
+
+
+#if 0
+EXPORT_SYMBOL(nand_calculate_ecc);
+EXPORT_SYMBOL(nand_correct_data);
+#endif
+
buf->f_blocks = yaffs_SuperToDevice(sb)->nBlocks * YAFFS_CHUNKS_PER_BLOCK;
buf->f_files = 0;
buf->f_ffree = 0;
- buf->f_bavail = yaffs_GetNumberOfFreeChunks(yaffs_SuperToDevice(sb));
+ buf->f_bavail = buf->f_bfree = yaffs_GetNumberOfFreeChunks(yaffs_SuperToDevice(sb));
return 0;
}
#define T(x)
#endif
+// 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);
+
// 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 int yaffs_CheckObjectHashSanity(yaffs_Device *dev);
static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr);
static void yaffs_GetTagsFromSpare(yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr);
-static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkInNAND);
+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);
int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare)
{
- return dev->readChunkFromNAND(dev,chunkInNAND,data,spare);
+ int retVal;
+ __u8 calcEcc[3];
+ yaffs_Spare localSpare;
+
+ if(!spare && data)
+ {
+ // If we don't have a real spare, then we use a local one.
+ // Need this for the calculation of the ecc
+ spare = &localSpare;
+ }
+
+ retVal = dev->readChunkFromNAND(dev,chunkInNAND,data,spare);
+ if(data)
+ {
+ // Do ECC correction
+ //Todo handle any errors
+ nand_calculate_ecc(data,calcEcc);
+ nand_correct_data (data,spare->ecc1, calcEcc);
+ nand_calculate_ecc(&data[256],calcEcc);
+ nand_correct_data (&data[256],spare->ecc2, calcEcc);
+ }
+ return retVal;
}
int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,int blockInNAND)
int writeOk = 0;
+ unsigned char rbData[YAFFS_BYTES_PER_CHUNK];
+ yaffs_Spare rbSpare;
+
do{
chunk = yaffs_AllocateChunk(dev,useReserve);
writeOk = yaffs_WriteChunkToNAND(dev,chunk,data,spare);
if(writeOk)
{
- //Todo read-back and verify
+ // Readback & verify
// If verify fails, then delete this chunk and try again
+ // To verify we compare everything except the block and
+ // page status bytes.
+ yaffs_ReadChunkFromNAND(dev,chunk,rbData,&rbSpare);
+
+ if(memcmp(data,rbData,YAFFS_BYTES_PER_CHUNK) != 0 ||
+ spare->tagByte0 != rbSpare.tagByte0 ||
+ spare->tagByte1 != rbSpare.tagByte1 ||
+ spare->tagByte2 != rbSpare.tagByte2 ||
+ spare->tagByte3 != rbSpare.tagByte3 ||
+ spare->tagByte4 != rbSpare.tagByte4 ||
+ spare->tagByte5 != rbSpare.tagByte5 ||
+ spare->tagByte6 != rbSpare.tagByte6 ||
+ spare->tagByte7 != rbSpare.tagByte7 ||
+ spare->ecc1[0] != rbSpare.ecc1[0] ||
+ spare->ecc1[1] != rbSpare.ecc1[1] ||
+ spare->ecc1[2] != rbSpare.ecc1[2] ||
+ spare->ecc2[0] != rbSpare.ecc2[0] ||
+ spare->ecc2[1] != rbSpare.ecc2[1] ||
+ spare->ecc2[2] != rbSpare.ecc2[2] )
+ {
+ // Didn't verify
+ yaffs_DeleteChunk(dev,chunk);
+ writeOk = 0;
+ }
+
}
}
} while(chunk >= 0 && ! writeOk);
}
-void yaffs_CalcECC(const __u8 *buffer, yaffs_Spare *spare)
+void yaffs_CalcECC(const __u8 *data, yaffs_Spare *spare)
{
-
- // Todo do nothing now. Need to put in ecc
- spare->ecc1[0] = spare->ecc1[1] = spare->ecc1[2] = 0xFF;
- spare->ecc2[0] = spare->ecc2[1] = spare->ecc2[2] = 0xFF;
+ nand_calculate_ecc (data , spare->ecc1);
+ nand_calculate_ecc (&data[256] , spare->ecc2);
}
void yaffs_CalcTagsECC(yaffs_Tags *tags)
{
// Todo don't do anything yet. Need to calculate ecc
- tags->ecc = 0xFFFFFFFF;
+ 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 &0x7f; j<<=1)
+ {
+ bit++;
+ if(b[i] & j)
+ {
+ ecc ^= bit;
+ }
+ }
+ }
+
+ tags->ecc = ecc;
+
+
}
+void yaffs_CheckECCOnTags(yaffs_Tags *tags)
+{
+ unsigned ecc = tags->ecc;
+
+ yaffs_CalcTagsECC(tags);
+
+ ecc ^= tags->ecc;
+
+ if(ecc)
+ {
+ // Needs fixing
+ unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes;
+
+ ecc--;
+
+ b[ecc / 8] ^= (1 << (ecc & 7));
+
+ // Now recvalc the ecc
+ yaffs_CalcTagsECC(tags);
+ }
+}
///////////////////////// TNODES ///////////////////////
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_UNKNOWN:
// todo this should not happen
else
{
// It's a data chunk
- yaffs_PutChunkIntoFile(object, tags.chunkId, newChunk);
+ yaffs_PutChunkIntoFile(object, tags.chunkId, newChunk,0);
}
tu->asBytes[6]= sparePtr->tagByte6;
tu->asBytes[7]= sparePtr->tagByte7;
- // Todo Check ECC on tags
+ yaffs_CheckECCOnTags(tagsPtr);
}
static void yaffs_SpareInitialise(yaffs_Spare *spare)
#endif
-static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkInNAND)
+static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkInNAND, int inScan)
{
yaffs_Tnode *tn;
yaffs_Device *dev = in->myDev;
+ int existingChunk;
+ yaffs_Tags existingTags;
+ yaffs_Tags newTags;
+ unsigned existingSerial, newSerial;
+
tn = yaffs_AddOrFindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);
+
+ if(inScan)
+ {
+ // 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.
+
+ existingChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK];
+
+ if(existingChunk != 0)
+ {
+ // We have a duplicate now we need to decide which one to use
+ // To do this we get both sets of tags and compare serial numbers.
+ yaffs_ReadChunkTagsFromNAND(dev,chunkInInode, &newTags);
+ yaffs_ReadChunkTagsFromNAND(dev,existingChunk, &existingTags);
+ newSerial = newTags.serialNumber;
+ existingSerial = existingTags.serialNumber;
+ if(((existingSerial+1) & 3) == newSerial)
+ {
+ // Use new
+ // Delete the old one and drop through to update the tnode
+ yaffs_DeleteChunk(dev,existingChunk);
+ }
+ else
+ {
+ // Use existing.
+ // Delete the new one and return early so that the tnode isn't changed
+ yaffs_DeleteChunk(dev,chunkInInode);
+ return YAFFS_OK;
+ }
+ }
+ }
+
tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] = chunkInNAND;
+
return YAFFS_OK;
}
}
else
{
- T(("Bad news deteing chunk %d\n",chunkId));
+ T(("Bad news deleting chunk %d\n",chunkId));
}
}
newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,buffer,&newTags,useReserve);
if(newChunkId >= 0)
{
- yaffs_PutChunkIntoFile(in,chunkInInode,newChunkId);
+ yaffs_PutChunkIntoFile(in,chunkInInode,newChunkId,0);
if(prevChunkId >= 0)
switch(in->variantType)
{
- case YAFFS_OBJECT_TYPE_UNKNOWN: // Todo got a problem
+ case YAFFS_OBJECT_TYPE_UNKNOWN:
+ // Should not happen
break;
case YAFFS_OBJECT_TYPE_FILE:
oh->fileSize = in->variant.fileVariant.fileSize;
case YAFFS_OBJECT_TYPE_HARDLINK:
oh->equivalentObjectId = in->variant.hardLinkVariant.equivalentObjectId;
break;
- case YAFFS_OBJECT_TYPE_DIRECTORY: // Do nothing
+ case YAFFS_OBJECT_TYPE_DIRECTORY:
+ // Do nothing
break;
case YAFFS_OBJECT_TYPE_SYMLINK:
strncpy(oh->alias,in->variant.symLinkVariant.alias,YAFFS_MAX_ALIAS_LENGTH);
inuse++;
pageBits |= ( 1 <<c);
in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,YAFFS_OBJECT_TYPE_FILE);
- // todo check for a clash (two data chunks with
+ // PutChuunkIntoFIle checks for a clash (two data chunks with
// the same chunkId).
- yaffs_PutChunkIntoFile(in,tags.chunkId,chunk);
+ yaffs_PutChunkIntoFile(in,tags.chunkId,chunk,1);
T((" %d %d data %d %d\n",blk,c,tags.objectId,tags.chunkId));
}
else
__u8 tagByte1;
__u8 tagByte2;
__u8 tagByte3;
- __u8 pageStatus;
+ __u8 pageStatus; // Currently unused, but sort of set aside to distinguish
+ // unused - vs- used -vs- deleted chunks. We achieve this by
+ // using the objectId tags.
__u8 blockStatus;
__u8 tagByte4;
__u8 tagByte5;
void *genericDevice; // Pointer to device context
// On an mtd this holds the mtd pointer.
+
+#ifdef __KERNEL__
+
struct semaphore sem;// Semaphore for waiting on erasure.
+
+#endif
// NAND access functions (Must be set before calling YAFFS)
//yaffs_ramem.c
// Since this creates the RAM block at start up it is pretty useless for testing the scanner.
+#ifndef __KERNEL__
+#define YAFFS_RAM_ENABLED
+#endif
+
#ifdef YAFFS_RAM_ENABLED
#include "yportenv.h"
+++ /dev/null
-/*
- * JFFS2 -- Journalling Flash File System, Version 2.
- *
- * Copyright (C) 2001 Red Hat, Inc.
- *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
- *
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
- *
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence. You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above. If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL. If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: yaffs_super.c,v 1.1 2002-05-20 17:42:08 aleph1 Exp $
- *
- */
-
-#include <linux/config.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/version.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/fs.h>
-#include <linux/jffs2.h>
-#include <linux/pagemap.h>
-#include <linux/mtd/mtd.h>
-#include <linux/interrupt.h>
-#include "nodelist.h"
-
-#ifndef MTD_BLOCK_MAJOR
-#define MTD_BLOCK_MAJOR 31
-#endif
-
-extern void jffs2_read_inode (struct inode *);
-void jffs2_put_super (struct super_block *);
-void jffs2_write_super (struct super_block *);
-static int jffs2_statfs (struct super_block *, struct statfs *);
-int jffs2_remount_fs (struct super_block *, int *, char *);
-extern void jffs2_clear_inode (struct inode *);
-
-static struct super_operations jffs2_super_operations =
-{
- read_inode: jffs2_read_inode,
-// delete_inode: jffs2_delete_inode,
- put_super: jffs2_put_super,
- write_super: jffs2_write_super,
- statfs: jffs2_statfs,
- remount_fs: jffs2_remount_fs,
- clear_inode: jffs2_clear_inode
-};
-
-static int jffs2_statfs(struct super_block *sb, struct statfs *buf)
-{
- struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
- unsigned long avail;
-
- buf->f_type = JFFS2_SUPER_MAGIC;
- buf->f_bsize = 1 << PAGE_SHIFT;
- buf->f_blocks = c->flash_size >> PAGE_SHIFT;
- buf->f_files = 0;
- buf->f_ffree = 0;
- buf->f_namelen = JFFS2_MAX_NAME_LEN;
-
- spin_lock_bh(&c->erase_completion_lock);
-
- avail = c->dirty_size + c->free_size;
- if (avail > c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE)
- avail -= c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE;
- else
- avail = 0;
-
- buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT;
-
-#if CONFIG_JFFS2_FS_DEBUG > 0
- printk(KERN_DEBUG "STATFS:\n");
- printk(KERN_DEBUG "flash_size: %08x\n", c->flash_size);
- printk(KERN_DEBUG "used_size: %08x\n", c->used_size);
- printk(KERN_DEBUG "dirty_size: %08x\n", c->dirty_size);
- printk(KERN_DEBUG "free_size: %08x\n", c->free_size);
- printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size);
- printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size);
- printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size);
-
- if (c->nextblock) {
- printk(KERN_DEBUG "nextblock: 0x%08x\n", c->nextblock->offset);
- } else {
- printk(KERN_DEBUG "nextblock: NULL\n");
- }
- if (c->gcblock) {
- printk(KERN_DEBUG "gcblock: 0x%08x\n", c->gcblock->offset);
- } else {
- printk(KERN_DEBUG "gcblock: NULL\n");
- }
- if (list_empty(&c->clean_list)) {
- printk(KERN_DEBUG "clean_list: empty\n");
- } else {
- struct list_head *this;
-
- list_for_each(this, &c->clean_list) {
- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
- printk(KERN_DEBUG "clean_list: %08x\n", jeb->offset);
- }
- }
- if (list_empty(&c->dirty_list)) {
- printk(KERN_DEBUG "dirty_list: empty\n");
- } else {
- struct list_head *this;
-
- list_for_each(this, &c->dirty_list) {
- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
- printk(KERN_DEBUG "dirty_list: %08x\n", jeb->offset);
- }
- }
- if (list_empty(&c->erasing_list)) {
- printk(KERN_DEBUG "erasing_list: empty\n");
- } else {
- struct list_head *this;
-
- list_for_each(this, &c->erasing_list) {
- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
- printk(KERN_DEBUG "erasing_list: %08x\n", jeb->offset);
- }
- }
- if (list_empty(&c->erase_pending_list)) {
- printk(KERN_DEBUG "erase_pending_list: empty\n");
- } else {
- struct list_head *this;
-
- list_for_each(this, &c->erase_pending_list) {
- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
- printk(KERN_DEBUG "erase_pending_list: %08x\n", jeb->offset);
- }
- }
- if (list_empty(&c->free_list)) {
- printk(KERN_DEBUG "free_list: empty\n");
- } else {
- struct list_head *this;
-
- list_for_each(this, &c->free_list) {
- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
- printk(KERN_DEBUG "free_list: %08x\n", jeb->offset);
- }
- }
- if (list_empty(&c->bad_list)) {
- printk(KERN_DEBUG "bad_list: empty\n");
- } else {
- struct list_head *this;
-
- list_for_each(this, &c->bad_list) {
- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
- printk(KERN_DEBUG "bad_list: %08x\n", jeb->offset);
- }
- }
- if (list_empty(&c->bad_used_list)) {
- printk(KERN_DEBUG "bad_used_list: empty\n");
- } else {
- struct list_head *this;
-
- list_for_each(this, &c->bad_used_list) {
- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
- printk(KERN_DEBUG "bad_used_list: %08x\n", jeb->offset);
- }
- }
-#endif /* CONFIG_JFFS2_FS_DEBUG */
-
- spin_unlock_bh(&c->erase_completion_lock);
-
-
- return 0;
-}
-
-static struct super_block *jffs2_read_super(struct super_block *sb, void *data, int silent)
-{
- struct jffs2_sb_info *c;
- struct inode *root_i;
- int i;
-
- D1(printk(KERN_DEBUG "jffs2: read_super for device %s\n", kdevname(sb->s_dev)));
-
- if (MAJOR(sb->s_dev) != MTD_BLOCK_MAJOR) {
- if (!silent)
- printk(KERN_DEBUG "jffs2: attempt to mount non-MTD device %s\n", kdevname(sb->s_dev));
- return NULL;
- }
-
- c = JFFS2_SB_INFO(sb);
- memset(c, 0, sizeof(*c));
-
- c->mtd = get_mtd_device(NULL, MINOR(sb->s_dev));
- if (!c->mtd) {
- D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", MINOR(sb->s_dev)));
- return NULL;
- }
- c->sector_size = c->mtd->erasesize;
- c->free_size = c->flash_size = c->mtd->size;
- c->nr_blocks = c->mtd->size / c->mtd->erasesize;
- c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL);
- if (!c->blocks)
- goto out_mtd;
- for (i=0; i<c->nr_blocks; i++) {
- INIT_LIST_HEAD(&c->blocks[i].list);
- c->blocks[i].offset = i * c->sector_size;
- c->blocks[i].free_size = c->sector_size;
- c->blocks[i].dirty_size = 0;
- c->blocks[i].used_size = 0;
- c->blocks[i].first_node = NULL;
- c->blocks[i].last_node = NULL;
- }
-
- spin_lock_init(&c->nodelist_lock);
- init_MUTEX(&c->alloc_sem);
- init_waitqueue_head(&c->erase_wait);
- spin_lock_init(&c->erase_completion_lock);
- spin_lock_init(&c->inocache_lock);
-
- INIT_LIST_HEAD(&c->clean_list);
- INIT_LIST_HEAD(&c->dirty_list);
- INIT_LIST_HEAD(&c->erasing_list);
- INIT_LIST_HEAD(&c->erase_pending_list);
- INIT_LIST_HEAD(&c->erase_complete_list);
- INIT_LIST_HEAD(&c->free_list);
- INIT_LIST_HEAD(&c->bad_list);
- INIT_LIST_HEAD(&c->bad_used_list);
- c->highest_ino = 1;
-
- if (jffs2_build_filesystem(c)) {
- D1(printk(KERN_DEBUG "build_fs failed\n"));
- goto out_nodes;
- }
- sb->s_op = &jffs2_super_operations;
-
- D1(printk(KERN_DEBUG "jffs2_read_super(): Getting root inode\n"));
- root_i = iget(sb, 1);
- if (is_bad_inode(root_i)) {
- D1(printk(KERN_WARNING "get root inode failed\n"));
- goto out_nodes;
- }
-
- D1(printk(KERN_DEBUG "jffs2_read_super(): d_alloc_root()\n"));
- sb->s_root = d_alloc_root(root_i);
- if (!sb->s_root)
- goto out_root_i;
-
-#if LINUX_VERSION_CODE >= 0x20403
- sb->s_maxbytes = 0xFFFFFFFF;
-#endif
- sb->s_blocksize = PAGE_CACHE_SIZE;
- sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
- sb->s_magic = JFFS2_SUPER_MAGIC;
- if (!(sb->s_flags & MS_RDONLY))
- jffs2_start_garbage_collect_thread(c);
- return sb;
-
- out_root_i:
- iput(root_i);
- out_nodes:
- jffs2_free_ino_caches(c);
- jffs2_free_raw_node_refs(c);
- kfree(c->blocks);
- out_mtd:
- put_mtd_device(c->mtd);
- return NULL;
-}
-
-void jffs2_put_super (struct super_block *sb)
-{
- struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
-
- D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n"));
-
- if (!(sb->s_flags & MS_RDONLY))
- jffs2_stop_garbage_collect_thread(c);
- jffs2_free_ino_caches(c);
- jffs2_free_raw_node_refs(c);
- kfree(c->blocks);
- if (c->mtd->sync)
- c->mtd->sync(c->mtd);
- put_mtd_device(c->mtd);
-
- D1(printk(KERN_DEBUG "jffs2_put_super returning\n"));
-}
-
-int jffs2_remount_fs (struct super_block *sb, int *flags, char *data)
-{
- struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
-
- if (c->flags & JFFS2_SB_FLAG_RO && !(sb->s_flags & MS_RDONLY))
- return -EROFS;
-
- /* We stop if it was running, then restart if it needs to.
- This also catches the case where it was stopped and this
- is just a remount to restart it */
- if (!(sb->s_flags & MS_RDONLY))
- jffs2_stop_garbage_collect_thread(c);
-
- if (!(*flags & MS_RDONLY))
- jffs2_start_garbage_collect_thread(c);
-
- sb->s_flags = (sb->s_flags & ~MS_RDONLY)|(*flags & MS_RDONLY);
-
- return 0;
-}
-
-void jffs2_write_super (struct super_block *sb)
-{
- struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
- sb->s_dirt = 0;
-
- if (sb->s_flags & MS_RDONLY)
- return;
-
- jffs2_garbage_collect_trigger(c);
- jffs2_erase_pending_blocks(c);
- jffs2_mark_erased_blocks(c);
-}
-
-
-static DECLARE_FSTYPE_DEV(jffs2_fs_type, "jffs2", jffs2_read_super);
-
-static int __init init_jffs2_fs(void)
-{
- int ret;
-
- printk(KERN_NOTICE "JFFS2 version 2.1. (C) 2001 Red Hat, Inc., designed by Axis Communications AB.\n");
-
-#ifdef JFFS2_OUT_OF_KERNEL
- /* sanity checks. Could we do these at compile time? */
- if (sizeof(struct jffs2_sb_info) > sizeof (((struct super_block *)NULL)->u)) {
- printk(KERN_ERR "JFFS2 error: struct jffs2_sb_info (%d bytes) doesn't fit in the super_block union (%d bytes)\n",
- sizeof(struct jffs2_sb_info), sizeof (((struct super_block *)NULL)->u));
- return -EIO;
- }
-
- if (sizeof(struct jffs2_inode_info) > sizeof (((struct inode *)NULL)->u)) {
- printk(KERN_ERR "JFFS2 error: struct jffs2_inode_info (%d bytes) doesn't fit in the inode union (%d bytes)\n",
- sizeof(struct jffs2_inode_info), sizeof (((struct inode *)NULL)->u));
- return -EIO;
- }
-#endif
-
- ret = jffs2_create_slab_caches();
- if (ret) {
- printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
- return ret;
- }
- ret = register_filesystem(&jffs2_fs_type);
- if (ret) {
- printk(KERN_ERR "JFFS2 error: Failed to register filesystem\n");
- jffs2_destroy_slab_caches();
- }
- return ret;
-}
-
-static void __exit exit_jffs2_fs(void)
-{
- jffs2_destroy_slab_caches();
- unregister_filesystem(&jffs2_fs_type);
-}
-
-module_init(init_jffs2_fs);
-module_exit(exit_jffs2_fs);
-
-MODULE_DESCRIPTION("The Journalling Flash File System, v2");
-MODULE_AUTHOR("Red Hat, Inc.");
-MODULE_LICENSE("GPL"); // Actually dual-licensed, but it doesn't matter for
- // the sake of this tag. It's Free Software.
-/*
- * 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.
- *
- */