*** empty log message ***
[yaffs/.git] / yaffs_guts.c
index 6bc6136e807285f2b4c692f3bf683bb2a77a24d7..66014bb3f449d95b5a94aec02ff026c17c622c0c 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);
@@ -108,7 +112,28 @@ 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;
 }
 
 int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,int blockInNAND)
@@ -127,6 +152,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 +163,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 +231,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 +1032,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
@@ -1411,7 +1506,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 +1571,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)
@@ -1721,13 +1816,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 +1917,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 +1986,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 +2062,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 +2071,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);
@@ -2535,9 +2672,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