package yaffs2.port.emulation; import java.io.RandomAccessFile; import yaffs2.port.Guts_H; import yaffs2.port.yaffs_Device; import yaffs2.port.yaffs_ExtendedTags; import yaffs2.port.yaffs_PackedTags2; import yaffs2.port.yaffs_packedtags2_C; import yaffs2.port.yaffs_tagsvalidity_C; import yaffs2.port.yaffsfs_H; import yaffs2.port.ydirectenv; import yaffs2.port.yportenv; import yaffs2.port.yaffs_Device.eraseBlockInNANDInterface; import yaffs2.port.yaffs_Device.initialiseNANDInterface; import yaffs2.port.yaffs_Device.markNANDBlockBadInterface; import yaffs2.port.yaffs_Device.queryNANDBlockInterface; import yaffs2.port.yaffs_Device.readChunkWithTagsFromNANDInterface; import yaffs2.port.yaffs_Device.writeChunkWithTagsToNANDInterface; import yaffs2.utils.*; import yaffs2.utils.factory.PrimitiveWrapperFactory; import yaffs2.utils.emulation.*; public class yaffs_fileem2k_C implements readChunkWithTagsFromNANDInterface, markNANDBlockBadInterface, eraseBlockInNANDInterface, initialiseNANDInterface, queryNANDBlockInterface, writeChunkWithTagsToNANDInterface { // PORT public static final yaffs_fileem2k_C instance = new yaffs_fileem2k_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. */ /* * This provides a YAFFS nand emulation on a file for emulating 2kB pages. * This is only intended as test code to test persistence etc. */ static final String yaffs_flashif_c_version = "$Id: yaffs_fileem2k_C.java,v 1.1 2007/09/24 13:31:33 peter.hilber Exp $"; // #include "yportenv.h" // // #include "yaffs_flashif.h" // #include "yaffs_guts.h" // #include "devextras.h" // // #include <sys/types.h> // #include <sys/stat.h> // #include <fcntl.h> // #include <unistd.h> // // #include "yaffs_fileem2k.h" // #include "yaffs_packedtags2.h" static final boolean SIMULATE_FAILURES = false; // typedef struct // { // __u8 data[PAGE_SIZE]; // Data + spare // } yflash_Page; // typedef struct // { // yflash_Page page[PAGES_PER_BLOCK]; // The pages in the block // // } yflash_Block; static final int MAX_HANDLES = 20; static final int BLOCKS_PER_HANDLE = 64*16; // XXX why * 16? // typedef struct // { // int handle[MAX_HANDLES]; // int nBlocks; // } yflash_Device; static yflash_Device filedisk = new yflash_Device(); static boolean yaffs_testPartialWrite = false; static /*__u8*/ byte[] localBuffer = new byte[yaffs_fileem2k_H.PAGE_SIZE]; static final int localBufferIndex = 0; static /*char * */ String NToName(/*char *buf,*/int n) { return "emfile" + n; } static /*char*/ byte[] dummyBuffer = new byte[yaffs_fileem2k_H.BLOCK_SIZE]; static final int dummyBufferSize = dummyBuffer.length; static final int dummyBufferIndex = 0; static RandomAccessFile GetBlockFileHandle(int n) { RandomAccessFile h; int requiredSize; /*char*/ String name = NToName(n); int fSize; int i; h = FileEmulationUnix.open(name, yaffsfs_H.O_RDWR | yaffsfs_H.O_CREAT, yaffsfs_H.S_IREAD | yaffsfs_H.S_IWRITE); if(h != null){ // FIXME rewriting every program launch // fSize = lseek(h,0,SEEK_END); // requiredSize = BLOCKS_PER_HANDLE * BLOCK_SIZE; // if(fSize < requiredSize){ for(i = 0; i < BLOCKS_PER_HANDLE; i++) if(FileEmulationUnix.write(h,dummyBuffer,dummyBufferIndex,yaffs_fileem2k_H.BLOCK_SIZE) != yaffs_fileem2k_H.BLOCK_SIZE) return null; // } } return h; } static boolean _STATIC_LOCAL_CheckInit_initialised = false; static boolean CheckInit() { // static boolean initialised = false; int h; int i; /*off_t*/ int fSize; /*off_t*/ int requiredSize; int written; int blk; yflash_Page p = new yflash_Page(); if(_STATIC_LOCAL_CheckInit_initialised) { return Guts_H.YAFFS_OK; } _STATIC_LOCAL_CheckInit_initialised = true; Unix.memset(dummyBuffer,dummyBufferIndex,(byte)0xff,dummyBufferSize); filedisk.nBlocks = yaffs_fileem2k_H.SIZE_IN_MB * yaffs_fileem2k_H.BLOCKS_PER_MB; for(i = 0; i < MAX_HANDLES; i++) filedisk.handle[i] = null; for(i = 0,blk = 0; blk < filedisk.nBlocks; blk+=BLOCKS_PER_HANDLE,i++) filedisk.handle[i] = GetBlockFileHandle(i); return true; } static int yflash_GetNumberOfBlocks() { CheckInit(); return filedisk.nBlocks; } public boolean /*yflash_*/writeChunkWithTagsToNAND(yaffs_Device dev,int chunkInNAND, /*const __u8 **/ byte[] data, int dataIndex, yaffs_ExtendedTags tags) { int written; int pos; RandomAccessFile h; int i; int nRead; int error; yportenv.T(yportenv.YAFFS_TRACE_MTD,("write chunk %d data %x tags %x"+ydirectenv.TENDSTR),PrimitiveWrapperFactory.get(chunkInNAND),/*(unsigned)*/PrimitiveWrapperFactory.get(yaffs2.utils.Utils.hashCode(data)), /*(unsigned)*/PrimitiveWrapperFactory.get(yaffs2.utils.Utils.hashCode(tags))); CheckInit(); if(data != null) { pos = (chunkInNAND % (yaffs_fileem2k_H.PAGES_PER_BLOCK * BLOCKS_PER_HANDLE)) * yaffs_fileem2k_H.PAGE_SIZE; h = filedisk.handle[(chunkInNAND / (yaffs_fileem2k_H.PAGES_PER_BLOCK * BLOCKS_PER_HANDLE))]; FileEmulationUnix.lseek(h,pos,yaffsfs_H.SEEK_SET); nRead = FileEmulationUnix.read(h, localBuffer,localBufferIndex, dev.subField1.nDataBytesPerChunk); for(i = error = 0; i < dev.subField1.nDataBytesPerChunk && !(error != 0); i++){ if(yaffs2.utils.Utils.byteAsUnsignedByte(localBuffer[localBufferIndex+i]) != 0xFF){ //FIXME yportenv.T(yportenv.PORT_TRACE_NANDSIM, "nand simulation: chunk %d data byte %d was %02x\n", PrimitiveWrapperFactory.get(chunkInNAND),PrimitiveWrapperFactory.get(i),PrimitiveWrapperFactory.get(yaffs2.utils.Utils.byteAsUnsignedByte(localBuffer[localBufferIndex+i]))); // printf("nand simulation: chunk %d data byte %d was %02x\n", // PORT I think %0x2 is a typo. // chunkInNAND,i,byteAsUnsignedByte(localBuffer[localBufferIndex+i])); error = 1; } } for(i = 0; i < dev.subField1.nDataBytesPerChunk; i++) localBuffer[localBufferIndex+i] &= data[dataIndex+i]; if(Unix.memcmp(localBuffer,localBufferIndex,data,dataIndex,dev.subField1.nDataBytesPerChunk) != 0) Unix.printf("nand simulator: data does not match\n"); FileEmulationUnix.lseek(h,pos,yaffsfs_H.SEEK_SET); written = FileEmulationUnix.write(h,localBuffer,localBufferIndex,dev.subField1.nDataBytesPerChunk); // TODO FIXME if ((yportenv.PORT_TRACE_CHECKSUMS & yaffs2.utils.Globals.yaffs_traceMask) != 0) CheckSum.checksumOfBytes(localBuffer,localBufferIndex,dev.subField1.nDataBytesPerChunk); if(yaffs_testPartialWrite){ FileEmulationUnix.close(h); System.exit(1); } if (SIMULATE_FAILURES) { if((chunkInNAND >> 6) == 100) written = 0; if((chunkInNAND >> 6) == 110) written = 0; } // #endif if(written != dev.subField1.nDataBytesPerChunk) return Guts_H.YAFFS_FAIL; } if(tags != null) { pos = (chunkInNAND % (yaffs_fileem2k_H.PAGES_PER_BLOCK * BLOCKS_PER_HANDLE)) * yaffs_fileem2k_H.PAGE_SIZE + yaffs_fileem2k_H.PAGE_DATA_SIZE ; h = filedisk.handle[(chunkInNAND / (yaffs_fileem2k_H.PAGES_PER_BLOCK * BLOCKS_PER_HANDLE))]; FileEmulationUnix.lseek(h,pos,yaffsfs_H.SEEK_SET); if( false && dev.subField1.isYaffs2) { byte[] tagsBuf = new byte[yaffs_ExtendedTags.SERIALIZED_LENGTH]; tags.writeTagsToByteArray(tagsBuf, 0); written = FileEmulationUnix.write(h,tagsBuf,0,yaffs_ExtendedTags.SERIALIZED_LENGTH); if(written != yaffs_ExtendedTags.SERIALIZED_LENGTH) return Guts_H.YAFFS_FAIL; } else { yaffs_PackedTags2 pt = new yaffs_PackedTags2(); yaffs_packedtags2_C.yaffs_PackTags2(pt,tags); /*__u8 **/ byte[] ptab = /*(__u8 *)&*/ pt.serialized; final int ptabIndex = pt.offset; nRead = FileEmulationUnix.read(h,localBuffer,localBufferIndex,pt.SERIALIZED_LENGTH); for(i = error = 0; i < pt.SERIALIZED_LENGTH && !(error != 0); i++){ if(yaffs2.utils.Utils.byteAsUnsignedByte(localBuffer[i]) != 0xFF){ // FIXME yportenv.T(yportenv.PORT_TRACE_NANDSIM, "nand simulation: chunk %d oob byte %d was %02x\n", PrimitiveWrapperFactory.get(chunkInNAND),PrimitiveWrapperFactory.get(i),PrimitiveWrapperFactory.get(yaffs2.utils.Utils.byteAsUnsignedByte(localBuffer[i]))); // printf("nand simulation: chunk %d oob byte %d was %02x\n", // PORT I think %0x2 is a typo. // chunkInNAND,i,byteAsUnsignedByte(localBuffer[i])); error = 1; } } for(i = 0; i < pt.SERIALIZED_LENGTH; i++) localBuffer[i] &= ptab[ptabIndex+i]; if(Unix.memcmp(localBuffer,localBufferIndex,pt.serialized,pt.offset, pt.SERIALIZED_LENGTH) != 0) // FIXME yportenv.T(yportenv.PORT_TRACE_NANDSIM, "nand sim: tags corruption\n"); // printf("nand sim: tags corruption\n"); FileEmulationUnix.lseek(h,pos,yaffsfs_H.SEEK_SET); written = FileEmulationUnix.write(h,localBuffer,localBufferIndex,pt.SERIALIZED_LENGTH); if(written != pt.SERIALIZED_LENGTH) return Guts_H.YAFFS_FAIL; } } return Guts_H.YAFFS_OK; } static boolean yaffs_CheckAllFF(/*const __u8 **/ byte[] ptr, int ptrIndex, int n) { while(n != 0) { n--; if(yaffs2.utils.Utils.byteAsUnsignedByte(ptr[ptrIndex])!=0xFF) return false; ptrIndex++; } return true; } static int fail300 = 1; static int fail320 = 1; static int failRead10 = 2; public boolean /*yflash_*/ readChunkWithTagsFromNAND(yaffs_Device dev,int chunkInNAND, /*__u8 **/ byte[] data, int dataIndex, yaffs_ExtendedTags tags) { int nread; int pos; RandomAccessFile h; yportenv.T(yportenv.YAFFS_TRACE_MTD,("read chunk %d data %x tags %x"+ydirectenv.TENDSTR),PrimitiveWrapperFactory.get(chunkInNAND),/*(unsigned)*/PrimitiveWrapperFactory.get(yaffs2.utils.Utils.hashCode(data)), /*(unsigned)*/PrimitiveWrapperFactory.get(yaffs2.utils.Utils.hashCode(tags))); CheckInit(); if(data != null) { pos = (chunkInNAND % (yaffs_fileem2k_H.PAGES_PER_BLOCK * BLOCKS_PER_HANDLE)) * yaffs_fileem2k_H.PAGE_SIZE; h = filedisk.handle[(chunkInNAND / (yaffs_fileem2k_H.PAGES_PER_BLOCK * BLOCKS_PER_HANDLE))]; FileEmulationUnix.lseek(h,pos,yaffsfs_H.SEEK_SET); nread = FileEmulationUnix.read(h,data,dataIndex,dev.subField1.nDataBytesPerChunk); if(nread != dev.subField1.nDataBytesPerChunk) return Guts_H.YAFFS_FAIL; } if(tags != null) { pos = (chunkInNAND % (yaffs_fileem2k_H.PAGES_PER_BLOCK * BLOCKS_PER_HANDLE)) * yaffs_fileem2k_H.PAGE_SIZE + yaffs_fileem2k_H.PAGE_DATA_SIZE; h = filedisk.handle[(chunkInNAND / (yaffs_fileem2k_H.PAGES_PER_BLOCK * BLOCKS_PER_HANDLE))]; FileEmulationUnix.lseek(h,pos,yaffsfs_H.SEEK_SET); if(false && dev.subField1.isYaffs2) { byte[] tagsBuf = new byte[yaffs_ExtendedTags.SERIALIZED_LENGTH]; nread= FileEmulationUnix.read(h,tagsBuf,0,yaffs_ExtendedTags.SERIALIZED_LENGTH); if(nread != yaffs_ExtendedTags.SERIALIZED_LENGTH) return Guts_H.YAFFS_FAIL; if(yaffs_CheckAllFF(/*(__u8 *)*/tagsBuf,0,yaffs_ExtendedTags.SERIALIZED_LENGTH)) { tags.readTagsFromByteArray(tagsBuf, 0); yaffs2.port.yaffs_tagsvalidity_C.yaffs_InitialiseTags(tags); } else { tags.readTagsFromByteArray(tagsBuf, 0); tags.chunkUsed = true; } } else { yaffs_PackedTags2 pt = new yaffs_PackedTags2(); nread= FileEmulationUnix.read(h,pt.serialized,pt.offset,pt.SERIALIZED_LENGTH); yaffs_packedtags2_C.yaffs_UnpackTags2(tags,pt); if (SIMULATE_FAILURES) { if((chunkInNAND >> 6) == 100) { if(fail300 != 0 && tags.eccResult == Guts_H.YAFFS_ECC_RESULT_NO_ERROR){ tags.eccResult = Guts_H.YAFFS_ECC_RESULT_FIXED; fail300 = 0; } } if((chunkInNAND >> 6) == 110) { if(fail320 != 0 && tags.eccResult == Guts_H.YAFFS_ECC_RESULT_NO_ERROR){ tags.eccResult = Guts_H.YAFFS_ECC_RESULT_FIXED; fail320 = 0; } } } // #endif if(failRead10>0 && chunkInNAND == 10){ failRead10--; nread = 0; } if(nread != pt.SERIALIZED_LENGTH) return Guts_H.YAFFS_FAIL; } } return Guts_H.YAFFS_OK; } public boolean /*yflash_*/ markNANDBlockBad(yaffs_Device dev, int blockNo) { int written; RandomAccessFile h; yaffs_PackedTags2 pt = new yaffs_PackedTags2(); CheckInit(); Unix.memset(pt,(byte)0); h = filedisk.handle[(blockNo / ( BLOCKS_PER_HANDLE))]; FileEmulationUnix.lseek(h,((blockNo % BLOCKS_PER_HANDLE) * dev.subField1.nChunksPerBlock) * yaffs_fileem2k_H.PAGE_SIZE + yaffs_fileem2k_H.PAGE_DATA_SIZE,yaffsfs_H.SEEK_SET); written = FileEmulationUnix.write(h,pt.serialized,pt.offset,pt.SERIALIZED_LENGTH); if(written != pt.SERIALIZED_LENGTH) return Guts_H.YAFFS_FAIL; return Guts_H.YAFFS_OK; } public boolean /*yflash_*/ eraseBlockInNAND(yaffs_Device dev, int blockNumber) { int i; RandomAccessFile h; CheckInit(); yportenv.T(yportenv.YAFFS_TRACE_ERASE,"erase block %d\n",PrimitiveWrapperFactory.get(blockNumber)); // printf("erase block %d\n",blockNumber); if(blockNumber == 320) fail320 = 1; if(blockNumber < 0 || blockNumber >= filedisk.nBlocks) { yportenv.T(yportenv.YAFFS_TRACE_ALWAYS,"Attempt to erase non-existant block %d\n",PrimitiveWrapperFactory.get(blockNumber)); return Guts_H.YAFFS_FAIL; } else { /*__u8*/ byte[] pg = new byte[yaffs_fileem2k_H.PAGE_SIZE]; final int pgIndex = 0; int syz = yaffs_fileem2k_H.PAGE_SIZE; int pos; Unix.memset(pg,pgIndex,(byte)0xff,syz); h = filedisk.handle[(blockNumber / ( BLOCKS_PER_HANDLE))]; FileEmulationUnix.lseek(h,((blockNumber % BLOCKS_PER_HANDLE) * dev.subField1.nChunksPerBlock) * yaffs_fileem2k_H.PAGE_SIZE,yaffsfs_H.SEEK_SET); for(i = 0; i < dev.subField1.nChunksPerBlock; i++) { FileEmulationUnix.write(h,pg,pgIndex,yaffs_fileem2k_H.PAGE_SIZE); } pos = FileEmulationUnix.lseek(h, 0,yaffsfs_H.SEEK_CUR); return Guts_H.YAFFS_OK; } } public boolean /*yflash_*/ initialiseNAND(yaffs_Device dev) { CheckInit(); return Guts_H.YAFFS_OK; } public boolean /*yflash_*/ queryNANDBlock(yaffs_Device dev, int blockNo, /*yaffs_BlockState*/ IntegerPointer state, IntegerPointer sequenceNumber) { yaffs_ExtendedTags tags = new yaffs_ExtendedTags(); int chunkNo; sequenceNumber.dereferenced = 0; chunkNo = blockNo * dev.subField1.nChunksPerBlock; /*yflash_*/ readChunkWithTagsFromNAND(dev,chunkNo,null,0,tags); if(tags.blockBad) { state.dereferenced = Guts_H.YAFFS_BLOCK_STATE_DEAD; } else if(!tags.chunkUsed) { state.dereferenced = Guts_H.YAFFS_BLOCK_STATE_EMPTY; } else if(tags.chunkUsed) { state.dereferenced = Guts_H.YAFFS_BLOCK_STATE_NEEDS_SCANNING; sequenceNumber.dereferenced = tags.sequenceNumber; } return Guts_H.YAFFS_OK; } }