package yaffs2.port; import yaffs2.utils.*; import yaffs2.utils.factory.PrimitiveWrapperFactory; public class yaffs_checkptrw_C { /* * 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. */ static final String yaffs_checkptrw_c_version = "$Id: yaffs_checkptrw_C.java,v 1.3 2007/07/01 01:29:50 alexander.dejaco Exp $"; // #include "yaffs_checkptrw.h" static boolean yaffs_CheckpointSpaceOk(yaffs_Device dev) { int blocksAvailable = dev.subField3.nErasedBlocks - dev.subField1.nReservedBlocks; yportenv.T(yportenv.YAFFS_TRACE_CHECKPOINT, ("checkpt blocks available = %d" + ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(blocksAvailable)); return (blocksAvailable <= 0) ? false : true; } static boolean yaffs_CheckpointErase(yaffs_Device dev) { int i; if(!(dev.subField1.eraseBlockInNAND != null)) return false; yportenv.T(yportenv.YAFFS_TRACE_CHECKPOINT,("checking blocks %d to %d"+ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(dev.subField2.internalStartBlock),PrimitiveWrapperFactory.get(dev.subField2.internalEndBlock)); for(i = dev.subField2.internalStartBlock; i <= dev.subField2.internalEndBlock; i++) { yaffs_BlockInfo bi = Guts_H.yaffs_GetBlockInfo(dev,i); if(bi.blockState() == Guts_H.YAFFS_BLOCK_STATE_CHECKPOINT){ yportenv.T(yportenv.YAFFS_TRACE_CHECKPOINT,("erasing checkpt block %d"+ydirectenv.TENDSTR),PrimitiveWrapperFactory.get(i)); if(dev.subField1.eraseBlockInNAND.eraseBlockInNAND(dev,i- dev.subField2.blockOffset /* realign */)){ bi.setBlockState(Guts_H.YAFFS_BLOCK_STATE_EMPTY); dev.subField3.nErasedBlocks++; dev.subField3.nFreeChunks += dev.subField1.nChunksPerBlock; } else { dev.subField1.markNANDBlockBad.markNANDBlockBad(dev,i); bi.setBlockState(Guts_H.YAFFS_BLOCK_STATE_DEAD); } } } dev.subField2.blocksInCheckpoint = 0; return true; } static void yaffs_CheckpointFindNextErasedBlock(yaffs_Device dev) { int i; int blocksAvailable = dev.subField3.nErasedBlocks - dev.subField1.nReservedBlocks; yportenv.T(yportenv.YAFFS_TRACE_CHECKPOINT, ("allocating checkpt block: erased %d reserved %d avail %d next %d "+ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(dev.subField3.nErasedBlocks),PrimitiveWrapperFactory.get(dev.subField1.nReservedBlocks),PrimitiveWrapperFactory.get(blocksAvailable),PrimitiveWrapperFactory.get(dev.subField2.checkpointNextBlock)); if(dev.subField2.checkpointNextBlock >= 0 && dev.subField2.checkpointNextBlock <= dev.subField2.internalEndBlock && blocksAvailable > 0){ for(i = dev.subField2.checkpointNextBlock; i <= dev.subField2.internalEndBlock; i++){ yaffs_BlockInfo bi = Guts_H.yaffs_GetBlockInfo(dev,i); if(bi.blockState() == Guts_H.YAFFS_BLOCK_STATE_EMPTY){ dev.subField2.checkpointNextBlock = i + 1; dev.subField2.checkpointCurrentBlock = i; yportenv.T(yportenv.YAFFS_TRACE_CHECKPOINT,("allocating checkpt block %d"+ydirectenv.TENDSTR),PrimitiveWrapperFactory.get(i)); return; } } } yportenv.T(yportenv.YAFFS_TRACE_CHECKPOINT,("out of checkpt blocks"+ydirectenv.TENDSTR)); dev.subField2.checkpointNextBlock = -1; dev.subField2.checkpointCurrentBlock = -1; } static void yaffs_CheckpointFindNextCheckpointBlock(yaffs_Device dev) { int i; yaffs_ExtendedTags tags = new yaffs_ExtendedTags(); yportenv.T(yportenv.YAFFS_TRACE_CHECKPOINT,("find next checkpt block: start: blocks %d next %d" + ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(dev.subField2.blocksInCheckpoint), PrimitiveWrapperFactory.get(dev.subField2.checkpointNextBlock)); if(dev.subField2.blocksInCheckpoint < dev.subField2.checkpointMaxBlocks) for(i = dev.subField2.checkpointNextBlock; i <= dev.subField2.internalEndBlock; i++){ int chunk = i * dev.subField1.nChunksPerBlock; int realignedChunk = chunk - dev.subField2.chunkOffset; dev.subField1.readChunkWithTagsFromNAND.readChunkWithTagsFromNAND(dev,realignedChunk,null,0,tags); yportenv.T(yportenv.YAFFS_TRACE_CHECKPOINT,("find next checkpt block: search: block %d oid %d seq %d eccr %d" + ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(i), PrimitiveWrapperFactory.get(tags.objectId),PrimitiveWrapperFactory.get(tags.sequenceNumber),PrimitiveWrapperFactory.get(tags.eccResult)); if(tags.sequenceNumber == Guts_H.YAFFS_SEQUENCE_CHECKPOINT_DATA){ /* Right kind of block */ dev.subField2.checkpointNextBlock = tags.objectId; dev.subField2.checkpointCurrentBlock = i; dev.subField2.checkpointBlockList[dev.subField2.blocksInCheckpoint] = i; dev.subField2.blocksInCheckpoint++; yportenv.T(yportenv.YAFFS_TRACE_CHECKPOINT,("found checkpt block %d"+ydirectenv.TENDSTR),PrimitiveWrapperFactory.get(i)); return; } } yportenv.T(yportenv.YAFFS_TRACE_CHECKPOINT,("found no more checkpt blocks"+ydirectenv.TENDSTR)); dev.subField2.checkpointNextBlock = -1; dev.subField2.checkpointCurrentBlock = -1; } static boolean yaffs_CheckpointOpen(yaffs_Device dev, boolean forWriting) { /* Got the functions we need? */ if (!(dev.subField1.writeChunkWithTagsToNAND != null) || !(dev.subField1.readChunkWithTagsFromNAND != null) || !(dev.subField1.eraseBlockInNAND != null) || !(dev.subField1.markNANDBlockBad != null)) return false; if(forWriting && !yaffs_CheckpointSpaceOk(dev)) return false; if(!(dev.subField2.checkpointBuffer != null)) { dev.subField2.checkpointBuffer = ydirectenv.YMALLOC_DMA(dev.subField1.nDataBytesPerChunk); dev.subField2.checkpointBufferIndex = 0; } if(!(dev.subField2.checkpointBuffer != null)) return false; dev.subField2.checkpointPageSequence = 0; dev.subField2.checkpointOpenForWrite = forWriting; dev.subField2.checkpointByteCount = 0; dev.subField2.checkpointCurrentBlock = -1; dev.subField2.checkpointCurrentChunk = -1; dev.subField2.checkpointNextBlock = dev.subField2.internalStartBlock; /* Erase all the blocks in the checkpoint area */ if(forWriting){ Unix.memset(dev.subField2.checkpointBuffer,dev.subField2.checkpointBufferIndex,(byte)0,dev.subField1.nDataBytesPerChunk); dev.subField2.checkpointByteOffset = 0; return yaffs_CheckpointErase(dev); } else { int i; /* Set to a value that will kick off a read */ dev.subField2.checkpointByteOffset = dev.subField1.nDataBytesPerChunk; /* A checkpoint block list of 1 checkpoint block per 16 block is (hopefully) * going to be way more than we need */ dev.subField2.blocksInCheckpoint = 0; dev.subField2.checkpointMaxBlocks = (dev.subField2.internalEndBlock - dev.subField2.internalStartBlock)/16 + 2; dev.subField2.checkpointBlockList = ydirectenv.YMALLOC_INT(/*sizeof(int) **/ dev.subField2.checkpointMaxBlocks); for(i = 0; i < dev.subField2.checkpointMaxBlocks; i++) dev.subField2.checkpointBlockList[i] = -1; } return true; } static boolean yaffs_CheckpointFlushBuffer(yaffs_Device dev) { int chunk; int realignedChunk; yaffs_ExtendedTags tags = new yaffs_ExtendedTags(); if(dev.subField2.checkpointCurrentBlock < 0){ yaffs_CheckpointFindNextErasedBlock(dev); dev.subField2.checkpointCurrentChunk = 0; } if(dev.subField2.checkpointCurrentBlock < 0) return false; tags.chunkDeleted = false; tags.objectId = dev.subField2.checkpointNextBlock; /* Hint to next place to look */ tags.chunkId = dev.subField2.checkpointPageSequence + 1; tags.sequenceNumber = Guts_H.YAFFS_SEQUENCE_CHECKPOINT_DATA; tags.byteCount = dev.subField1.nDataBytesPerChunk; if(dev.subField2.checkpointCurrentChunk == 0){ /* First chunk we write for the block? Set block state to checkpoint */ yaffs_BlockInfo bi = Guts_H.yaffs_GetBlockInfo(dev,dev.subField2.checkpointCurrentBlock); bi.setBlockState(Guts_H.YAFFS_BLOCK_STATE_CHECKPOINT); dev.subField2.blocksInCheckpoint++; } chunk = dev.subField2.checkpointCurrentBlock * dev.subField1.nChunksPerBlock + dev.subField2.checkpointCurrentChunk; yportenv.T(yportenv.YAFFS_TRACE_CHECKPOINT,("checkpoint wite buffer nand %d(%d:%d) objid %d chId %d" + ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(chunk), PrimitiveWrapperFactory.get(dev.subField2.checkpointCurrentBlock), PrimitiveWrapperFactory.get(dev.subField2.checkpointCurrentChunk),PrimitiveWrapperFactory.get(tags.objectId),PrimitiveWrapperFactory.get(tags.chunkId),null,null,null,null); realignedChunk = chunk - dev.subField2.chunkOffset; dev.subField1.writeChunkWithTagsToNAND.writeChunkWithTagsToNAND(dev,realignedChunk,dev.subField2.checkpointBuffer, dev.subField2.checkpointBufferIndex,tags); dev.subField2.checkpointByteOffset = 0; dev.subField2.checkpointPageSequence++; dev.subField2.checkpointCurrentChunk++; if(dev.subField2.checkpointCurrentChunk >= dev.subField1.nChunksPerBlock){ dev.subField2.checkpointCurrentChunk = 0; dev.subField2.checkpointCurrentBlock = -1; } Unix.memset(dev.subField2.checkpointBuffer,dev.subField2.checkpointBufferIndex,(byte)0, dev.subField1.nDataBytesPerChunk); return true; } static int yaffs_CheckpointWrite(yaffs_Device dev,/**const void **/ byte[] data, int dataIndex, int nBytes) { int i=0; boolean ok = true; /**__u8 * */ byte[] dataBytes = /**(__u8 *)*/ (data); int dataBytesIndex = dataIndex; if(!(dev.subField2.checkpointBuffer != null)) return 0; while(i < nBytes && ok) { dev.subField2.checkpointBuffer[dev.subField2.checkpointBufferIndex+dev.subField2.checkpointByteOffset] = dataBytes[dataBytesIndex]; dev.subField2.checkpointByteOffset++; i++; dataBytesIndex++; dev.subField2.checkpointByteCount++; if(dev.subField2.checkpointByteOffset < 0 || dev.subField2.checkpointByteOffset >= dev.subField1.nDataBytesPerChunk) ok = yaffs_CheckpointFlushBuffer(dev); } return i; } static int yaffs_CheckpointRead(yaffs_Device dev, /**void **/ byte[] data, int dataIndex, int nBytes) { int i=0; boolean ok = true; yaffs_ExtendedTags tags = new yaffs_ExtendedTags(); int chunk; int realignedChunk; /**__u8 **/ byte[] dataBytes = /**(__u8 *)*/ data; int dataBytesIndex = dataIndex; if(!(dev.subField2.checkpointBuffer != null)) return 0; while(i < nBytes && ok) { if(dev.subField2.checkpointByteOffset < 0 || dev.subField2.checkpointByteOffset >= dev.subField1.nDataBytesPerChunk) { if(dev.subField2.checkpointCurrentBlock < 0){ yaffs_CheckpointFindNextCheckpointBlock(dev); dev.subField2.checkpointCurrentChunk = 0; } if(dev.subField2.checkpointCurrentBlock < 0) ok = false; else { chunk = dev.subField2.checkpointCurrentBlock * dev.subField1.nChunksPerBlock + dev.subField2.checkpointCurrentChunk; realignedChunk = chunk - dev.subField2.chunkOffset; /* read in the next chunk */ /* printf("read checkpoint page %d\n",dev.checkpointPage); */ dev.subField1.readChunkWithTagsFromNAND.readChunkWithTagsFromNAND(dev, realignedChunk, dev.subField2.checkpointBuffer, dev.subField2.checkpointBufferIndex, tags); if(tags.chunkId != (dev.subField2.checkpointPageSequence + 1) || tags.sequenceNumber != Guts_H.YAFFS_SEQUENCE_CHECKPOINT_DATA) ok = false; dev.subField2.checkpointByteOffset = 0; dev.subField2.checkpointPageSequence++; dev.subField2.checkpointCurrentChunk++; if(dev.subField2.checkpointCurrentChunk >= dev.subField1.nChunksPerBlock) dev.subField2.checkpointCurrentBlock = -1; } } if(ok){ dataBytes[dataBytesIndex] = dev.subField2.checkpointBuffer[dev.subField2.checkpointBufferIndex+dev.subField2.checkpointByteOffset]; dev.subField2.checkpointByteOffset++; i++; dataBytesIndex++; dev.subField2.checkpointByteCount++; } } return i; } static boolean yaffs_CheckpointClose(yaffs_Device dev) { if(dev.subField2.checkpointOpenForWrite){ if(dev.subField2.checkpointByteOffset != 0) yaffs_CheckpointFlushBuffer(dev); } else { int i; for(i = 0; i < dev.subField2.blocksInCheckpoint && dev.subField2.checkpointBlockList[i] >= 0; i++){ yaffs_BlockInfo bi = Guts_H.yaffs_GetBlockInfo(dev,dev.subField2.checkpointBlockList[i]); if(bi.blockState() == Guts_H.YAFFS_BLOCK_STATE_EMPTY) bi.setBlockState(Guts_H.YAFFS_BLOCK_STATE_CHECKPOINT); else { // Todo this looks odd... } } ydirectenv.YFREE(dev.subField2.checkpointBlockList); dev.subField2.checkpointBlockList = null; } dev.subField3.nFreeChunks -= dev.subField2.blocksInCheckpoint * dev.subField1.nChunksPerBlock; dev.subField3.nErasedBlocks -= dev.subField2.blocksInCheckpoint; yportenv.T(yportenv.YAFFS_TRACE_CHECKPOINT,("checkpoint byte count %d" + ydirectenv.TENDSTR), PrimitiveWrapperFactory.get(dev.subField2.checkpointByteCount)); if(dev.subField2.checkpointBuffer != null){ /* free the buffer */ ydirectenv.YFREE(dev.subField2.checkpointBuffer); dev.subField2.checkpointBuffer = null; return true; } else return false; } static boolean yaffs_CheckpointInvalidateStream(yaffs_Device dev) { /* Erase the first checksum block */ yportenv.T(yportenv.YAFFS_TRACE_CHECKPOINT,(("checkpoint invalidate" + ydirectenv.TENDSTR))); if(!yaffs_CheckpointSpaceOk(dev)) return false; return yaffs_CheckpointErase(dev); } }