| /* | 
 |  * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. | 
 |  * | 
 |  * Copyright (C) 2002-2007 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. | 
 |  */ | 
 |  | 
 | /* XXX U-BOOT XXX */ | 
 | #include <common.h> | 
 | #include <malloc.h> | 
 |  | 
 | const char *yaffs_checkptrw_c_version = | 
 |     "$Id: yaffs_checkptrw.c,v 1.14 2007/05/15 20:07:40 charles Exp $"; | 
 |  | 
 |  | 
 | #include "yaffs_checkptrw.h" | 
 |  | 
 |  | 
 | static int yaffs_CheckpointSpaceOk(yaffs_Device *dev) | 
 | { | 
 |  | 
 | 	int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks; | 
 |  | 
 | 	T(YAFFS_TRACE_CHECKPOINT, | 
 | 		(TSTR("checkpt blocks available = %d" TENDSTR), | 
 | 		blocksAvailable)); | 
 |  | 
 |  | 
 | 	return (blocksAvailable <= 0) ? 0 : 1; | 
 | } | 
 |  | 
 |  | 
 | static int yaffs_CheckpointErase(yaffs_Device *dev) | 
 | { | 
 |  | 
 | 	int i; | 
 |  | 
 |  | 
 | 	if(!dev->eraseBlockInNAND) | 
 | 		return 0; | 
 | 	T(YAFFS_TRACE_CHECKPOINT,(TSTR("checking blocks %d to %d"TENDSTR), | 
 | 		dev->internalStartBlock,dev->internalEndBlock)); | 
 |  | 
 | 	for(i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) { | 
 | 		yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i); | 
 | 		if(bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT){ | 
 | 			T(YAFFS_TRACE_CHECKPOINT,(TSTR("erasing checkpt block %d"TENDSTR),i)); | 
 | 			if(dev->eraseBlockInNAND(dev,i- dev->blockOffset /* realign */)){ | 
 | 				bi->blockState = YAFFS_BLOCK_STATE_EMPTY; | 
 | 				dev->nErasedBlocks++; | 
 | 				dev->nFreeChunks += dev->nChunksPerBlock; | 
 | 			} | 
 | 			else { | 
 | 				dev->markNANDBlockBad(dev,i); | 
 | 				bi->blockState = YAFFS_BLOCK_STATE_DEAD; | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	dev->blocksInCheckpoint = 0; | 
 |  | 
 | 	return 1; | 
 | } | 
 |  | 
 |  | 
 | static void yaffs_CheckpointFindNextErasedBlock(yaffs_Device *dev) | 
 | { | 
 | 	int  i; | 
 | 	int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks; | 
 | 	T(YAFFS_TRACE_CHECKPOINT, | 
 | 		(TSTR("allocating checkpt block: erased %d reserved %d avail %d next %d "TENDSTR), | 
 | 		dev->nErasedBlocks,dev->nReservedBlocks,blocksAvailable,dev->checkpointNextBlock)); | 
 |  | 
 | 	if(dev->checkpointNextBlock >= 0 && | 
 | 	   dev->checkpointNextBlock <= dev->internalEndBlock && | 
 | 	   blocksAvailable > 0){ | 
 |  | 
 | 		for(i = dev->checkpointNextBlock; i <= dev->internalEndBlock; i++){ | 
 | 			yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i); | 
 | 			if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY){ | 
 | 				dev->checkpointNextBlock = i + 1; | 
 | 				dev->checkpointCurrentBlock = i; | 
 | 				T(YAFFS_TRACE_CHECKPOINT,(TSTR("allocating checkpt block %d"TENDSTR),i)); | 
 | 				return; | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	T(YAFFS_TRACE_CHECKPOINT,(TSTR("out of checkpt blocks"TENDSTR))); | 
 |  | 
 | 	dev->checkpointNextBlock = -1; | 
 | 	dev->checkpointCurrentBlock = -1; | 
 | } | 
 |  | 
 | static void yaffs_CheckpointFindNextCheckpointBlock(yaffs_Device *dev) | 
 | { | 
 | 	int  i; | 
 | 	yaffs_ExtendedTags tags; | 
 |  | 
 | 	T(YAFFS_TRACE_CHECKPOINT,(TSTR("find next checkpt block: start:  blocks %d next %d" TENDSTR), | 
 | 		dev->blocksInCheckpoint, dev->checkpointNextBlock)); | 
 |  | 
 | 	if(dev->blocksInCheckpoint < dev->checkpointMaxBlocks) | 
 | 		for(i = dev->checkpointNextBlock; i <= dev->internalEndBlock; i++){ | 
 | 			int chunk = i * dev->nChunksPerBlock; | 
 | 			int realignedChunk = chunk - dev->chunkOffset; | 
 |  | 
 | 			dev->readChunkWithTagsFromNAND(dev,realignedChunk,NULL,&tags); | 
 | 			T(YAFFS_TRACE_CHECKPOINT,(TSTR("find next checkpt block: search: block %d oid %d seq %d eccr %d" TENDSTR), | 
 | 				i, tags.objectId,tags.sequenceNumber,tags.eccResult)); | 
 |  | 
 | 			if(tags.sequenceNumber == YAFFS_SEQUENCE_CHECKPOINT_DATA){ | 
 | 				/* Right kind of block */ | 
 | 				dev->checkpointNextBlock = tags.objectId; | 
 | 				dev->checkpointCurrentBlock = i; | 
 | 				dev->checkpointBlockList[dev->blocksInCheckpoint] = i; | 
 | 				dev->blocksInCheckpoint++; | 
 | 				T(YAFFS_TRACE_CHECKPOINT,(TSTR("found checkpt block %d"TENDSTR),i)); | 
 | 				return; | 
 | 			} | 
 | 		} | 
 |  | 
 | 	T(YAFFS_TRACE_CHECKPOINT,(TSTR("found no more checkpt blocks"TENDSTR))); | 
 |  | 
 | 	dev->checkpointNextBlock = -1; | 
 | 	dev->checkpointCurrentBlock = -1; | 
 | } | 
 |  | 
 |  | 
 | int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting) | 
 | { | 
 |  | 
 | 	/* Got the functions we need? */ | 
 | 	if (!dev->writeChunkWithTagsToNAND || | 
 | 	    !dev->readChunkWithTagsFromNAND || | 
 | 	    !dev->eraseBlockInNAND || | 
 | 	    !dev->markNANDBlockBad) | 
 | 		return 0; | 
 |  | 
 | 	if(forWriting && !yaffs_CheckpointSpaceOk(dev)) | 
 | 		return 0; | 
 |  | 
 | 	if(!dev->checkpointBuffer) | 
 | 		dev->checkpointBuffer = YMALLOC_DMA(dev->nDataBytesPerChunk); | 
 | 	if(!dev->checkpointBuffer) | 
 | 		return 0; | 
 |  | 
 |  | 
 | 	dev->checkpointPageSequence = 0; | 
 |  | 
 | 	dev->checkpointOpenForWrite = forWriting; | 
 |  | 
 | 	dev->checkpointByteCount = 0; | 
 | 	dev->checkpointSum = 0; | 
 | 	dev->checkpointXor = 0; | 
 | 	dev->checkpointCurrentBlock = -1; | 
 | 	dev->checkpointCurrentChunk = -1; | 
 | 	dev->checkpointNextBlock = dev->internalStartBlock; | 
 |  | 
 | 	/* Erase all the blocks in the checkpoint area */ | 
 | 	if(forWriting){ | 
 | 		memset(dev->checkpointBuffer,0,dev->nDataBytesPerChunk); | 
 | 		dev->checkpointByteOffset = 0; | 
 | 		return yaffs_CheckpointErase(dev); | 
 |  | 
 |  | 
 | 	} else { | 
 | 		int i; | 
 | 		/* Set to a value that will kick off a read */ | 
 | 		dev->checkpointByteOffset = dev->nDataBytesPerChunk; | 
 | 		/* A checkpoint block list of 1 checkpoint block per 16 block is (hopefully) | 
 | 		 * going to be way more than we need */ | 
 | 		dev->blocksInCheckpoint = 0; | 
 | 		dev->checkpointMaxBlocks = (dev->internalEndBlock - dev->internalStartBlock)/16 + 2; | 
 | 		dev->checkpointBlockList = YMALLOC(sizeof(int) * dev->checkpointMaxBlocks); | 
 | 		for(i = 0; i < dev->checkpointMaxBlocks; i++) | 
 | 			dev->checkpointBlockList[i] = -1; | 
 | 	} | 
 |  | 
 | 	return 1; | 
 | } | 
 |  | 
 | int yaffs_GetCheckpointSum(yaffs_Device *dev, __u32 *sum) | 
 | { | 
 | 	__u32 compositeSum; | 
 | 	compositeSum =  (dev->checkpointSum << 8) | (dev->checkpointXor & 0xFF); | 
 | 	*sum = compositeSum; | 
 | 	return 1; | 
 | } | 
 |  | 
 | static int yaffs_CheckpointFlushBuffer(yaffs_Device *dev) | 
 | { | 
 |  | 
 | 	int chunk; | 
 | 	int realignedChunk; | 
 |  | 
 | 	yaffs_ExtendedTags tags; | 
 |  | 
 | 	if(dev->checkpointCurrentBlock < 0){ | 
 | 		yaffs_CheckpointFindNextErasedBlock(dev); | 
 | 		dev->checkpointCurrentChunk = 0; | 
 | 	} | 
 |  | 
 | 	if(dev->checkpointCurrentBlock < 0) | 
 | 		return 0; | 
 |  | 
 | 	tags.chunkDeleted = 0; | 
 | 	tags.objectId = dev->checkpointNextBlock; /* Hint to next place to look */ | 
 | 	tags.chunkId = dev->checkpointPageSequence + 1; | 
 | 	tags.sequenceNumber =  YAFFS_SEQUENCE_CHECKPOINT_DATA; | 
 | 	tags.byteCount = dev->nDataBytesPerChunk; | 
 | 	if(dev->checkpointCurrentChunk == 0){ | 
 | 		/* First chunk we write for the block? Set block state to | 
 | 		   checkpoint */ | 
 | 		yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,dev->checkpointCurrentBlock); | 
 | 		bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT; | 
 | 		dev->blocksInCheckpoint++; | 
 | 	} | 
 |  | 
 | 	chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + dev->checkpointCurrentChunk; | 
 |  | 
 |  | 
 | 	T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint wite buffer nand %d(%d:%d) objid %d chId %d" TENDSTR), | 
 | 		chunk, dev->checkpointCurrentBlock, dev->checkpointCurrentChunk,tags.objectId,tags.chunkId)); | 
 |  | 
 | 	realignedChunk = chunk - dev->chunkOffset; | 
 |  | 
 | 	dev->writeChunkWithTagsToNAND(dev,realignedChunk,dev->checkpointBuffer,&tags); | 
 | 	dev->checkpointByteOffset = 0; | 
 | 	dev->checkpointPageSequence++; | 
 | 	dev->checkpointCurrentChunk++; | 
 | 	if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock){ | 
 | 		dev->checkpointCurrentChunk = 0; | 
 | 		dev->checkpointCurrentBlock = -1; | 
 | 	} | 
 | 	memset(dev->checkpointBuffer,0,dev->nDataBytesPerChunk); | 
 |  | 
 | 	return 1; | 
 | } | 
 |  | 
 |  | 
 | int yaffs_CheckpointWrite(yaffs_Device *dev,const void *data, int nBytes) | 
 | { | 
 | 	int i=0; | 
 | 	int ok = 1; | 
 |  | 
 |  | 
 | 	__u8 * dataBytes = (__u8 *)data; | 
 |  | 
 |  | 
 |  | 
 | 	if(!dev->checkpointBuffer) | 
 | 		return 0; | 
 |  | 
 | 	if(!dev->checkpointOpenForWrite) | 
 | 		return -1; | 
 |  | 
 | 	while(i < nBytes && ok) { | 
 |  | 
 |  | 
 |  | 
 | 		dev->checkpointBuffer[dev->checkpointByteOffset] = *dataBytes ; | 
 | 		dev->checkpointSum += *dataBytes; | 
 | 		dev->checkpointXor ^= *dataBytes; | 
 |  | 
 | 		dev->checkpointByteOffset++; | 
 | 		i++; | 
 | 		dataBytes++; | 
 | 		dev->checkpointByteCount++; | 
 |  | 
 |  | 
 | 		if(dev->checkpointByteOffset < 0 || | 
 | 		   dev->checkpointByteOffset >= dev->nDataBytesPerChunk) | 
 | 			ok = yaffs_CheckpointFlushBuffer(dev); | 
 |  | 
 | 	} | 
 |  | 
 | 	return 	i; | 
 | } | 
 |  | 
 | int yaffs_CheckpointRead(yaffs_Device *dev, void *data, int nBytes) | 
 | { | 
 | 	int i=0; | 
 | 	int ok = 1; | 
 | 	yaffs_ExtendedTags tags; | 
 |  | 
 |  | 
 | 	int chunk; | 
 | 	int realignedChunk; | 
 |  | 
 | 	__u8 *dataBytes = (__u8 *)data; | 
 |  | 
 | 	if(!dev->checkpointBuffer) | 
 | 		return 0; | 
 |  | 
 | 	if(dev->checkpointOpenForWrite) | 
 | 		return -1; | 
 |  | 
 | 	while(i < nBytes && ok) { | 
 |  | 
 |  | 
 | 		if(dev->checkpointByteOffset < 0 || | 
 | 		   dev->checkpointByteOffset >= dev->nDataBytesPerChunk) { | 
 |  | 
 | 		   	if(dev->checkpointCurrentBlock < 0){ | 
 | 				yaffs_CheckpointFindNextCheckpointBlock(dev); | 
 | 				dev->checkpointCurrentChunk = 0; | 
 | 			} | 
 |  | 
 | 			if(dev->checkpointCurrentBlock < 0) | 
 | 				ok = 0; | 
 | 			else { | 
 |  | 
 | 				chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + | 
 | 				          dev->checkpointCurrentChunk; | 
 |  | 
 | 				realignedChunk = chunk - dev->chunkOffset; | 
 |  | 
 | 	   			/* read in the next chunk */ | 
 | 	   			/* printf("read checkpoint page %d\n",dev->checkpointPage); */ | 
 | 				dev->readChunkWithTagsFromNAND(dev, realignedChunk, | 
 | 							       dev->checkpointBuffer, | 
 | 							      &tags); | 
 |  | 
 | 				if(tags.chunkId != (dev->checkpointPageSequence + 1) || | 
 | 				   tags.sequenceNumber != YAFFS_SEQUENCE_CHECKPOINT_DATA) | 
 | 				   ok = 0; | 
 |  | 
 | 				dev->checkpointByteOffset = 0; | 
 | 				dev->checkpointPageSequence++; | 
 | 				dev->checkpointCurrentChunk++; | 
 |  | 
 | 				if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock) | 
 | 					dev->checkpointCurrentBlock = -1; | 
 | 			} | 
 | 		} | 
 |  | 
 | 		if(ok){ | 
 | 			*dataBytes = dev->checkpointBuffer[dev->checkpointByteOffset]; | 
 | 			dev->checkpointSum += *dataBytes; | 
 | 			dev->checkpointXor ^= *dataBytes; | 
 | 			dev->checkpointByteOffset++; | 
 | 			i++; | 
 | 			dataBytes++; | 
 | 			dev->checkpointByteCount++; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return 	i; | 
 | } | 
 |  | 
 | int yaffs_CheckpointClose(yaffs_Device *dev) | 
 | { | 
 |  | 
 | 	if(dev->checkpointOpenForWrite){ | 
 | 		if(dev->checkpointByteOffset != 0) | 
 | 			yaffs_CheckpointFlushBuffer(dev); | 
 | 	} else { | 
 | 		int i; | 
 | 		for(i = 0; i < dev->blocksInCheckpoint && dev->checkpointBlockList[i] >= 0; i++){ | 
 | 			yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,dev->checkpointBlockList[i]); | 
 | 			if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY) | 
 | 				bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT; | 
 | 			else { | 
 | 				// Todo this looks odd... | 
 | 			} | 
 | 		} | 
 | 		YFREE(dev->checkpointBlockList); | 
 | 		dev->checkpointBlockList = NULL; | 
 | 	} | 
 |  | 
 | 	dev->nFreeChunks -= dev->blocksInCheckpoint * dev->nChunksPerBlock; | 
 | 	dev->nErasedBlocks -= dev->blocksInCheckpoint; | 
 |  | 
 |  | 
 | 	T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint byte count %d" TENDSTR), | 
 | 			dev->checkpointByteCount)); | 
 |  | 
 | 	if(dev->checkpointBuffer){ | 
 | 		/* free the buffer */ | 
 | 		YFREE(dev->checkpointBuffer); | 
 | 		dev->checkpointBuffer = NULL; | 
 | 		return 1; | 
 | 	} | 
 | 	else | 
 | 		return 0; | 
 |  | 
 | } | 
 |  | 
 | int yaffs_CheckpointInvalidateStream(yaffs_Device *dev) | 
 | { | 
 | 	/* Erase the first checksum block */ | 
 |  | 
 | 	T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint invalidate"TENDSTR))); | 
 |  | 
 | 	if(!yaffs_CheckpointSpaceOk(dev)) | 
 | 		return 0; | 
 |  | 
 | 	return yaffs_CheckpointErase(dev); | 
 | } |