*** empty log message ***
[yaffs/.git] / yaffs_guts.c
index 6bc6136e807285f2b4c692f3bf683bb2a77a24d7..88b02a3659cf7b51da032b6d0bf31ba7d2991dcc 100644 (file)
 #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.
@@ -63,7 +67,7 @@ static const char yaffs_countBits[256] =
 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);
@@ -78,8 +82,8 @@ loff_t yaffs_GetFileSize(yaffs_Object *obj);
 
 static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve);
 
-#if YAFFS_PARANOID
-static int yaffs_CheckFileSanity(yaffs_Object *in)
+#ifdef YAFFS_PARANOID
+static int yaffs_CheckFileSanity(yaffs_Object *in);
 #else
 #define yaffs_CheckFileSanity(in)
 #endif
@@ -108,9 +112,63 @@ static int yaffs_WriteChunkToNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND
 
 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;
 }
 
+#ifdef YAFFS_PARANOID
+
+static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND)
+{
+       static int init = 0;
+       static __u8 cmpbuf[YAFFS_BYTES_PER_CHUNK];
+       static __u8 data[YAFFS_BYTES_PER_CHUNK];
+       static __u8 spare[16];
+       
+       int retVal;
+       
+       retVal  = YAFFS_OK;
+       
+       dev->readChunkFromNAND(dev,chunkInNAND,data,(yaffs_Spare *)spare);
+       
+       
+       
+       if(!init)
+       {
+               memset(cmpbuf,0xff,YAFFS_BYTES_PER_CHUNK);
+               init = 1;
+       }
+       
+       if(memcmp(cmpbuf,data,YAFFS_BYTES_PER_CHUNK)) retVal = YAFFS_FAIL;
+       if(memcmp(cmpbuf,spare,16)) retVal = YAFFS_FAIL;
+       
+       return retVal;
+       
+}
+
+#endif
+
+
 int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,int blockInNAND)
 {
        return dev->eraseBlockInNAND(dev,blockInNAND);
@@ -127,6 +185,9 @@ static int yaffs_WriteNewChunkToNAND(struct yaffs_DeviceStruct *dev, const __u8
        
        int writeOk = 0;
        
+       unsigned char rbData[YAFFS_BYTES_PER_CHUNK];
+       yaffs_Spare rbSpare;
+       
        do{
                chunk = yaffs_AllocateChunk(dev,useReserve);
        
@@ -135,8 +196,33 @@ static int yaffs_WriteNewChunkToNAND(struct yaffs_DeviceStruct *dev, const __u8
                        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);
@@ -178,20 +264,60 @@ static __u16 yaffs_CalcNameSum(const char *name)
 }
 
 
-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 ///////////////////////
@@ -939,8 +1065,10 @@ yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev,int number,yaffs_ObjectTyp
                                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
@@ -1334,7 +1462,7 @@ static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve)
                }
 
 #ifdef YAFFS_PARANOID
-               if(yaffs_CheckChunkErased(retVal) == YAFFS_FAIL)
+               if(yaffs_CheckChunkErased(dev,retVal) == YAFFS_FAIL)
                {
                        T(("..................Trying to allocate non-erased page %d\n",retVal));
                }
@@ -1411,7 +1539,7 @@ int  yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)
                        else
                        {
                                // It's a data chunk
-                               yaffs_PutChunkIntoFile(object, tags.chunkId, newChunk);
+                               yaffs_PutChunkIntoFile(object, tags.chunkId, newChunk,0);
 
                        }
                        
@@ -1476,7 +1604,7 @@ static void yaffs_GetTagsFromSpare(yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr)
        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)
@@ -1582,7 +1710,7 @@ int yaffs_FindChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_Tags *tags)
                theChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] << dev->chunkGroupBits;
 
                // Now we need to do the shifting etc and search for it
-               for(i = 0,found = 0; i < dev->chunkGroupSize && !found; i++)
+               for(i = 0,found = 0; theChunk && i < dev->chunkGroupSize && !found; i++)
                {
                        yaffs_ReadChunkTagsFromNAND(dev,theChunk,tags);
                        if(tags->chunkId == chunkInInode &&
@@ -1624,7 +1752,7 @@ int yaffs_FindAndDeleteChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_Tags
                theChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] << dev->chunkGroupBits;
     
                // Now we need to do the shifting etc and search for it
-               for(i = 0,found = 0; i < dev->chunkGroupSize && !found; i++)
+               for(i = 0,found = 0; theChunk && i < dev->chunkGroupSize && !found; i++)
                {
                        yaffs_ReadChunkTagsFromNAND(dev,theChunk,tags);
                        if(tags->chunkId == chunkInInode &&
@@ -1685,20 +1813,20 @@ static int yaffs_CheckFileSanity(yaffs_Object *in)
        
        for(chunk = 1; chunk <= nChunks; chunk++)
        {
-               tn = yaffs_FindLevel0Tnode(&in->variant.fileVariant, chunk);
+               tn = yaffs_FindLevel0Tnode(in->myDev,&in->variant.fileVariant, chunk);
     
                if(tn)
                {
     
-                       theChunk = tn->level0[chunk & YAFFS_TNODES_LEVEL0_MASK] << dev->chunkGroupBits;
+                       theChunk = tn->level0[chunk & YAFFS_TNODES_LEVEL0_MASK] << in->myDev->chunkGroupBits;
     
 
-                               yaffs_ReadChunkTagsFromNAND(theChunk,tags);
+                               yaffs_ReadChunkTagsFromNAND(in->myDev,theChunk,tags);
                                if(tags->chunkId == chunk &&
                                tags->objectId == in->objectId)
                                {
                                        // found it;
-                                       
+                               
                                }
                                else
                                {
@@ -1721,13 +1849,53 @@ static int yaffs_CheckFileSanity(yaffs_Object *in)
 
 #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;
 }
 
@@ -1782,7 +1950,7 @@ static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId)
        }
        else
        {
-               T(("Bad news deteing chunk %d\n",chunkId));
+               T(("Bad news deleting chunk %d\n",chunkId));
        }
        
 }
@@ -1851,7 +2019,7 @@ int yaffs_WriteChunkDataToObject(yaffs_Object *in,int chunkInInode, const __u8 *
        newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,buffer,&newTags,useReserve);
        if(newChunkId >= 0)
        {
-               yaffs_PutChunkIntoFile(in,chunkInInode,newChunkId);
+               yaffs_PutChunkIntoFile(in,chunkInInode,newChunkId,0);
                
                
                if(prevChunkId >= 0)
@@ -1927,7 +2095,8 @@ int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name)
        
                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;
@@ -1935,7 +2104,8 @@ int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name)
                        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);
@@ -2233,6 +2403,7 @@ int yaffs_FlushFile(yaffs_Object *in)
        int retVal;
        if(in->dirty)
        {
+               T(("flushing object header\n"));
                retVal = yaffs_UpdateObjectHeader(in,NULL);
        }
        else
@@ -2535,9 +2706,9 @@ static int yaffs_Scan(yaffs_Device *dev)
                                        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