|
|
/*
* $Log: V:/Flite/archives/TrueFFS5/Src/inftl.c_V $ * * Rev 1.40 Apr 15 2002 20:14:40 oris * Changed the use of SecondUnitStructure for INFTLTEST utility. * * Rev 1.39 Apr 15 2002 08:39:32 oris * Placed quick mount under if def of CHECK_MOUNT for INFTLTST. * * Rev 1.38 Apr 15 2002 07:37:20 oris * Improved power failure resistant: * - setUnavail macro was changed for improved code readability. * - MarkSectorAsChecking macro was changed to MarkSectorAsChecked * - Bug fix - getSectorFlags - in case invalid sector flags are found do not forget to reset TL buffer to ff's and mark it as remapped. * - Added support for VERIFY_ERASED_SECTOR compilation flag - make sure a sector is free before writing it - most of the code * is found in writeAndCheck routine. * - Added MAKE_SURE_IGNORE_HAS_BAD_EDC compilation flag - make sure that the sector is really marked * as ignored. If not mark the entire sector as 0. * - Initialize a addition 1k buffer for the TL - used for verify erase. * - Bug fix - verifySector routine had several bugs. * - Bug fix - foldUnit routine bad comparison in FL_OFF mode. * - Bug fix - foldBestChain routine missing update of sector count after folding failed and bad search for free unit. * - Bug fix - mountUnit routine had bad handling for corrupted unit header. * Added support for RAM based MTD power failure tests. * Remove cache allocation checks - They are not needed since the cache routine itself check for proper allocation. * Bug fix - prevent initialization of flash record in case flash is NULL. * Bug fix - defragment routine used to call allocateUnit instead of foldBestChain. * Bug fix - bad debug print when format routine none INFTL media. * Bug fix - missing several dismount in INFTL format. * Bug fix - format routine could not place protection onto binary partition containing only the bios driver. * Changed format debug massages. * * Rev 1.37 Feb 19 2002 21:00:22 oris * Fixed FL_READ_ONLY compilation problems. * Replaced TL_LEAVE_BINARY_AREA with FL_LEAVE_BINARY_AREA * Improved protection against power failures: * - formatUnit/getUnitTailer and setUnitTailer - Added argument to support temporary units (unit with erase mark on an invalid offset so that if not fixed before next mount they will be considered as free). * - foldUnit - Removed setUnavailable (not called only from foldBestChain) and added code to restore temporary unit into permanent ones(mark erase mark in proper place). * - foldBestChain - added folding not in place and several bug fixes. * - AllocatUnit - Change sequence to be more robust. * - checkFolding/applyWearLeveling - Bug fix - read newest unit in chain after allocate call it might change as part of the allocate process. * - deleteSector - check status of write operation. * - mountInftl - perforce space reclamation only if no free units. * * Rev 1.36 Jan 29 2002 20:09:28 oris * Removed warnings. * Buf fix - chainging protection attributes of a DiskOnChip with more then a single floor. * Bug fix - if an invalid sector flag is found in getSectorFlags routine and read operation failed, SECTOR_IGNORED should have been returned. * * Rev 1.35 Jan 28 2002 21:25:46 oris * Bug fix - discard mark write operation had bad casting causing the * mark to be written to much lower addresses. * Changed discard variable to static const. * allocateAndWriteSectors - improved going along chain algorithm to scan * chain only once. * * Rev 1.34 Jan 23 2002 23:33:20 oris * Removed CHECK_DISCARD compilation flag. * Bug fix - bad casting caused discard mark to be written to a different unit then was expected in formatUnit(). * Changed discardQuickMountInfo to mark quick mount area as discarded instead of erasing it. * Improved markAsIgnored routine. * Bug fix - Problems with insert and remove key routines. * Bug fix - write BBT for INFTL formatted DiskOnChip was not supported. * Changed DFORMAT_PRINT syntax * * Rev 1.33 Jan 21 2002 20:44:56 oris * Bug fix - Erase of quick mount information does not take block multiplication into account. * * Rev 1.32 Jan 20 2002 20:28:48 oris * Removed warnings * Changed putGetBuffer to handle FAR pointers (prototype and pointers arithmetic's). * Quick mount is now saved if either of the following occurs * - Last mount did not use quick mount information. * - A write operation was made in this mount * Bug in quickMount units size calculation (affected large DiskOnChips). * * Rev 1.31 Jan 20 2002 10:49:52 oris * Added initialization of Bnand record in mount and format. * Removed badFormat field from Bnand record. * Improoved last sector cache mechanism * Removed support for PPP=3 interleave-2 * Changed FL_MALLOC allocation calls to FL_FAR_MALLOC and changed RAM tables pointers to FAR1. * Split preMount routine into protection routine and other premount routins. * Added DOUBLE_MAX_UNIT_CHAIN instead of MAX_UNIT_CHAIN*2 * Added protection agains power failures. * - Added several modes of verify write : * - FL_UPS no protection * - FL_ON verify each and every write operation * - FL_OFF full protection with minmal performance penalty. * - Added verifyVolume API to scan the media for slower mount, but with not further performance penalty. * - default after mount is FL_OFF * - Added folding not in place. * - Added verification of last sector of the chains (in folding). * - Added discard mark before erasing. * - Changed sector flags and unit data retrival error correction logic. * - Limit foldBestChain folding tryies. * - Improoved mount logic for choosing between invalid chains. * Quick mount mechanism * - Forced quick mount as default * - Delete previous data only after first write operation. * - Remove previous quick mount info with an erase operation. * - Added remove previous quick mount info API (In preMount). * Imprroved error report mechanizm of brocken chains (should never happen): * - error on read return unused area of flash * - error on write report flGeneralFailure * - error on mount fix chains. If error on a chain that was already validated , report flGeneralFailure * Format routine * - Force quick mount (ignoring user flag) * - Bug fix - format with leave binary partition of a protected binary partition. * - Removed single floor support. * * Rev 1.30 Nov 21 2001 11:38:26 oris * Changed FL_MARK_DELETE to FL_ON. * Changed FL_WITH_VERIFY_WRITE and FL_WITHOUT_VERIFY_WRITE to FL_ON and FL_OFF. * * Rev 1.29 Nov 16 2001 00:22:22 oris * Reorganized - removed function declaration, moved debug routines to a * separated file. * Bug fix - VERIFY_WRITE logic - marking unit as unavailable was done on the * virtual unit and not the last physical unit plus the least sector count and * max chain length , where not reinitialized in foldBestChange. The result * might cause infinite loop in foldbestchain if foldUnit fails. * Bug fix - Support for DiskOnChip with last floors having less chips. * Improved progress callback routine to show current unit starting from 1 and not 0 and to indicate bad and unavailable blocks as well. * Bug fix - all routines that changed protection attributes might not use * Bug fix - formatting with LEAVE_BINARY_PARTITION when binary partition is * exactly the floor size (virtual size). and improved it for bootAreaLen not * 0 and not -1 (leave only part of the previous binary partition). * Quick mount feature - Made sure the mount operation changes quick_mount * validity even if QUICK_MOUNT_FEATURE is commented. * Support 2 unchangeable protected partitions (not only 1). * Added discard mark before erase and placed this option under ifdef (default off). * Changed isValidUnitFlags to check all fields (isValidParityResult) not just * PUN and VUN. * getUnitData - bug in the logic of using second unit data structure. * No need to reread the unit data if verify write is activated after setUnitData. * Added check in virtual2physical to make sure this the unit found is valid. * initINFTLbasic - Use dword variable to calculate blocks per unit (support 64k blocks). * Change block multiplication from MORE then 32K units (not equal) * Improved support for read only mode including mount - FL_READ_ONLY * Add runtime option of VERIFY_WRITE * Bug fix - Binary partition in the first floor used only 1001 units while in * other floors 1002 units. * * Rev 1.28 Oct 11 2001 23:54:58 oris * Bug fix - When changing protection attribute of a BDTL partition (change * key, change lock , change protection type) on a DiskOnChip with more then * a single floor, an error massage might be returned since changing * protection attributes uses the same buffer as the structure holding the * protection area to work on. * * Rev 1.27 Sep 24 2001 18:23:50 oris * Bug fix - missing break in foldBestChain - very rare case. * Removed warnings. * * Rev 1.26 Sep 16 2001 21:47:54 oris * Placed intergrity check of sector in the last unit of the virtual unit under * VERIFY_WRITE compilation flag. * Bug fix - missing big-endian conversion when using static memory allocation. * * Rev 1.25 Sep 15 2001 23:46:54 oris * Removed redundant isAvailable checks. * Make sure mount operation does not reconstruct BBT. * Bug fix - folding in wear leveling does not change NAC to 1. * Bug fix - Bad casting caused bad protection type to be returned under BIG_ENDIAN. * Changed change protection attributes routine applied on protected partition * from flWrongKey to flHWProtection. * Improved algorithm reliability with VERIFY_WRITE. Following are the changes in the algorithm: * 1) virtual2Physical - * a) added flag stating if the specific sector is not free in the last unit of the chain. * 2) foldUnit - * a) if can not copy sector to the last unit of the chain, mark unit as * unavailable and return error code. * b) If verify write is enabled check even sectors that re marked as used and * are found on the last unit of the chain. * 3) foldBestChain - * a) Ignore unavailable units * b) If folding failed start looking from the beginning (it will be marked as * unavailable by the foldunit routine). * c) when done make all unit available. * 4) Added checkFolding routine - after folding check if succeeded. If not * loop up to MAX_FOLDING_LOOP each time free a unit with foldbestchain, * append a unit to the problematic chain and try to fold it. * 5) allocateUnit - Now when there are less then 2 unit reclaim space. This is * to minimize the chance of folding within a fold operation. * 6) MountINFTL - Make sure there are at least 2 free units. * * Rev 1.24 Jul 29 2001 16:10:00 oris * eraseOrphanUnit bug was fixed ( we did not add vol.firstUnit to the unitNo). * * Rev 1.23 Jul 15 2001 20:45:12 oris * Improoved documentation. * Changed unitBaseAddress function to a macro. * Removed unneccesary if statments in applyWearLeveling(). * * Rev 1.22 Jul 13 2001 01:06:14 oris * Changed multiBufferCounter to signed allowing a better buffer management. * Changed consequtiveNumbers routine into a macro. * Reorganized the DEBUG chains routines. * Bug fix - H/W read protected partition did not report as such. * Changed swapUnits routine name to applyWearleveling. * Added basics for last sector return mechanism - * foldUnit receives an additional field. * read2Sectors returns edc error sector address and actually read sector address * Added several static prefixes for static routines. * Added edc check for media header read operation. * Bug fix - parturition size smaller then a unit was acceptable. * Added default protection for unused DPSes. * Bug fix - formatINFTL with leave binary partition flag when previous binary * partition was larger then a single floor. * Improved mount documentation. * Changed policy - do not erase unit with bad erase mark. * * Rev 1.21 Jun 17 2001 08:20:06 oris * Added NO_READ_BBT_CODE compilation flag to reduce code size. * Improoved Reliability: * 1) Try to return next sector in chain if the current one has EDC error * 2) Mount routine erases all blocks not marked with the erase mark. * * Affected routines: * 1) virtual2Physical can recive the physical address to start looking for and * not the last virtual unit of the chain. * 2) copySector ,foldUnit, mapSector, read2sectors- if EDC error accures * return try returning the next sector. * 3) foldUnit - if EDC error accures return try returning the next sector. * 4) writeMultiSecotr - improove ppp = 3 * 5) mountINFTL - erase all units not marked with the erase mark. * * Rev 1.21 Jun 17 2001 08:18:02 oris * Changed recusive include define to INFTL_H. * Added FL_BAD_ERASE_MARK definition for units without the erase mark on * mount operation. * * For the rest of the revisions see revision 1.24 in the PVCS. */
/***********************************************************************************/ /* M-Systems Confidential */ /* Copyright (C) M-Systems Flash Disk Pioneers Ltd. 1995-2001 */ /* All Rights Reserved */ /***********************************************************************************/ /* NOTICE OF M-SYSTEMS OEM */ /* SOFTWARE LICENSE AGREEMENT */ /* */ /* THE USE OF THIS SOFTWARE IS GOVERNED BY A SEPARATE LICENSE */ /* AGREEMENT BETWEEN THE OEM AND M-SYSTEMS. REFER TO THAT AGREEMENT */ /* FOR THE SPECIFIC TERMS AND CONDITIONS OF USE, */ /* OR CONTACT M-SYSTEMS FOR LICENSE ASSISTANCE: */ /* E-MAIL = [email protected] */ /***********************************************************************************/
/*************************************************/ /* T r u e F F S 5.0 S o u r c e F i l e s */ /* --------------------------------------------- */ /*************************************************/
/*****************************************************************************
* File Header * * ----------- * * Name : inftl.c * * * * Description : Implementation of INFTL flash translation layer. * * * *****************************************************************************/
/* Internal compilation flags */
/* #define CHAINS_DEBUG */ /* Prints unit chains after mount routine */ /* #define CHECK_MOUNT */ /* Print serious tl debug warnings to */ /* tl_out global file handle */ /* #define MAKE_SURE_IGNORE_HAS_BAD_EDC */ /* Make sure the ignore mark was */ /* written. If not fill sector */ /* with 0's. */ /* List of included files */
#include "inftl.h"
/* Private variables */
static Bnand vols[VOLUMES]; #ifndef FL_MALLOC
#ifdef NFTL_CACHE
static ucacheEntry socketUcache[SOCKETS][U_CACHE_SIZE]; static byte socketScache[SOCKETS][S_CACHE_SIZE]; #endif /* NFTL_CACHE */
static Sbyte socketHeap[SOCKETS][ANAND_HEAP_SIZE]; static byte multiSectorBuf[SOCKETS][SECTOR_SIZE<<1]; #else
static byte *multiSectorBuf[SOCKETS]; static Sbyte multiSectorBufCounter[SOCKETS]; #endif /* FL_MALLOC */
#ifdef NFTL_CACHE
/* translation table for Sector Flags cache */ static byte scacheTable[4] = { SECTOR_DELETED, /* 0 */ SECTOR_IGNORE, /* 1 */ SECTOR_USED, /* 2 */ SECTOR_FREE }; /* 3 */ #endif /* NFTL_CACHE */
/* Macros */ #define roundToUnits(var) ((var > 0) ? ((ANANDUnitNo)((var - 1) >> vol.unitSizeBits) + 1) : 0)
#define NextGoodUnit(addr,bbt) for(;bbt[(addr >> vol.unitSizeBits) - vol.firstQuickMountUnit]!=ANAND_UNIT_FREE;addr+=1L<<vol.unitSizeBits)
#define countOf(unitNo) (vol.physicalUnits[unitNo] & UNIT_COUNT)
#define isAvailable(unitNo) ((vol.physicalUnits[unitNo] == ANAND_UNIT_FREE) || (countOf(unitNo) <= UNIT_MAX_COUNT))
#define setUnavail(unitNo) (vol.physicalUnits[unitNo] = UNIT_UNAVAIL)
#define setUnitCount(unitNo,unitCount) { vol.physicalUnits[unitNo] &= ~UNIT_COUNT; vol.physicalUnits[unitNo] |= (ANANDPhysUnit)unitCount; }
#define isLegalUnit(unitNo) ((unitNo < vol.noOfUnits) || (unitNo == ANAND_NO_UNIT))
#define isValidSectorFlag(sectorFlag) ((sectorFlag==SECTOR_FREE)||(sectorFlag==SECTOR_USED)||(sectorFlag==SECTOR_DELETED))
#define badParityResult(parityResult) (parityResult != ALL_PARITY_BITS_OK)
#define consecutiveNumbers(first,second) ((byte)(second+1)==first)
#define unitBaseAddress(vol,unitNo) ((CardAddress)((ANANDUnitNo)unitNo+(ANANDUnitNo)vol.firstUnit) << vol.unitSizeBits)
#define distanceOf(read, expected) (onesCount((byte)(read ^ expected)))
#define MarkSectorAsChecked(addr) (scannedBlocks[((addr)>>SECTOR_SIZE_BITS) & vol.sectorsPerUnitMask] = TRUE)
#define WasSectorChecked(addr) scannedBlocks[((addr)>>SECTOR_SIZE_BITS) & vol.sectorsPerUnitMask]
/* M-Systems INFTL debug routines */ #ifndef CHECK_MOUNT
#define TL_DEBUG_PRINT(x,y,z)
#define SET_EXIT(x)
#define DID_MOUNT_FAIL 1
#endif /* CHECK_MOUNT */
#if (defined(CHECK_MOUNT) || defined (CHAINS_DEBUG))
#include "inftldbg.c"
#endif /* CHECK_MOUNT || CHAINS_DEBUG */
/*------------------------------------------------------*/ /* o n e s C o u n t */ /* */ /* counts number of bits that valued 1 in a given byte */ /*------------------------------------------------------*/
static byte onesCount(byte flag) { byte counter;
for (counter = 0; flag; flag >>= 1) if (flag & 1) counter++;
return counter; }
/*----------------------------------------------------------------------*/ /* g e t U n i t T a i l e r */ /* */ /* Get the erase record of a unit. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* unitNo : Physical unit number */ /* eraseMark : Receives the erase mark of the unit */ /* eraseCount : Receives the erase count of the unit */ /* offset : offset in unit */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /* */ /*----------------------------------------------------------------------*/
static FLStatus getUnitTailer(Bnand vol, ANANDUnitNo unitNo, word *eraseMark, dword *eraseCount, dword offset) { UnitTailer unitTailer; FLStatus status;
status = vol.flash->read(vol.flash, unitBaseAddress(vol,unitNo) + offset, &unitTailer, sizeof(UnitTailer), EXTRA);
/* Mask out any 1 -> 0 bit faults by or'ing with spare data */ *eraseMark = (word)(LE2(unitTailer.eraseMark) | LE2(unitTailer.eraseMark1)); *eraseCount = LE4(unitTailer.eraseCount); return status; }
#ifndef FL_READ_ONLY
/*----------------------------------------------------------------------*/ /* s e t U n i t T a i l e r */ /* */ /* Set the erase record of a unit. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* unitNo : Physical unit number */ /* eraseMark : Erase mark to set */ /* eraseCount : Erase count to set */ /* offset : offset in unit */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /* */ /*----------------------------------------------------------------------*/
static FLStatus setUnitTailer(Bnand vol, ANANDUnitNo unitNo, word eraseMark, dword eraseCount, dword offset) { UnitTailer unitTailer;
toLE2(unitTailer.eraseMark,eraseMark); toLE2(unitTailer.eraseMark1,eraseMark); toLE4(unitTailer.eraseCount,eraseCount);
return vol.flash->write(vol.flash, unitBaseAddress(vol,unitNo) + offset, &unitTailer, sizeof(UnitTailer), EXTRA); }
/*----------------------------------------------------------------------*/ /* m a r k U n i t B a d */ /* */ /* Mark a unit as bad in the conversion table and the bad units table. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* unitNo : Physical number of bad unit */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
static FLStatus markUnitBad(Bnand vol, ANANDUnitNo unitNo) { word eraseMark; dword eraseCount; FLStatus status;
vol.physicalUnits[unitNo] = UNIT_BAD; if(vol.freeUnits) vol.freeUnits--;
status = getUnitTailer(&vol,unitNo,&eraseMark,&eraseCount,UNIT_TAILER_OFFSET); if (status == flOK) status = setUnitTailer(&vol,unitNo,FL_BAD_ERASE_MARK,eraseCount,UNIT_TAILER_OFFSET);
#ifdef NFTL_CACHE
if (vol.ucache != NULL) /* Mark unit cache as unavaialbel */ { vol.ucache[unitNo].virtualUnitNo = 0xDEAD; vol.ucache[unitNo].prevUnitNo = 0xDEAD; } #endif /* NFTL_CACHE */
return status; }
/*----------------------------------------------------------------------*/ /* f o r m a t U n i t */ /* */ /* Format one unit. Erase the unit, and mark the physical units table. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* unitNo : Physical unit to format */ /* eraseMarkOffset : Offset to place erase mark */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
static FLStatus formatUnit(Bnand vol, ANANDUnitNo unitNo, dword eraseMarkOffset) { word eraseMark; dword eraseCount; FLStatus status; #ifndef RAM_MTD
static const #endif /* RAM_MTD */
byte discard = (byte)CLEAR_DISCARD;
status = getUnitTailer(&vol,unitNo,&eraseMark,&eraseCount,UNIT_TAILER_OFFSET); if(status != flOK) return status;
#ifdef NFTL_CACHE
/* Update ANANDUnitHeader cache to prevent re-filling from flash */ if (vol.ucache != NULL) { vol.ucache[unitNo].virtualUnitNo = ANAND_NO_UNIT; vol.ucache[unitNo].prevUnitNo = ANAND_NO_UNIT; vol.ucache[unitNo].ANAC = ANAND_UNIT_FREE; vol.ucache[unitNo].NAC = ANAND_UNIT_FREE; }
/*
* Purge the Sector Flags cache (set entries for all the unit's * sectors to SECTOR_FREE). */
if(vol.scache!=NULL) { tffsset(&(vol.scache[unitNo << (vol.unitSizeBits - SECTOR_SIZE_BITS-2)]), S_CACHE_4_SECTORS_FREE, 1 << (vol.unitSizeBits - SECTOR_SIZE_BITS - 2)); } #endif /* NFTL_CACHE */
/* Mark unit as unusable before erase */ vol.flash->write(vol.flash,(((dword)(vol.firstUnit + unitNo))<<vol.unitSizeBits)+DISCARD_UNIT_OFFSET,&discard,1,EXTRA);
/* Physicaly erase unit */ status = vol.flash->erase(vol.flash,(word)((word)(vol.firstUnit + unitNo) << vol.blockMultiplierBits),(word)(1 << vol.blockMultiplierBits));
vol.eraseSum++; eraseCount++; if (eraseCount == 0) /* was hex FF's */ eraseCount++;
if (status == flOK) status = setUnitTailer(&vol,unitNo,ERASE_MARK,eraseCount,eraseMarkOffset);
if (status != flOK) { markUnitBad(&vol,unitNo); /* make sure unit format is not valid */ return status; }
if (vol.physicalUnits[unitNo] != ANAND_UNIT_FREE) { vol.physicalUnits[unitNo] = ANAND_UNIT_FREE; vol.freeUnits++; }
return status; }
#endif /* FL_READ_ONLY */
/*----------------------------------------------------------------------*/ /* g e t U n i t D a t a */ /* */ /* Get virtual unit No. and replacement unit no. of a unit. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* unitNo : Physical unit number */ /* virtualUnitNo : Returns the virtual unit no. */ /* prevUnitNo : Returns the previous unit no. */ /* ANAC : Returns the Accumulating Number Along Chain. */ /* NAC : Returns the Number Along Chain value. */ /* validFields : Returns a bit map of the valid fields. */ /* */ /* Returns: */ /* flOK on success, flHWProtection on H/W read protection. */ /*----------------------------------------------------------------------*/
static FLStatus getUnitData(Bnand vol, ANANDUnitNo unitNo, ANANDUnitNo *virtualUnitNo, ANANDUnitNo *prevUnitNo, byte *ANAC, byte *NAC, byte *validFields) { ANANDUnitHeader unitData; SecondANANDUnitHeader secondUnitData; FLStatus status; byte parityPerField=0; byte temp; byte returnedValidField = ALL_PARITY_BITS_OK; byte curValidFields[2]; int index;
#ifdef NFTL_CACHE
/* on cache miss read ANANDUnitHeader from flash and re-fill cache */ if ((vol.ucache != NULL)&&(vol.ucache[unitNo].virtualUnitNo != 0xDEAD) && (vol.ucache[unitNo].prevUnitNo != 0xDEAD)) { *virtualUnitNo = vol.ucache[unitNo].virtualUnitNo; *prevUnitNo = vol.ucache[unitNo].prevUnitNo; *ANAC = vol.ucache[unitNo].ANAC; *NAC = vol.ucache[unitNo].NAC; } else #endif /* NFTL_CACHE */
{ /* no ANANDUnitHeader cache MUST read first header */
*validFields = 0; /* Set all fields to be invalid */
/* Read first unit data */ status = vol.flash->read(vol.flash, unitBaseAddress(vol,unitNo) + UNIT_DATA_OFFSET, &unitData, sizeof(ANANDUnitHeader), EXTRA); if(status != flOK) return status;
*virtualUnitNo = LE2(unitData.virtualUnitNo); *prevUnitNo = LE2(unitData.prevUnitNo ); *ANAC = unitData.ANAC; *NAC = unitData.NAC;
for(index=0;index<2;index++) { /* If all data is 0xff assume a free unit */ if((*virtualUnitNo == ANAND_NO_UNIT )&& (*prevUnitNo == ANAND_NO_UNIT )&& (*ANAC == ANAND_UNIT_FREE)&& (*NAC == ANAND_UNIT_FREE)&& (unitData.discarded == ANAND_UNIT_FREE)) { #ifndef FL_READ_ONLY
if(index!=0) { /* If this is the second copy then the first was not */ /* erased, but since it was written first it makes no */ /* sence. Let us erase it just in case. */
status = formatUnit(&vol,unitNo,UNIT_TAILER_OFFSET); if(status != flOK) return status; } #endif /* FL_READ_ONLY */
break; }
/* Not a free unit check unit data validity (discard and partity) */ if ((onesCount((byte)(unitData.discarded^DISCARD))>1)) { /* Discarded mark is more then 1 bit distance from 0xAA */ /* Assume erase operation was interrupted and erase unit */
TL_DEBUG_PRINT(tl_out,"getUnitData : unit %d has an invalid discard mark\n",unitNo); TL_DEBUG_PRINT(tl_out," it might be helpful to know it was %d\n",unitData.discarded);
#ifndef FL_READ_ONLY
status = formatUnit(&vol,unitNo,UNIT_TAILER_OFFSET); if(status != flOK) return status; #endif /* FL_READ_ONLY */
*virtualUnitNo = ANAND_NO_UNIT; *prevUnitNo = ANAND_NO_UNIT; *ANAC = ANAND_UNIT_FREE; *NAC = ANAND_UNIT_FREE; break; }
/* Discarded OK now check parity field */ parityPerField = 0; temp=(byte)(((byte *)virtualUnitNo)[0]^((byte *)virtualUnitNo)[1]); if((onesCount(temp) & 1)==1) parityPerField|=VU_PARITY_BIT; temp=(byte)(((byte *)prevUnitNo)[0]^((byte *)prevUnitNo)[1]); if((onesCount(temp) & 1)==1) parityPerField|=PU_PARITY_BIT; if((onesCount(unitData.ANAC) & 1)==1) parityPerField|=ANAC_PARITY_BIT; if((onesCount(unitData.NAC) & 1)==1) parityPerField|=NAC_PARITY_BIT;
/* Store valid fields using bitmap */ curValidFields[index] = (byte)((~(parityPerField ^ unitData.parityPerField)) & ALL_PARITY_BITS_OK);
if(curValidFields[index] == ALL_PARITY_BITS_OK) { /* If either copies has valid fields (all of them) use it */ break; } else { if(index>0) { /* Use first header data if possible otherwise use second */ returnedValidField = (byte)(curValidFields[0] | curValidFields[1]); TL_DEBUG_PRINT(tl_out,"getUnitData : The returned valid field indicator is %d\n",returnedValidField); TL_DEBUG_PRINT(tl_out," While %d indicates a valid unit data\n",ALL_PARITY_BITS_OK); if(curValidFields[0] & VU_PARITY_BIT) *virtualUnitNo = LE2(unitData.virtualUnitNo);
if(curValidFields[0] & PU_PARITY_BIT) *prevUnitNo = LE2(unitData.prevUnitNo );
if(curValidFields[0] & ANAC_PARITY_BIT) *ANAC = unitData.ANAC;
if(curValidFields[0] & NAC_PARITY_BIT) *NAC = unitData.ANAC; break; } }
/* Read second unit header for next iteration */ status = vol.flash->read(vol.flash, unitBaseAddress(vol,unitNo) + SECOND_HEADER_OFFSET + UNIT_DATA_OFFSET, &secondUnitData, sizeof(SecondANANDUnitHeader), EXTRA); if(status != flOK) return status;
*virtualUnitNo = LE2(secondUnitData.virtualUnitNo); *prevUnitNo = LE2(secondUnitData.prevUnitNo ); *ANAC = secondUnitData.ANAC; *NAC = secondUnitData.NAC;
TL_DEBUG_PRINT(tl_out,"getUnitData : First unit header is not OK in unit %d \n",unitNo); TL_DEBUG_PRINT(tl_out,"getUnitData : Virtual Unit No = %d \n",LE2(unitData.virtualUnitNo)); TL_DEBUG_PRINT(tl_out,"getUnitData : Previous Unit No = %d \n",LE2(unitData.prevUnitNo)); TL_DEBUG_PRINT(tl_out,"getUnitData : ANAC = %d \n",unitData.ANAC); TL_DEBUG_PRINT(tl_out,"getUnitData : NAC = %d \n",unitData.NAC); TL_DEBUG_PRINT(tl_out,"getUnitData : ParityPerField = %d \n",unitData.parityPerField); TL_DEBUG_PRINT(tl_out,"getUnitData : Discarded = %d \n",unitData.discarded);
TL_DEBUG_PRINT(tl_out,"getUnitData : Second unit header of the same unit %d is:\n",unitNo); TL_DEBUG_PRINT(tl_out,"getUnitData : Virtual Unit No = %d \n",LE2(secondUnitData.virtualUnitNo)); TL_DEBUG_PRINT(tl_out,"getUnitData : Previous Unit No = %d \n",LE2(secondUnitData.prevUnitNo)); TL_DEBUG_PRINT(tl_out,"getUnitData : ANAC = %d \n",secondUnitData.ANAC); TL_DEBUG_PRINT(tl_out,"getUnitData : NAC = %d \n",secondUnitData.NAC); TL_DEBUG_PRINT(tl_out,"getUnitData : ParityPerField = %d \n",secondUnitData.parityPerField); }
#ifdef NFTL_CACHE
if ((vol.ucache != NULL) && /* Cache enabled */ (returnedValidField == ALL_PARITY_BITS_OK)) /* All fields valid */ { vol.ucache[unitNo].virtualUnitNo = *virtualUnitNo; vol.ucache[unitNo].prevUnitNo = *prevUnitNo; vol.ucache[unitNo].ANAC = *ANAC; vol.ucache[unitNo].NAC = *NAC; } #endif /* NFTL_CACHE */
} *validFields = returnedValidField; return flOK;
}
/*----------------------------------------------------------------------*/ /* g e t P r e v U n i t */ /* */ /* Get next unit in chain. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* unitNo : Physical unit number */ /* virUnitNo : The expected virtual unit number */ /* */ /* Returns: */ /* Physical unit number of the unit following unitNo in the chain. */ /* If such unit do not exist, return ANAND_NO_UNIT. */ /*----------------------------------------------------------------------*/
static ANANDUnitNo getPrevUnit(Bnand vol, ANANDUnitNo unitNo, ANANDUnitNo virUnitNo) { ANANDUnitNo virtualUnitNo, replacementUnitNo; byte ANAC,NAC; byte parityPerField;
/* If first in chain there can be not previous unit */ if ((vol.physicalUnits[unitNo] & FIRST_IN_CHAIN)) return ANAND_NO_UNIT;
getUnitData(&vol,unitNo,&virtualUnitNo,&replacementUnitNo,&ANAC,&NAC,&parityPerField);
/* check if unit is valid */ if((badParityResult(parityPerField)) || ( virUnitNo != virtualUnitNo )) { TL_DEBUG_PRINT(tl_out,"getPrevUnit : An invalid unit was detected on getPrevUnit - parity is %x/0xf ",parityPerField); TL_DEBUG_PRINT(tl_out,"given virtual unit no is %d ",virUnitNo); TL_DEBUG_PRINT(tl_out,"where %d was read\n",virtualUnitNo); SET_EXIT(INFTL_FAILED_MOUNT); return ANAND_BAD_CHAIN_UNIT; } return replacementUnitNo; }
#ifdef NFTL_CACHE
/*----------------------------------------------------------------------*/ /* g e t S e c t o r F l a g s F r o m C a c h e */ /* */ /* Get sector flags from Sector Cache. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* address : starting address of the sector */ /* */ /* Returns: */ /* sector flags (SECTOR_USED, SECTOR_DELETED etc.) */ /*----------------------------------------------------------------------*/
static byte getSectorFlagsFromCache(Bnand vol, CardAddress address) { return scacheTable[((vol.scache[(address - vol.firstUnitAddress) >> (SECTOR_SIZE_BITS+2)] >> (((word)address >> 8) & 0x7)) & 0x3)]; }
/*----------------------------------------------------------------------*/ /* s e t S e c t o r F l a g s C a c h e */ /* */ /* Get sector flags from Sector Cache. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* address : starting address of the sector */ /* sectorFlags : one of SECTOR_USED, SECTOR_DELETED etc. */ /* */ /*----------------------------------------------------------------------*/
static void setSectorFlagsCache(Bnand vol, CardAddress address, byte sectorFlags) { register byte tmp, val;
if (vol.scache == NULL) return;
tmp = vol.scache[(address - vol.firstUnitAddress) >> (SECTOR_SIZE_BITS+2)];
switch(sectorFlags) { case SECTOR_USED: val = S_CACHE_SECTOR_USED; break; case SECTOR_FREE: val = S_CACHE_SECTOR_FREE; break; case SECTOR_DELETED: val = S_CACHE_SECTOR_DELETED; break; default:/* SECTOR_IGNORE */val = S_CACHE_SECTOR_IGNORE; break; }
switch (((word)address >> 8) & 0x7) { case 0: tmp = (tmp & 0xfc) | (val ); break; /* update bits 0..1 */ case 2: tmp = (tmp & 0xf3) | (val << 2); break; /* bits 2..3 */ case 4: tmp = (tmp & 0xcf) | (val << 4); break; /* bits 4..5 */ case 6: tmp = (tmp & 0x3f) | (val << 6); break; /* bits 6..7 */ }
vol.scache[(address - vol.firstUnitAddress) >> (SECTOR_SIZE_BITS+2)] = tmp; }
#endif /* NFTL_CACHE */
/*----------------------------------------------------------------------*/ /* g e t S e c t o r F l a g s */ /* */ /* Get sector status. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* sectorAddress : Physical address of the sector */ /* */ /* Returns: */ /* Return the OR of the two bytes in the sector status area (the */ /* bytes should contain the same data). */ /*----------------------------------------------------------------------*/
static byte getSectorFlags(Bnand vol, CardAddress sectorAddress) { byte flags[2]; byte blockFlag = SECTOR_IGNORE; byte index,tmpSector; FLStatus status;
#ifdef NFTL_CACHE
if (vol.scache != NULL) { /* check for Sector Flags cache hit */
blockFlag = getSectorFlagsFromCache(&vol, sectorAddress); if (blockFlag != SECTOR_IGNORE) return blockFlag; } #endif /* NFTL_CACHE */
vol.flash->read(vol.flash, sectorAddress + SECTOR_DATA_OFFSET, flags, sizeof flags, EXTRA);
if((flags[0] == flags[1]) && (isValidSectorFlag(flags[0]))) { blockFlag = flags[0]; } else /* Sector flags that were read are not legal or not valid */ { TL_DEBUG_PRINT(tl_out,"getSectorFlags : Sector flags are not valid - physical addr %ld ",sectorAddress); TL_DEBUG_PRINT(tl_out,"first flag was %x ",flags[0]); TL_DEBUG_PRINT(tl_out,"while scond is %x\n",flags[1]); SET_EXIT(INFTL_FAILED_MOUNT);
/* Force remapping of internal catched sector */ vol.flash->socket->remapped = TRUE;
/* Check for ignored sector using the EDC */ status = vol.flash->read(vol.flash, sectorAddress, inftlBuffer, SECTOR_SIZE, EDC); #if (defined(VERIFY_WRITE) || defined (VERIFY_VOLUME) || defined(VERIFY_ERASED_SECTOR))
/* Now restore the ff's for the verifySectors routine */ tffsset(inftlBuffer,0xff,SECTOR_SIZE); #endif /* VERIFY_WRITE || VERIFY_VOLUME || VERIFY_ERASED_SECTOR */
if(status == flOK) { /* Check if distance is less then 2 bits failure since */ /* 2 bits failure can be either delete or used */ for (index=0 , tmpSector = (byte)SECTOR_USED ; index < 2 ; index++ , tmpSector = (byte)SECTOR_DELETED) { if (distanceOf(flags[0], tmpSector) + distanceOf(flags[1], tmpSector) <= 2) { blockFlag = tmpSector; break; } } if(index>=2) return SECTOR_IGNORE; } else { return SECTOR_IGNORE; } }
#ifdef NFTL_CACHE
/* update Sector Flags cache */ setSectorFlagsCache(&vol, sectorAddress, blockFlag); #endif /* NFTL_CACHE */
return blockFlag; }
#ifndef FL_READ_ONLY
/*----------------------------------------------------------------------*/ /* s e t U n i t D a t a */ /* */ /* Set virtual unit No. and replacement unit no. of a unit. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* unitNo : Physical unit number */ /* virtualUnitNo : Virtual unit no. */ /* prevUnitNo : Previous unit no. */ /* ANAC : Accumulating Number Along Chain */ /* NAC : Number Along Chain. */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /* */ /*----------------------------------------------------------------------*/
static FLStatus setUnitData(Bnand vol, ANANDUnitNo unitNo, ANANDUnitNo virtualUnitNo, ANANDUnitNo prevUnitNo,byte ANAC,byte NAC) { ANANDUnitHeader unitData; SecondANANDUnitHeader secondUnitData; FLStatus status; ANANDUnitNo newVirtualUnitNo, newPrevUnitNo; byte newANAC,newNAC; byte temp; byte parityPerField = 0;
if( prevUnitNo == unitNo ) /* prevent chain loop */ return flGeneralFailure;
/* Calculate parity per field */ temp=(byte)(((byte *)&virtualUnitNo)[0]^((byte *)&virtualUnitNo)[1]); if((onesCount(temp) & 1)==1) parityPerField|=VU_PARITY_BIT;
temp=(byte)(((byte *)&prevUnitNo)[0]^((byte *)&prevUnitNo)[1]); if((onesCount(temp) & 1)==1) parityPerField|=PU_PARITY_BIT;
if((onesCount(ANAC) & 1)==1) parityPerField|=ANAC_PARITY_BIT;
if((onesCount(NAC) & 1)==1) parityPerField|=NAC_PARITY_BIT;
/* Store fields in proper unit header record */ toLE2(unitData.virtualUnitNo,virtualUnitNo); toLE2(secondUnitData.virtualUnitNo,virtualUnitNo); toLE2(unitData.prevUnitNo,prevUnitNo); toLE2(secondUnitData.prevUnitNo,prevUnitNo); unitData.ANAC=secondUnitData.ANAC=ANAC; unitData.NAC=secondUnitData.NAC=NAC; unitData.parityPerField=secondUnitData.parityPerField=parityPerField; unitData.discarded=DISCARD;
/* Write first unit header */ status = vol.flash->write(vol.flash, unitBaseAddress(vol,unitNo) + UNIT_DATA_OFFSET, &unitData, sizeof(ANANDUnitHeader), EXTRA); if(status == flOK) /* Write second unit header */ status = vol.flash->write(vol.flash, unitBaseAddress(vol,unitNo) + SECOND_HEADER_OFFSET+UNIT_DATA_OFFSET, &secondUnitData, sizeof(SecondANANDUnitHeader), EXTRA); if(status == flOK) {
#if (defined(VERIFY_WRITE) || defined(VERIFY_ERASED_SECTOR))
if (vol.flash->socket->verifyWrite == FL_ON) goto fillCache; #endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */
#ifdef NFTL_CACHE
if (vol.ucache != NULL) /* Mark unit cache as none valid before read */ { vol.ucache[unitNo].virtualUnitNo = 0xDEAD; vol.ucache[unitNo].prevUnitNo = 0xDEAD; } #endif /* NFTL_CACHE */
status = getUnitData(&vol,unitNo,&newVirtualUnitNo, &newPrevUnitNo,&newANAC,&newNAC,&parityPerField); if (status == flOK) { if ((virtualUnitNo == newVirtualUnitNo) && (prevUnitNo == newPrevUnitNo ) && (!badParityResult(parityPerField) ) ) goto fillCache; } }
/* If we reached here we failed in writing unit header */ /* Erase unit and report write fault */
DEBUG_PRINT(("setUnitData : Failed setting unit data\r\n")); status = formatUnit(&vol,unitNo,UNIT_TAILER_OFFSET); if (status != flOK) { markUnitBad(&vol,unitNo); return status; } else { return flWriteFault; }
fillCache: /* Unit headers were placed OK, now update cache */ #ifdef NFTL_CACHE
/* Update ANANDUnitHeader cache to prevent re-filling from flash */ if (vol.ucache != NULL) { vol.ucache[unitNo].virtualUnitNo = virtualUnitNo; vol.ucache[unitNo].prevUnitNo = prevUnitNo; vol.ucache[unitNo].ANAC = ANAC; vol.ucache[unitNo].NAC = NAC; } #endif /* NFTL_CACHE */
return flOK; }
/*----------------------------------------------------------------------*/ /* d i s c a r d Q u i c k M o u n t I n f o */ /* */ /* Mark quick mount information is none valid. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
static FLStatus discardQuickMountInfo(Bnand vol) { #ifndef RAM_MTD
static const #endif /* RAM_MTD */
dword tmp = 0; /* Dis - validate quick mount data */ if(vol.firstMediaWrite == FALSE) { vol.firstMediaWrite = TRUE; return vol.flash->write(vol.flash, QUICK_MOUNT_VALID_SIGN_OFFSET + ((dword)vol.firstQuickMountUnit << vol.unitSizeBits), &tmp, sizeof(tmp), 0); } return flOK; }
/*----------------------------------------------------------------------*/ /* m a r k A s I g n o r e d */ /* */ /* Mark sector at given address as ignored. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* addr : Physical address of the sector */ /* */ /*----------------------------------------------------------------------*/
static void markAsIgnored(Bnand vol,CardAddress addr) { #ifndef RAM_MTD
static const #endif /* RAM_MTD */
byte sectorFlags[2] = {SECTOR_IGNORE,SECTOR_IGNORE};
DEBUG_PRINT(("markAsIgnored : A sector is being marked as ignored\r\n"));
discardQuickMountInfo(&vol);
#ifdef NFTL_CACHE
setSectorFlagsCache(&vol, addr, SECTOR_IGNORE); #endif /* NFTL_CACHE */
vol.flash->write(vol.flash,addr+SECTOR_DATA_OFFSET,sectorFlags,sizeof(sectorFlags),EXTRA);
#ifdef MAKE_SURE_IGNORE_HAS_BAD_EDC
/* Force remapping of internal catched sector */ vol.flash->socket->remapped = TRUE;
/* Make sure EDC is wrong - a slite problem with PPP */ if(vol.flash->read(vol.flash,addr,inftlBuffer,sizeof(inftlBuffer),EDC)==flOK) { tffsset(inftlBuffer,0,sizeof(inftlBuffer)); vol.flash->write(vol.flash,addr,inftlBuffer,sizeof(inftlBuffer),0); } #if (defined(VERIFY_WRITE) || defined (VERIFY_VOLUME) || defined(VERIFY_ERASED_SECTOR))
/* Now restore the ff's for the verifySectors routine */ tffsset(inftlBuffer,0xff,sizeof(inftlBuffer)); #endif /* VERIFY_WRITE || VERIFY_VOLUME || VERIFY_ERASED_SECTOR */
#endif /* MAKE_SURE_IGNORE_HAS_BAD_EDC */
}
#endif /* FL_READ_ONLY */
/*----------------------------------------------------------------------*/ /* v i r t u a l 2 P h y s i c a l */ /* */ /* Translate virtual sector number to physical address. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* sectorNo : Virtual sector number */ /* startAddress : Physical address to start from */ /* lastOK : TRUE - the current sector in the last unit of */ /* virtual unit chain is free (not marked as */ /* deleted / ignored or used). */ /* */ /* Note: The first unit of the search is assumed to be valid. */ /* */ /* Returns: */ /* physical address of sectorNo */ /*----------------------------------------------------------------------*/
static CardAddress virtual2Physical(Bnand vol, SectorNo sectorNo, CardAddress startAddress,FLBoolean* lastOK) { word unitOffset = (word)((sectorNo & vol.sectorsPerUnitMask) << SECTOR_SIZE_BITS); ANANDUnitNo unitNo, virUnitNo; ANANDUnitNo chainBound = 0; CardAddress sectorAddress = ANAND_UNASSIGNED_ADDRESS; byte sectorFlags = SECTOR_FREE;
/* follow the chain */ virUnitNo = (ANANDUnitNo)(sectorNo >> vol.sectorsPerUnitBits); if (startAddress == ANAND_UNASSIGNED_ADDRESS) { /* Start from last unit in chain */ unitNo = vol.virtualUnits[virUnitNo]; } else { /* Start from the unit that follows the given unit */
TL_DEBUG_PRINT(tl_out,"virtual2Physical : Virtual to physical started from middle of chain on unit %d\n",virUnitNo); SET_EXIT(INFTL_FAILED_MOUNT); unitNo = getPrevUnit(&vol,(ANANDUnitNo)((startAddress >> vol.unitSizeBits) - vol.firstUnit),virUnitNo); if(unitNo == ANAND_BAD_CHAIN_UNIT) return ANAND_BAD_CHAIN_ADDRESS; }
for (;unitNo != ANAND_NO_UNIT;unitNo = getPrevUnit(&vol,unitNo,virUnitNo)) { if((unitNo == ANAND_BAD_CHAIN_UNIT ) || (chainBound >= DOUBLE_MAX_UNIT_CHAIN) ) return ANAND_BAD_CHAIN_ADDRESS;
sectorAddress = unitBaseAddress(vol,unitNo) + unitOffset; sectorFlags = getSectorFlags(&vol,sectorAddress);
/* Report if the last unit of the chain is used */ if ((unitNo == vol.virtualUnits[virUnitNo]) && (sectorFlags != SECTOR_FREE)) *lastOK = FALSE;
if((sectorFlags==SECTOR_FREE) || (sectorFlags==SECTOR_IGNORE)) { chainBound++; continue; } break; }
if((sectorFlags==SECTOR_IGNORE)||(sectorFlags==SECTOR_FREE)||(sectorFlags==SECTOR_DELETED)) /* Sector was never written*/ return ANAND_UNASSIGNED_ADDRESS; return sectorAddress; }
/*----------------------------------------------------------------------*/ /* i n i t I N F T L b a s i c */ /* */ /* Initializes essential volume data */ /* */ /* Note : This routine is called both by the mount and format initINFTL */ /* and as a preparation for counting the number of partitions function. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* flash : Flash media mounted on this socket */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
static FLStatus initINFTLbasic(Bnand vol, FLFlash *flash) { dword noOfUnits; /* Keep this variable dword , for large DiskOnChips */
if (flash == NULL || !(flash->flags & INFTL_ENABLED)) { DEBUG_PRINT(("\nDebug: media is not fit for INFTL format.\r\n")); return flUnknownMedia; } if(flash->readBBT == NULL) { DEBUG_PRINT(("\nDEBUG : MTD read BBT routine was not initialized\r\n")); return flFeatureNotSupported; }
vol.flash = flash; vol.erasableBlockSizeBits = flash->erasableBlockSizeBits; vol.unitSizeBits = vol.erasableBlockSizeBits; noOfUnits = (dword)((vol.flash->noOfChips * vol.flash->chipSize) >> vol.unitSizeBits);
/* Bound number of units to find room in 64 Kbytes Segment */
if((noOfUnits > MAX_UNIT_NUM) && (vol.unitSizeBits < MAX_UNIT_SIZE_BITS)) { vol.unitSizeBits++; noOfUnits >>= 1; }
vol.blockMultiplierBits = vol.unitSizeBits - vol.erasableBlockSizeBits;
/* get pointer to buffer (we assume SINGLE_BUFFER is not defined) */ vol.buffer = flBufferOf(flSocketNoOf(vol.flash->socket)); #ifdef VERIFY_ERASED_SECTOR
vol.verifyBuffer = (dword *)flReadBackBufferOf(flSocketNoOf(flash->socket)); #endif /* VERIFY_ERASED_SECTOR */
flash->socket->remapped = TRUE;
return flOK; }
/*----------------------------------------------------------------------*/ /* i n i t N F T L */ /* */ /* Initializes essential volume data as a preparation for mount or */ /* format. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* flash : Flash media mounted on this socket */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
static FLStatus initINFTL(Bnand vol, FLFlash *flash) { dword chipSize; /* Keep this variable dword , for large DiskOnChips */ FLStatus status;
if(flash!=NULL) { tffsset(&vol,0,sizeof(Bnand)); /* Clear Bnand volume */
#ifdef NT5PORT
vol.socketNo = (byte)(flSocketNoOf(flash->socket)); /* socket No */ #else
vol.socketNo = flSocketNoOf(flash->socket); /* socket No */ #endif /*NT5PORT*/
}
status = initINFTLbasic(&vol, flash); if(status != flOK) return status;
chipSize = (dword)(flash->chipSize * flash->noOfChips);
#ifndef FL_MALLOC
if (chipSize > (dword)MAX_VOLUME_MBYTES << 20) { DEBUG_PRINT(("\nDebug: TrueFFS is customized for smaller media capacities.\r\n")); return flGeneralFailure; } if (ASSUMED_NFTL_UNIT_SIZE > (1L<<vol.unitSizeBits)) { DEBUG_PRINT(("\nDebug: TrueFFS is customized for smaller unit sizes.\r\n")); return flGeneralFailure; } #endif /* FL_MALLOC */
vol.physicalUnits = NULL; vol.virtualUnits = NULL;
#ifdef NFTL_CACHE
vol.ucache = NULL; vol.scache = NULL; #endif /* NFTL_CACHE */
vol.mappedSectorNo = UNASSIGNED_SECTOR; vol.countsValid = 0; /* No units have a valid count yet */ vol.firstUnit = 0; vol.sectorsPerUnit = 1 << (vol.unitSizeBits - SECTOR_SIZE_BITS); vol.sectorsPerUnitBits = vol.unitSizeBits - SECTOR_SIZE_BITS; vol.sectorsPerUnitMask = vol.sectorsPerUnit - 1; vol.noOfUnits = (ANANDUnitNo)(chipSize >> vol.unitSizeBits); vol.firstMediaWrite = FALSE; #if (defined(VERIFY_WRITE) || defined(VERIFY_VOLUME) || defined(VERIFY_ERASED_SECTOR))
vol.verifiedSectorNo = 0; /* Largest sector verified so far */ #endif /* VERIFY_WRITE || VERIFY_VOLUME || VERIFY_ERASED_SECTOR */
#ifdef CHECK_MOUNT
vol.debugState = 0; #endif /* CHECK_MOUNT */
return flOK; }
/*----------------------------------------------------------------------*/ /* i n i t T a b l e s */ /* */ /* Allocates and initializes the dynamic volume table, including the */ /* unit tables and secondary virtual map. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* ramForUnits : Number of bytes allocated to previous volumes */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
#ifdef FL_MALLOC
static FLStatus initTables(Bnand vol) { /* Allocate the conversion tables */
vol.physicalUnits = (ANANDPhysUnit FAR1*) FL_FAR_MALLOC(vol.noOfUnits * sizeof(ANANDPhysUnit)); if (vol.noOfVirtualUnits > 0) vol.virtualUnits = (ANANDUnitNo FAR1*) FL_FAR_MALLOC(vol.noOfVirtualUnits * sizeof(ANANDUnitNo)); if ((vol.physicalUnits == NULL) || ((vol.virtualUnits == NULL) && (vol.noOfVirtualUnits > 0))) { DEBUG_PRINT(("\nDebug: failed allocating conversion tables for INFTL.\r\n")); return flNotEnoughMemory; }
/* Allocate the multi-sector buffer (one per socket) */ if (++(multiSectorBufCounter[vol.socketNo]) == 0) { multiSectorBuf[vol.socketNo] = (byte *)FL_MALLOC(SECTOR_SIZE<<1); if (multiSectorBuf[vol.socketNo] == NULL) { DEBUG_PRINT(("\nDebug: failed allocating multi-sector buffers for INFTL.\r\n")); return flNotEnoughMemory; } } return flOK; } #else
static FLStatus initTables(Bnand vol,dword ramForUnits) { Sbyte *heapPtr;
vol.heap = &socketHeap[flSocketNoOf(vol.flash->socket)][ramForUnits]; heapPtr = vol.heap; vol.physicalUnits = (ANANDPhysUnit FAR1*) heapPtr; heapPtr += vol.noOfUnits * sizeof(ANANDPhysUnit); vol.virtualUnits = (ANANDUnitNo FAR1*) heapPtr; heapPtr += vol.noOfVirtualUnits * sizeof(ANANDUnitNo);
if ((ANAND_HEAP_SIZE < heapPtr - vol.heap) || (ASSUMED_NFTL_UNIT_SIZE > 1L << vol.unitSizeBits)) { DEBUG_PRINT(("\nDebug: not enough memory for INFTL conversion tables.\r\n")); return flNotEnoughMemory; } return flOK; } #endif /* FL_MALLOC */
/*----------------------------------------------------------------------*/ /* f i r s t I n C h a i n */ /* */ /* Find first unit in chain. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* unitNo : Start the search from this unit */ /* nextUnit : Returns the Second unit of the chain. */ /* */ /* Returns: */ /* Physical unit number of the last unit in chain. */ /*----------------------------------------------------------------------*/
static ANANDUnitNo firstInChain(Bnand vol, ANANDUnitNo unitNo,ANANDUnitNo* nextUnit) { ANANDUnitNo firstVirtualUnitNo, firstReplacementUnitNo,prevVirtualUnitNo; ANANDUnitNo nextUnitNo,prevReplacementUnitNo; ANANDUnitNo chainBound = 0; byte ANAC,NAC,parityPerField;
if(unitNo==ANAND_NO_UNIT) /* Sanity check */ { if(nextUnit!=NULL) *nextUnit=ANAND_NO_UNIT; return unitNo; }
/* If this unit is the first of its chain , no need to keep looking */ if( vol.physicalUnits[unitNo] & FIRST_IN_CHAIN ) { if(nextUnit!=NULL) *nextUnit=ANAND_NO_UNIT; return unitNo; } getUnitData(&vol,unitNo,&firstVirtualUnitNo,&firstReplacementUnitNo,&ANAC,&NAC,&parityPerField); /* check if unit is valid */ if(badParityResult(parityPerField)) { DEBUG_PRINT(("\nFirst In chain found bad unit header\r\n")); if(nextUnit!=NULL) *nextUnit=ANAND_NO_UNIT; return ANAND_BAD_CHAIN_UNIT; }
nextUnitNo=unitNo; unitNo=firstReplacementUnitNo; while( (unitNo < vol.noOfUnits) && /* Validate replacement unit no. */ (chainBound < DOUBLE_MAX_UNIT_CHAIN) ) { if(unitNo==ANAND_NO_UNIT) break; getUnitData(&vol,unitNo,&prevVirtualUnitNo,&prevReplacementUnitNo,&ANAC,&NAC,&parityPerField); /* check if unit is valid */ if(badParityResult(parityPerField)) { DEBUG_PRINT(("\nFirst In chain found bad unit header\r\n")); if(nextUnit!=NULL) *nextUnit=ANAND_NO_UNIT;
return ANAND_BAD_CHAIN_UNIT; }
if(( vol.physicalUnits[unitNo] & FIRST_IN_CHAIN )&&(firstVirtualUnitNo==prevVirtualUnitNo)) { if(nextUnit!=NULL) *nextUnit=nextUnitNo;
return unitNo; }
if( prevVirtualUnitNo != (firstVirtualUnitNo ) ) { /* This one does not belong to the chain */ if(nextUnit!=NULL) *nextUnit=ANAND_NO_UNIT;
return nextUnitNo; } nextUnitNo = unitNo; unitNo = prevReplacementUnitNo; chainBound++; }
return ANAND_BAD_CHAIN_UNIT; }
#ifndef FL_READ_ONLY
/*----------------------------------------------------------------------*/ /* w r i t e A n d C h e c k */ /* */ /* Physicaly write up to 2 sectors on given address and verify. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* address : Physical address of the sector to write to */ /* fromAddress : Buffer of data to write */ /* flags : Write flags (ECC, overwrite etc.) */ /* howMany : Number of sectors to write. */ /* */ /* Returns: */ /* Status : 0 on success, failed otherwise. */ /*----------------------------------------------------------------------*/
static FLStatus writeAndCheck(Bnand vol, CardAddress address, void FAR1 *fromAddress, unsigned flags,word howMany) { FLStatus status; register int i; #ifdef VERIFY_ERASED_SECTOR
register int noOfDword; int j; #endif /* VERIFY_ERASED_SECTOR */
/* Toggle verify write flag */ #if (defined(VERIFY_WRITE) || defined(VERIFY_ERASED_SECTOR))
switch (flVerifyWrite[vol.socketNo][vol.flash->socket->curPartition]) { case FL_OFF: if (vol.verifiedSectorNo>vol.curSectorWrite+howMany) break; case FL_ON: #ifdef VERIFY_WRITE
vol.flash->socket->verifyWrite = FL_ON; #endif /* VERIFY_WRITE */
#ifdef VERIFY_ERASED_SECTOR
/* Make sure all of the sectors are really free */ checkStatus(vol.flash->read(vol.flash,address,vol.verifyBuffer,SECTOR_SIZE*howMany,0)); noOfDword = SECTOR_SIZE/sizeof(dword); for(j=0;j<howMany;j++) /* Loop over sectors */ { for(i = 0;i<noOfDword;i++) /* Loop over sector data */ { if(vol.verifyBuffer[i]!=0xffffffffL) { markAsIgnored(&vol,address+j*SECTOR_SIZE); DEBUG_PRINT(("writeAndCheck : The sector was not erased and is ignored\r\n")); return flWriteFault; } } } #endif /* VERIFY_ERASED_SECTOR */
break; default: break; } #endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */
/* Write sector */ status = vol.flash->write(vol.flash,address,fromAddress,SECTOR_SIZE*howMany,(word)flags);
#ifdef VERIFY_WRITE
if(flVerifyWrite[vol.socketNo][vol.flash->socket->curPartition] != FL_ON) vol.flash->socket->verifyWrite = FL_OFF; #endif /* VERIFY_WRITE */
/* Make sure write succeded and update sector catche */ if (status == flWriteFault) { /* write failed, ignore this sector */ DEBUG_PRINT(("writeAndCheck : Write of a sector failed and was marked as ignored\r\n"));
for(i=0;i<howMany;i++) { markAsIgnored(&vol,address+i*SECTOR_SIZE); } } #ifdef NFTL_CACHE
else { for(i=0;i<howMany;i++) setSectorFlagsCache(&vol, address+i*SECTOR_SIZE, SECTOR_USED); } #endif /* NFTL_CACHE */
return status; }
/*----------------------------------------------------------------------*/ /* a s s i g n U n i t */ /* */ /* Assigns a virtual unit no. to a unit */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* unitNo : Physical unit number */ /* virtualUnitNo : Virtual unit number to assign */ /* ANAC : Accumulating Number Along Chain */ /* NAC : Number Along Chain. */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
static FLStatus assignUnit(Bnand vol, ANANDUnitNo unitNo, ANANDUnitNo virtualUnitNo, byte ANAC, byte NAC) { FLStatus status;
/* Perpare sector count */ if((vol.countsValid > virtualUnitNo ) && /* Has sector count */ (vol.virtualUnits[virtualUnitNo] != ANAND_NO_UNIT)) /* Was used */ { vol.physicalUnits[unitNo] = countOf(vol.virtualUnits[virtualUnitNo]); } else { vol.physicalUnits[unitNo] = 0; }
/* Vadim for ASAP policy*/ #ifdef ENVIRONMENT_VARS
if(NAC>=MAX_UNIT_CHAIN) NAC=MAX_UNIT_CHAIN-1; #endif /* ENVIRONMENT_VARS */
status = setUnitData(&vol,unitNo,virtualUnitNo,(ANANDUnitNo)vol.virtualUnits[virtualUnitNo],(byte)(ANAC+1),(byte)(NAC+1)); if (status != flOK) { markUnitBad(&vol,unitNo); } else { if(unitNo>vol.noOfUnits) return flGeneralFailure; vol.virtualUnits[virtualUnitNo]=unitNo; if(vol.freeUnits) vol.freeUnits--; } return status; }
/*----------------------------------------------------------------------*/ /* f o r m a t C h a i n */ /* */ /* Format all the units in a chain. Start from the last one and go */ /* backwards until unitNo is reached. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* unitNo : Format the chain from this unit onwards */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
static FLStatus formatChain(Bnand vol, ANANDUnitNo unitNo) { /* Erase the chain from end to start */ ANANDUnitNo chainBound; ANANDUnitNo unitToErase; FLStatus status;
for (chainBound=0;; chainBound++) { /* Find last unit in chain */ unitToErase = firstInChain(&vol,unitNo,NULL);
if((unitToErase == ANAND_BAD_CHAIN_UNIT ) || (chainBound >= DOUBLE_MAX_UNIT_CHAIN) ) return flGeneralFailure;
status = formatUnit(&vol,unitToErase,UNIT_TAILER_OFFSET); if(status != flOK) return status;
if (unitToErase == unitNo) break; /* Erased everything */ }
return flOK; }
/*----------------------------------------------------------------------*/ /* c r e a t e U n i t C o u n t */ /* */ /* Count the number of sectors in a unit that hold valid data. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* unitNo : Physical unit number */ /* */ /*----------------------------------------------------------------------*/
static void createUnitCount(Bnand vol, ANANDUnitNo unitNo) { register int i; SectorNo sectorNo; ANANDUnitNo physicalUnitNo = vol.virtualUnits[unitNo]; CardAddress sectorAddress; FLBoolean lastOK; /* Dummy variable */
if (physicalUnitNo == ANAND_NO_UNIT) return;
/* Get a count of the valid sector in this unit */ setUnitCount(physicalUnitNo,0);
sectorNo = (SectorNo)unitNo << vol.sectorsPerUnitBits; for (i = 0; i < vol.sectorsPerUnit; i++, sectorNo++) { sectorAddress = virtual2Physical(&vol,sectorNo,ANAND_UNASSIGNED_ADDRESS,&lastOK); /* Do not check for brocken chain. If one exists we will have a */ /* large sector count , which will delay folding. */ if (sectorAddress != ANAND_UNASSIGNED_ADDRESS) vol.physicalUnits[physicalUnitNo]++; } }
#if (defined(VERIFY_WRITE) || defined(VERIFY_VOLUME) || defined(VERIFY_ERASED_SECTOR))
/*----------------------------------------------------------------------*/ /* v e r i f y S e c t o r s */ /* */ /* Verify sectors for power failures simptoms and fix if neccesary. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* sectorCount : No of sectors to verify */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
FLStatus verifySectors(Bnand vol, dword sectorCount) { FLStatus status; word unitOffset; ANANDUnitNo virUnitNo; ANANDUnitNo unitNo; dword curRead; CardAddress sectorAddress; word j; byte chainBound,index; byte sectorFlags[2]; static FLBoolean scannedBlocks[MAX_SECTORS_PER_BLOCK]; byte FAR1* buffer;
if (vol.verifiedSectorNo >= vol.virtualSectors) return flOK;
/* Initialize variables */ buffer = flReadBackBufferOf(vol.socketNo);
if(buffer==NULL) { DEBUG_PRINT(("\nDebug : Can not verify sectors since no buffer was allocated\r\n")); return flOK; }
virUnitNo = (ANANDUnitNo)(vol.verifiedSectorNo >> vol.sectorsPerUnitBits); sectorCount = TFFSMIN(vol.virtualSectors - vol.verifiedSectorNo,sectorCount);
/* Force remapping of internal catched sector */ vol.flash->socket->remapped = TRUE; tffsset(inftlBuffer,0xff,sizeof(inftlBuffer)); /* Useds as FF'S buffer */
/* Run over required number of virtual sectors */ for (; sectorCount > 0 ; virUnitNo++ ,sectorCount -= curRead) { /* Calculate needed number of sector in this unit */ unitOffset = (word)((vol.verifiedSectorNo & vol.sectorsPerUnitMask) << SECTOR_SIZE_BITS); curRead = TFFSMIN(sectorCount,((1UL<<vol.unitSizeBits)-unitOffset)>>SECTOR_SIZE_BITS); unitNo = vol.virtualUnits[virUnitNo];
if(unitNo == ANAND_NO_UNIT) /* Unit is empty */ { vol.verifiedSectorNo += ((1<<vol.unitSizeBits)-unitOffset)>>SECTOR_SIZE_BITS; continue; }
/* Unit is not empty - initialize sector array */ if(unitOffset==0) tffsset(scannedBlocks,FALSE,sizeof(scannedBlocks));
for(chainBound=0;;chainBound++) { /* Go over the chain from newest unit to oldest */
if(chainBound!=0) /* Get next unit */ { unitNo = getPrevUnit(&vol,unitNo,virUnitNo); if((unitNo == ANAND_BAD_CHAIN_UNIT ) || (chainBound>=DOUBLE_MAX_UNIT_CHAIN) ) { DEBUG_PRINT(("\nverifySectors : Bad chain was found\r\n")); return flGeneralFailure; } if(unitNo == ANAND_NO_UNIT) break; }
/* Check required sectors of the unit - 2 sectors at a time */ sectorAddress = unitBaseAddress(vol,unitNo)+unitOffset; for (index=0;index<curRead;index+=2) { /* Read sector flags if needed
* * Note - getSectorFlags routine must not change the inftlBuffer */ if(WasSectorChecked(sectorAddress) == FALSE) sectorFlags[0] = getSectorFlags(&vol,sectorAddress); if(WasSectorChecked(sectorAddress+SECTOR_SIZE) == FALSE) sectorFlags[1] = getSectorFlags(&vol,sectorAddress+SECTOR_SIZE);
/* Try checking 2 sectors together */ if((sectorFlags[0]==sectorFlags[1] ) && (WasSectorChecked(sectorAddress+SECTOR_SIZE) == FALSE) && (WasSectorChecked(sectorAddress) == FALSE)) { /* Indenctical sector flags - sectors are checked together */ switch(sectorFlags[0]) { case SECTOR_FREE: status = vol.flash->read(vol.flash,sectorAddress,buffer,SECTOR_SIZE<<1,0); if(status != flOK) return status; if (tffscmp(inftlBuffer,buffer,sizeof(inftlBuffer))) { markAsIgnored(&vol,sectorAddress); createUnitCount(&vol,virUnitNo); } if (tffscmp(inftlBuffer,buffer+sizeof(inftlBuffer),sizeof(inftlBuffer))) { markAsIgnored(&vol,sectorAddress+SECTOR_SIZE); createUnitCount(&vol,virUnitNo); } break; case SECTOR_DELETED: case SECTOR_USED: status = vol.flash->read(vol.flash,sectorAddress,buffer,SECTOR_SIZE<<1,EDC); if(status == flDataError) { status = vol.flash->read(vol.flash,sectorAddress,buffer,SECTOR_SIZE,EDC); if(status != flOK) { markAsIgnored(&vol,sectorAddress); status = vol.flash->read(vol.flash,sectorAddress+SECTOR_SIZE,buffer,SECTOR_SIZE,EDC); if(status != flOK) { markAsIgnored(&vol,sectorAddress+SECTOR_SIZE); } else { MarkSectorAsChecked(sectorAddress+SECTOR_SIZE); } } else { MarkSectorAsChecked(sectorAddress); markAsIgnored(&vol,sectorAddress+SECTOR_SIZE); } createUnitCount(&vol,virUnitNo); break; } MarkSectorAsChecked(sectorAddress); default: /* SECTOR_IGNORE */ break; }/* Flag type case */ sectorAddress+=SECTOR_SIZE<<1; } /* Flags are indentical */ else /* Check each sectors individualy */ { for(j=0;j<2;j++,sectorAddress+=SECTOR_SIZE) { if(WasSectorChecked(sectorAddress) == TRUE) continue;
switch(sectorFlags[j]) { case SECTOR_FREE: status = vol.flash->read(vol.flash,sectorAddress,buffer,SECTOR_SIZE,0); if(status != flOK) return status; if (tffscmp(inftlBuffer,buffer,sizeof(inftlBuffer))) { markAsIgnored(&vol,sectorAddress); createUnitCount(&vol,virUnitNo); } break; case SECTOR_USED: case SECTOR_DELETED: status = vol.flash->read(vol.flash,sectorAddress,buffer,SECTOR_SIZE,EDC); if(status == flDataError) { markAsIgnored(&vol,sectorAddress); createUnitCount(&vol,virUnitNo); } MarkSectorAsChecked(sectorAddress); default: /* SECTOR_IGNORE || SECTOR_DELETED */ break; } /* Flag type */ } /* Try second sector */ } /* Flags are NOT indentical */ } /* Loop over all sector of unit or until required sectors */ } /* Loop over all units chains */ vol.verifiedSectorNo+=curRead; } /* Loop over all required sectors */ return flOK; }
/*----------------------------------------------------------------------*/ /* c h e c k V o l u m e */ /* */ /* Scanthe entire media for partialy written sectors. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
static FLStatus checkVolume(Bnand vol) { return verifySectors(&vol, 0xffffffffL); /* Force scan of entire media */ }
#endif /* VERIFY_WRITE || VERIFY_VOLUME || VERIFY_ERASED_SECTOR */
/*----------------------------------------------------------------------*/ /* f o l d U n i t */ /* */ /* Copy all the sectors that hold valid data in the chain to the last */ /* unit of the chain and erase the chain. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* virtualUnitNo : Virtual unit number of the first unit in */ /* chain. */ /* foldingFlag : One of the following flags: */ /* FL_NORMAL_FOLDING - Returns fail status if can not fold */ /* FL_FORCE_FOLDING - Force folding even if last sector is used */ /* FL_NOT_IN_PLACE - Fold into a unit with no erase mark */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
static FLStatus foldUnit(Bnand vol, ANANDUnitNo virtualUnitNo, int foldingFlag) { ANANDUnitNo unitNo = vol.virtualUnits[virtualUnitNo]; ANANDUnitNo prevUnitNo; SectorNo virtualSectorNo; CardAddress targetSectorAddress; int newSectorCount = 0; int i; byte ANAC,NAC,parityPerField; #ifdef ENVIRONMENT_VARS
ANANDUnitNo nextUnitNo = ANAND_NO_UNIT; CardAddress firstUnitAddress = ANAND_UNASSIGNED_ADDRESS; ANANDUnitNo firstUnit = ANAND_NO_UNIT; FLBoolean foldFirstOnly = (flPolicy[vol.socketNo] [vol.flash->socket->curPartition] == FL_COMPLETE_ASAP) ? TRUE : FALSE; #endif /* ENVIRONMENT_VARS */
#if (defined(VERIFY_WRITE) || defined(VERIFY_ERASED_SECTOR))
byte verifyWriteState = flVerifyWrite[vol.socketNo] [vol.flash->socket->curPartition]; #endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */
FLBoolean lastOK; byte chainBound; FLStatus status; CardAddress sourceSectorAddress;
/* Sanity checks */ if(unitNo>=vol.noOfUnits) /* Empty or INVALID */ { return flGeneralFailure; }
/* Internal statistics */ vol.unitsFolded++; /* Force remapping of internal catched sector */ vol.flash->socket->remapped = TRUE; virtualSectorNo = (SectorNo)virtualUnitNo << vol.sectorsPerUnitBits; targetSectorAddress = unitBaseAddress(vol,unitNo);
/* When verify write option is set to FL_OFF a very lazy check of the */ /* media is done every time a folding operation is issued */ #if (defined(VERIFY_WRITE) || defined(VERIFY_ERASED_SECTOR))
if(verifyWriteState == FL_OFF) { status = verifySectors(&vol,SECTORS_VERIFIED_PER_FOLDING); if(status != flOK) return status; vol.curSectorWrite = virtualSectorNo; /* Store virtual sector Number */ } #endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */
/* When policy is set to FL_COMPLETE_ASAP - folding is performed only on */ /* the oldest unit, so find oldest unit and store its location */ #ifdef ENVIRONMENT_VARS
if(foldFirstOnly == TRUE) { firstUnit = firstInChain(&vol,unitNo,&nextUnitNo); if(firstUnit == ANAND_BAD_CHAIN_UNIT) /* Error going along chain */ { return flGeneralFailure; } firstUnitAddress = unitBaseAddress(vol,firstUnit); } #endif
/* If chain has no valid sectors simply erase it. */ if((vol.countsValid>virtualUnitNo) && /* Unit valid sectors were counted */ (countOf(unitNo)==0)) /* No valid sectors in unit chain */ { #ifdef ENVIRONMENT_VARS
if((foldFirstOnly == TRUE) && (nextUnitNo != ANAND_NO_UNIT)) { /* Erase only first unit of chain */ vol.physicalUnits[nextUnitNo] = FIRST_IN_CHAIN; return formatChain(&vol,firstUnit); } else #endif /* ENVIRONMENT_VARS */
{ /* Erase chain completely */ vol.virtualUnits[virtualUnitNo] = ANAND_NO_UNIT; return formatChain(&vol,unitNo); } }
/* Single unit with valid sectors can not be folded */ if((vol.physicalUnits[unitNo]&FIRST_IN_CHAIN)==FIRST_IN_CHAIN) return flGeneralFailure;
/***********************************/ /* Copy all sectors to target unit */ /***********************************/
for (i = 0; i < vol.sectorsPerUnit; #ifdef ENVIRONMENT_VARS
firstUnitAddress += SECTOR_SIZE, #endif /* ENVIRONMENT_VARS */
#if (defined(VERIFY_WRITE) || defined(VERIFY_ERASED_SECTOR))
vol.curSectorWrite++, /* Update virtual sector Number */ #endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */
i++, virtualSectorNo++,targetSectorAddress += SECTOR_SIZE) { lastOK = TRUE; /* Set last sector of chain as valid */ sourceSectorAddress = virtual2Physical(&vol,virtualSectorNo,ANAND_UNASSIGNED_ADDRESS,&lastOK);
/* Check if sector is on the target unit. If so on some configuration we */ /* Verify the content and on some , we just assume the data is fine. */ if(sourceSectorAddress == targetSectorAddress) { #if (defined(VERIFY_WRITE) || defined(VERIFY_ERASED_SECTOR))
switch(verifyWriteState) { case FL_ON: /* Always verify */ break; case FL_OFF: /* Verify only if area was not scanned yet */ if(virtualSectorNo >= vol.verifiedSectorNo) break; default: /* FL_UPS - Never verify */ newSectorCount++; continue; } #else
newSectorCount++; continue; #endif /* VERIFY WRITE */
}
/* Read sector - Loop down the chain as long as there is an EDC error */ /* or infinit loop chain (chain bound) */ for (chainBound=0 ; (sourceSectorAddress != ANAND_UNASSIGNED_ADDRESS) ; chainBound++) { if(sourceSectorAddress == ANAND_BAD_CHAIN_ADDRESS) /* Could not follow chain */ return flGeneralFailure;
status = vol.flash->read(vol.flash,sourceSectorAddress, inftlBuffer,SECTOR_SIZE,EDC); if (status != flOK) { if (status == flDataError) { DEBUG_PRINT(("foldUnit : EDC error on folding\r\n")); if (chainBound >= MAX_UNIT_CHAIN) return status; sourceSectorAddress = virtual2Physical(&vol,virtualSectorNo,sourceSectorAddress,&lastOK); continue; } else { return status; } } break; } /* chain bound EDC error loop */
if (sourceSectorAddress == ANAND_UNASSIGNED_ADDRESS) /* Sector not found */ continue;
newSectorCount++; if (sourceSectorAddress == targetSectorAddress) /* Sector already exists */ continue;
/* Try and copy the relevant sector */
#ifdef ENVIRONMENT_VARS
/* On FL_COMPLETE_ASAP copy sector only if it is on the first unit */ if((flPolicy[vol.socketNo][vol.flash->socket->curPartition]==FL_COMPLETE_ASAP) && (sourceSectorAddress!=firstUnitAddress)) continue; #endif /* ENVIRONMENT_VARS */
if ((lastOK == FALSE) && (foldingFlag == FL_NORMAL_FOLDING)) { /* Last sector of the chain is already used */ return flCanNotFold; } status = writeAndCheck(&vol,targetSectorAddress,inftlBuffer,EDC,1); vol.parasiteWrites++;
/* On EDC error assume previous sector is empty */
switch (status) { case flOK: /* Success */ break;
case flWriteFault: /* Faild in verify write */ if (foldingFlag == FL_NORMAL_FOLDING) { DEBUG_PRINT(("foldUnit : Failed to write a sector while folding but will not force folding\r\n")); return flCanNotFold; } break;
default : /* Protection error or any other */ return status; } } /* Sector copy loop */
/***************************************************************/ /* After all sectors have been copied , erase the unused units */ /***************************************************************/
if(foldingFlag == FL_NOT_IN_PLACE) /* Add erase mark to validate unit */ { word eraseMark; dword eraseCount;
checkStatus(getUnitTailer(&vol,unitNo,&eraseMark,&eraseCount,UNIT_TAILER_OFFSET_2)); checkStatus(setUnitTailer(&vol,unitNo,eraseMark,eraseCount,UNIT_TAILER_OFFSET)); }
if (newSectorCount > 0) /* Some sectors remaining*/ { /* Set target unit in physical unit table as first in chain */ status = getUnitData(&vol,unitNo,&virtualUnitNo, &prevUnitNo,&ANAC,&NAC,&parityPerField); if(status != flOK) return status; /* check if unit is valid */ if(badParityResult(parityPerField)) return flGeneralFailure; #ifdef ENVIRONMENT_VARS
/* Erase only oldest unit */ if(flPolicy[vol.socketNo][vol.flash->socket->curPartition]==FL_COMPLETE_ASAP) { vol.physicalUnits[nextUnitNo] |= FIRST_IN_CHAIN; return formatUnit(&vol, firstUnit,UNIT_TAILER_OFFSET); } else #endif
{ vol.physicalUnits[unitNo] |= FIRST_IN_CHAIN; unitNo=prevUnitNo; /* erase all units in chain but the last one */ } } else { /* Erase chain completely */ vol.virtualUnits[virtualUnitNo] = ANAND_NO_UNIT; }
/* Erase source units */ return formatChain(&vol,unitNo); }
/*----------------------------------------------------------------------*/ /* f o l d B e s t C h a i n */ /* */ /* Find the best chain to fold and fold it.A good chain to fold is a */ /* long chain with a small number of sectors that hold valid data. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* unitNo : Receives the physical unit no. of the first */ /* unit in the chain that was folded. */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
static FLStatus foldBestChain(Bnand vol, ANANDUnitNo *unitNo) { word leastCount, longestChain, unitCount; ANANDUnitNo u, firstUnitNo, newVirtualUnitNo, prevUnitNo; ANANDUnitNo virtualUnitNo; ANANDUnitNo frozenCandidate = ANAND_NO_UNIT; ANANDUnitNo lazyMountCounter = 0; ANANDUnitNo newestUnit; FLStatus status = flOK; byte NAC,ANAC,parityPerField; int foldingTries;
/* Will exit when no more units are available or up to 10 times */ for (*unitNo = ANAND_NO_UNIT,foldingTries = 0 ; foldingTries < MAX_FOLDING_LOOP ; foldingTries++) { /*********************************************/ /* Pick unit to fold using huristic function */ /*********************************************/
virtualUnitNo = ANAND_NO_UNIT; longestChain = 1; /* Minimal chain length to fold == 1 */ leastCount = vol.sectorsPerUnit+1;
for (u = 0; u < vol.noOfVirtualUnits; u++) { firstUnitNo = vol.virtualUnits[u]; if(firstUnitNo == ANAND_NO_UNIT) /* Free unit */ continue;
if( !(isAvailable(firstUnitNo)) ) { /* Do not attempt to fold frozen unit. */ /* They will become unfrozen by the end of this routine. */ frozenCandidate = u; /* Remember for forced folding */ DEBUG_PRINT(("FoldBestChains : Skiped unavailable unit\r\n")); continue; }
/* Lazy mount - Make sure unit has a valid sector count */ if (vol.countsValid <= u) { if(lazyMountCounter>=MAX_CREATE_UNIT_COUNT) { /* If lazy mount takes too long , try and shorten it. */ if(virtualUnitNo!=ANAND_NO_UNIT) break; } createUnitCount(&vol,u); lazyMountCounter++; vol.countsValid = u + 1; }
unitCount = countOf(firstUnitNo); /* No of valid sectors */
/* If empty unit, use it. */ if(unitCount==0) { leastCount = unitCount; /* Store minimal sector count */ virtualUnitNo = u; /* Store virtual unit number */ break; }
if ((leastCount < unitCount) || /* Already found a better unit */ (vol.physicalUnits[firstUnitNo] & FIRST_IN_CHAIN)) /* 1 unit in chain */ continue;
/* Sector count is smaller or equal now check chains length */ status = getUnitData(&vol,firstUnitNo,&newVirtualUnitNo, &prevUnitNo,&ANAC,&NAC,&parityPerField); if(status != flOK) return status; /* check if unit is valid */ if((badParityResult(parityPerField)) || (newVirtualUnitNo != u)) return flGeneralFailure;
if((leastCount == unitCount) && /* If sector count is equal */ (longestChain >= NAC) ) /* Use chain length */ continue;
/* If we reached here the current unit is the best so far */ longestChain = NAC; /* Store maximal chain length */ leastCount = unitCount; /* Store minimal sector count */ virtualUnitNo = u; /* Store virtual unit number */ } /* End of unit huristic loop */
/****************************************/ /* Try folding the unit that was picked */ /****************************************/
if (virtualUnitNo != ANAND_NO_UNIT) /* Found a chain (more then 1 unit) */ {
*unitNo = firstInChain(&vol,vol.virtualUnits[virtualUnitNo],NULL); if(*unitNo==ANAND_BAD_CHAIN_UNIT) return flGeneralFailure;
status = foldUnit(&vol,virtualUnitNo,FL_NORMAL_FOLDING); switch(status) { case flOK: /* Try to make sure that there are at least 2 free units */ if(pVol->freeUnits < 2) { DEBUG_PRINT(("foldBestChains : Folding success, but need more units.\r\n")); continue; } break; case flCanNotFold: DEBUG_PRINT(("foldBestChains : Failed folding, mark as unavailable and try folding another\r\n")); setUnavail(vol.virtualUnits[virtualUnitNo]); *unitNo = ANAND_NO_UNIT; continue; default: DEBUG_PRINT(("foldBestChains : ERROR - Failed folding, with status diffrent then flCanNotFold.\r\n")); return status; } } else /* All remaining chains have single unit */ {
if (frozenCandidate == ANAND_NO_UNIT) /* Simply no chain larger then 1 */ { if(*unitNo == ANAND_NO_UNIT) /* Not even 1 unit was folded */ { DEBUG_PRINT(("foldBestChains : Failed - not enough units on flash.\r\n")); return flNotEnoughMemory; } else /* Aleady freed one unit - simply tried to get */ { return flOK; } } } break; } /* End folding tries loop */
/**************************************************************/ /* Unfreeze all frozen units , and fold them using freed unit */ /**************************************************************/
if (frozenCandidate != ANAND_NO_UNIT) /* At least one unit was frozen */ { DEBUG_PRINT(("foldBestChains : Found a frozen unit.\r\n")); /* find free unit to be appended */ if(*unitNo==ANAND_NO_UNIT) /* No unit was folded */ { DEBUG_PRINT(("foldBestChains : No free unit was found so far, so search for one.\r\n"));
if(vol.freeUnits) /* There are free units */ { ANANDUnitNo originalUnit = vol.roverUnit; do { if (++vol.roverUnit >= vol.noOfUnits) vol.roverUnit = 0;
if (vol.physicalUnits[vol.roverUnit] == ANAND_UNIT_FREE) { /* found a free unit, if not erased, */ if (formatUnit(&vol,vol.roverUnit,UNIT_TAILER_OFFSET_2) != flOK) continue; /* this unit is bad, find another */
*unitNo = vol.roverUnit; DEBUG_PRINT(("foldBestChains : Found a free unit for folding not in place.\r\n")); break; } }while (vol.roverUnit != originalUnit); } if(*unitNo==ANAND_NO_UNIT) /* No unit was found */ { /* Force folding of the last frozen unit - Loose data */ DEBUG_PRINT(("foldBestChains : Will force folding on Frozen candidate\r\n")); *unitNo = firstInChain(&vol,vol.virtualUnits[frozenCandidate],NULL); if(*unitNo==ANAND_BAD_CHAIN_UNIT) return flGeneralFailure; createUnitCount(&vol,frozenCandidate); status = foldUnit(&vol,frozenCandidate,FL_FORCE_FOLDING); if(status!= flOK) return status; } }
for (u=0 ; u < vol.noOfVirtualUnits ; u++) /* Loop over units and unfreeze */ { if(vol.virtualUnits[u] == ANAND_NO_UNIT) continue; if(isAvailable(vol.virtualUnits[u])) continue; createUnitCount(&vol,u); DEBUG_PRINT(("foldBestChains : Now free all frozen units.\r\n")); /* If unit is unavailable append newly found unit to chain and fold */ checkStatus(getUnitData(&vol,vol.virtualUnits[u],&newVirtualUnitNo, &prevUnitNo,&ANAC,&NAC,&parityPerField)); /* check if unit is valid */ if((badParityResult(parityPerField)) || (newVirtualUnitNo != u)) return flGeneralFailure; /* Remember the appended unit */ newestUnit = *unitNo; /* Remember oldest unit to return as new allocated unit */ *unitNo = firstInChain(&vol,vol.virtualUnits[u],NULL); /* Erase the erase mark and move erase count to offset 6K */ checkStatus(formatUnit(&vol,newestUnit,UNIT_TAILER_OFFSET_2)); checkStatus(assignUnit(&vol,newestUnit,u,ANAC,NAC)); status = foldUnit(&vol,u,FL_NOT_IN_PLACE); if(status != flOK) return status; } } return flOK; }
/*----------------------------------------------------------------------*/ /* a l l o c a t e U n i t */ /* */ /* Find a free unit to allocate, erase it if necessary. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* unitNo : Receives the physical number of the allocated */ /* unit */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
static FLStatus allocateUnit(Bnand vol, ANANDUnitNo *unitNo) { ANANDUnitNo originalUnit = vol.roverUnit; FLStatus status; dword eraseCount; word eraseMark;
if (vol.freeUnits < 2) { status = foldBestChain(&vol,unitNo); /* make free units by folding the best chain */ if(status != flNotEnoughMemory) return status;
DEBUG_PRINT(("Debug: Using last free unit of the media.\r\n")); }
do { if (++vol.roverUnit >= vol.noOfUnits) vol.roverUnit = 0;
if (vol.physicalUnits[vol.roverUnit] == ANAND_UNIT_FREE) { /* found a free unit, if not erased, */
status = getUnitTailer(&vol,vol.roverUnit,&eraseMark,&eraseCount,UNIT_TAILER_OFFSET); if(status != flOK) return status; if (eraseMark != ERASE_MARK) { if (formatUnit(&vol,vol.roverUnit,UNIT_TAILER_OFFSET) != flOK) continue; /* this unit is bad, find another */ } *unitNo = vol.roverUnit; return flOK; } } while (vol.roverUnit != originalUnit);
return foldBestChain(&vol,unitNo); /* make free units by folding the best chain */ }
/*----------------------------------------------------------------------*/ /* c h e c k F o l d i n g */ /* */ /* Check and complete a failed folding operation. */ /* */ /* Parameters: */ /* foldStatus : Pointer identifying drive */ /* virtualUnitNo : Virtual unit number of the first unit in */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
static FLStatus checkFolding(Bnand vol,FLStatus foldStatus,ANANDUnitNo virtualUnitNo) { ANANDUnitNo newVirtualUnitNo, prevUnitNo , dummyUnitNo; byte ANAC,NAC,parityPerField;
if(foldStatus == flCanNotFold) { DEBUG_PRINT(("checkFolding : Perform folding not in place\r\n"));
/* Get a new unit for extending the virtual unit */ checkStatus(allocateUnit(&vol,&dummyUnitNo)); checkStatus(getUnitData(&vol,vol.virtualUnits[virtualUnitNo],&newVirtualUnitNo, &prevUnitNo,&ANAC,&NAC,&parityPerField)); if((newVirtualUnitNo!=virtualUnitNo)|| (badParityResult(parityPerField))) { return flGeneralFailure; }
#ifdef ENVIRONMENT_VARS
/* Prepare NAC */ if(NAC>=MAX_UNIT_CHAIN) NAC=MAX_UNIT_CHAIN-1; #endif /* ENVIRONMENT_VARS */
checkStatus(assignUnit(&vol,dummyUnitNo,virtualUnitNo,ANAC,NAC)); foldStatus = foldUnit(&vol,virtualUnitNo,FL_FORCE_FOLDING); } return foldStatus; }
/*----------------------------------------------------------------------*/ /* a p p l y W e a r l e v e l i n g */ /* */ /* Try to extend the current vurtial chain in order to force static */ /* files wear leveling. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
static FLStatus applyWearleveling(Bnand vol) { ANANDUnitNo replacementUnitNo,newVirtualUnitNo; ANANDUnitNo startUnit,curUnit; FLStatus status; byte NAC,ANAC,parityPerField;
/* Increament wear leveling counter */ vol.wearLevel.currUnit++; if(vol.wearLevel.currUnit>=vol.noOfVirtualUnits) vol.wearLevel.currUnit=0;
/****************************************************/ /* Searching for a candidate virtual unit to extend */ /****************************************************/
startUnit = vol.wearLevel.currUnit; for(curUnit = startUnit ; vol.virtualUnits[curUnit] == ANAND_NO_UNIT;curUnit++) { if(curUnit>=vol.noOfVirtualUnits) curUnit = 0; if(startUnit == curUnit) break; }
if(startUnit == curUnit) /* the media is empty*/ { return flOK; }
vol.wearLevel.currUnit = curUnit; /* Store last leveld unit */
/***************************************************************************/ /* Now fold the virtual chain. (if a single unit chain add before folding) */ /***************************************************************************/
if(vol.physicalUnits[vol.virtualUnits[curUnit]] & FIRST_IN_CHAIN) /* chain is 1 unit long */ { /* This is a one unit chain, so we have to add a unit before folding */
status = allocateUnit(&vol,&replacementUnitNo); /* Find unit to append */ if(status != flOK) return status;
if(vol.virtualUnits[curUnit] == ANAND_NO_UNIT) { /* We did folding for the very same unit and now virtual unit is FREE */ vol.freeUnits--; return flOK; }
/* Get previous unit information (ANAC and NAC) */ status = getUnitData(&vol,vol.virtualUnits[curUnit],&newVirtualUnitNo, &replacementUnitNo, &ANAC,&NAC,&parityPerField); if(status != flOK) return status;
if(badParityResult(parityPerField)) /* check if unit is valid */ return flGeneralFailure;
if(newVirtualUnitNo != curUnit) /* Problem with RAM tables */ { return flGeneralFailure; }
status = assignUnit(&vol,replacementUnitNo,curUnit,ANAC,NAC); if(status != flOK) return status; } /* Perform folding and verify operation */ status = foldUnit(&vol,curUnit,FL_NORMAL_FOLDING); if (status != flOK) return checkFolding(&vol,status,curUnit); return flOK; }
#endif /* FL_READ_ONLY */
/*----------------------------------------------------------------------*/ /* m a p S e c t o r */ /* */ /* Maps and returns location of a given sector no. */ /* NOTE: This function is used in place of a read-sector operation. */ /* */ /* A one-sector cache is maintained to save on map operations. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* sectorNo : Sector no. to read */ /* physAddress : Optional pointer to receive sector address */ /* */ /* Returns: */ /* Pointer to physical sector location. NULL returned if sector */ /* does not exist. */ /*----------------------------------------------------------------------*/
static const void FAR0 *mapSector(Bnand vol, SectorNo sectorNo, CardAddress *physAddress) { FLStatus status; FLBoolean lastOK; /* Dummy variable */ byte chainBound;
if ((sectorNo != vol.mappedSectorNo) || (vol.flash->socket->remapped) || (vol.buffer->owner == &vol) ) { vol.flash->socket->remapped = TRUE; vol.mappedSector = NULL; vol.mappedSectorAddress = ANAND_UNASSIGNED_ADDRESS; if (sectorNo < vol.virtualSectors) /* While EDC error on sector read */ { for (chainBound=0 ; (chainBound < DOUBLE_MAX_UNIT_CHAIN) ; chainBound++) { vol.mappedSectorAddress = virtual2Physical(&vol,sectorNo,vol.mappedSectorAddress,&lastOK); switch(vol.mappedSectorAddress) { case ANAND_UNASSIGNED_ADDRESS: case ANAND_BAD_CHAIN_ADDRESS: vol.mappedSector = NULL; /* no such sector */ break;
default: vol.mappedSector = inftlBuffer; status = vol.flash->read(vol.flash,vol.mappedSectorAddress,inftlBuffer,SECTOR_SIZE,EDC); if (status == flOK) break; if (status != flDataError) return dataErrorToken; continue; } break; } if (chainBound >= DOUBLE_MAX_UNIT_CHAIN) return dataErrorToken;
/* Store sector for next mapping operation */ vol.mappedSectorNo = sectorNo; /* Sector number */ vol.buffer->owner = &vol; /* Partition */ vol.flash->socket->remapped = FALSE; /* Valid */ } }
if (physAddress) *physAddress = vol.mappedSectorAddress;
return vol.mappedSector; }
/*----------------------------------------------------------------------*/ /* r e a d 2 S e c t o r s */ /* */ /* read content of a set of consecutive sectors. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* sectorNo : Sector no. to read */ /* dest : pointer to buffer to read */ /* sectorCount : # of sectors to read ( up to 2 ) */ /* */ /* Returns: */ /* status of the read operaton */ /*----------------------------------------------------------------------*/
static FLStatus read2Sectors(Bnand vol, SectorNo sectorNo, byte FAR1 *dest, SectorNo sectorCount)
{ CardAddress mappedSectorAddress[2]; FLStatus status = flOK; FLStatus retStatus = flOK; word i; byte chainBound; FLBoolean lastOK; /* Dummy variable */
if ((sectorNo+sectorCount-1) >= vol.virtualSectors) { return flSectorNotFound; /* Out of bounds */ } else { /* find physical location of the 2 sectors */ for(i=0;i<sectorCount;i++) { mappedSectorAddress[i] = virtual2Physical(&vol,sectorNo+i, ANAND_UNASSIGNED_ADDRESS,&lastOK); /* If chain is brocken report sector not found */ if(mappedSectorAddress[i] == ANAND_BAD_CHAIN_ADDRESS) mappedSectorAddress[i] = ANAND_UNASSIGNED_ADDRESS; }
/* When possible read the 2 sectors together */ if((sectorCount==2)&& ( mappedSectorAddress[1] > mappedSectorAddress[0] ) && ((mappedSectorAddress[1] - mappedSectorAddress[0])==SECTOR_SIZE) && ( mappedSectorAddress[0] != ANAND_UNASSIGNED_ADDRESS ) && ( mappedSectorAddress[1] != ANAND_UNASSIGNED_ADDRESS )) { if (vol.flash->read(vol.flash,mappedSectorAddress[0], dest,SECTOR_SIZE<<1,EDC) == flOK) { vol.sectorsRead+=2; return flOK; } }
/* Sectors are not together */ for (i=0;i<sectorCount;i++,dest = (byte FAR1 *)flAddLongToFarPointer(dest,SECTOR_SIZE)) { /* While EDC error on sector read - keep reading from older unit */ for (chainBound=0 ; (chainBound < DOUBLE_MAX_UNIT_CHAIN) ; chainBound++) { if (mappedSectorAddress[i] == ANAND_UNASSIGNED_ADDRESS) { tffsset(dest,0,SECTOR_SIZE); retStatus = flSectorNotFound; break; } else { status=vol.flash->read(vol.flash,mappedSectorAddress[i], dest,SECTOR_SIZE,EDC); vol.sectorsRead++; if (status == flOK) { break; } else if (status == flDataError) { mappedSectorAddress[i] = virtual2Physical(&vol,sectorNo+i,mappedSectorAddress[i],&lastOK); /* If chain is brocken report sector not found */ if(mappedSectorAddress[i]==ANAND_BAD_CHAIN_ADDRESS) mappedSectorAddress[i] = ANAND_UNASSIGNED_ADDRESS; } else { return status; } } } if (chainBound >= DOUBLE_MAX_UNIT_CHAIN) return status; } } return retStatus; }
/*----------------------------------------------------------------------*/ /* r e a d S e c t o r s */ /* */ /* Read content of a set of consecutive sectors. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* sectorNo : Sector no. to read */ /* dest : pointer to buffer to read */ /* sectorCount : # of sectors to read */ /* */ /* Returns: */ /* status of the read operaton */ /* */ /*----------------------------------------------------------------------*/
static FLStatus readSectors(Bnand vol, SectorNo sectorNo, void FAR1 *dest, SectorNo sectorCount) { byte FAR1* curDest; SectorNo lastSector = 0; FLStatus retStatus = flOK; FLStatus status; SectorNo i;
#ifdef SCATTER_GATHER
curDest = *(byte FAR1 **)dest; #else
curDest = (byte FAR1 *)dest; #endif /* SCATTER_GATHER */
if ((sectorNo+sectorCount) > vol.virtualSectors) { return flSectorNotFound; /* Out of bounds */ }
/****************************************************************/ /* Read first sector if it's only one or it has odd address */ /****************************************************************/ if(((sectorNo & 1)!=0)||(sectorCount==1)) { status=read2Sectors(&vol, sectorNo, curDest,1); if (status != flOK) { if (status == flSectorNotFound) { retStatus = flSectorNotFound; } else { return status; } } if(sectorCount==1) return status; sectorNo++; sectorCount--; #ifdef SCATTER_GATHER
dest = (byte FAR1 **)dest+1; #else
dest = flAddLongToFarPointer(dest,SECTOR_SIZE); #endif /* SCATTER_GATHER */
}
if(((sectorNo+sectorCount-1) & 1)==0) /* keep last sector if it has odd address*/ lastSector=sectorNo+sectorCount-1;
/*****************************************/ /* Read pairs of consequtive sectors */ /*****************************************/
#ifdef SCATTER_GATHER
curDest = multiSectorBuf[vol.socketNo]; #else
curDest = (byte FAR1 *)dest; #endif /* SCATTER_GATHER */
for(i=0;i<((sectorCount>>1)<<1);i+=2) /* read pair of sectors*/ { status=read2Sectors(&vol,sectorNo+i, curDest,2); if (status != flOK) { if (status == flSectorNotFound) { retStatus = flSectorNotFound; } else { return status; } } #ifdef SCATTER_GATHER
/* copy from temporary buffer to user scattered buffers */ tffscpy(*(byte FAR1 **)dest,curDest,SECTOR_SIZE); tffscpy(*((byte FAR1 **)dest+1),&(curDest[SECTOR_SIZE]),SECTOR_SIZE); dest = (byte FAR1 **)dest+2; #else
curDest=(byte FAR1 *)flAddLongToFarPointer(curDest,(SECTOR_SIZE<<1)); #endif /* SCATTER_GATHER */
}
/********************************/ /* Read last uneven sectors */ /********************************/
#ifdef SCATTER_GATHER
curDest = *(byte FAR1 **)dest; #endif /* SCATTER_GATHER */
if(lastSector!=0) /* read last sector */ { checkStatus(read2Sectors(&vol,lastSector, curDest,1)); } return retStatus; }
#ifndef FL_READ_ONLY
/*----------------------------------------------------------------------*/ /* a l l o c a t e A n d W r i t e S e c t o r s */ /* */ /* Write to sectorNo. if necessary, allocate a free sector first. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* sectorNo : Virtual sector no. to write */ /* fromAddress : Address of sector data. */ /* howMany : Number of sectors to write. */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
static FLStatus allocateAndWriteSectors(Bnand vol, SectorNo sectorNo, void FAR1 *fromAddress,word howMany) { ANANDUnitNo newVirtualUnitNo, newPrevUnitNo; ANANDUnitNo virtualUnitNo = (ANANDUnitNo)(sectorNo >> vol.sectorsPerUnitBits); ANANDUnitNo lastUnitNo = vol.virtualUnits[virtualUnitNo]; ANANDUnitNo unitNo,prevUnitNo; FLStatus status; byte ANAC,NAC,parityPerField; word unitOffset = (word)((sectorNo & vol.sectorsPerUnitMask) << SECTOR_SIZE_BITS); word chainBound = 0; word foundSoFar = 0; word newSect = howMany; byte sectorFlags; FLBoolean firstUnitFound = FALSE; FLBoolean secondUnitFound = FALSE; FLBoolean noneFreeFound = FALSE; ANANDUnitNo commonPrevUnitNo = ANAND_NO_UNIT; ANANDUnitNo commonUnitNo = lastUnitNo;
/************************************/ /* Find a unit to write this sector */ /************************************/
unitNo = lastUnitNo; /* newest unit that either sectors is not FREE */ prevUnitNo = ANAND_NO_UNIT; /* oldest unit with place for required sectors */
while (unitNo != ANAND_NO_UNIT) { if(firstUnitFound == FALSE) { sectorFlags = getSectorFlags(&vol,unitBaseAddress(vol,unitNo) + unitOffset);
switch(sectorFlags) { case SECTOR_USED: newSect--; /* Sector exists - do not increament counter */ case SECTOR_DELETED: foundSoFar++; firstUnitFound = TRUE; case SECTOR_IGNORE: if(noneFreeFound == FALSE) /* Store none free space */ { commonPrevUnitNo = prevUnitNo; commonUnitNo = unitNo; noneFreeFound = TRUE; } default: break; } } if(howMany==2) { if(secondUnitFound == FALSE) { sectorFlags = getSectorFlags(&vol,unitBaseAddress(vol,unitNo) + unitOffset+512);
switch(sectorFlags) { case SECTOR_USED: newSect--; /* Sector exists - do not increament counter */ case SECTOR_DELETED: foundSoFar++; secondUnitFound = TRUE; case SECTOR_IGNORE: if(noneFreeFound == FALSE) /* Store none free space */ { commonPrevUnitNo = prevUnitNo; commonUnitNo = unitNo; noneFreeFound = TRUE; } default: break; } } }
if(foundSoFar == howMany) /* Both sectors have been found */ break;
/* Both sectors are FREE */ prevUnitNo = unitNo; unitNo = getPrevUnit(&vol,unitNo,virtualUnitNo); if(unitNo == ANAND_BAD_CHAIN_UNIT) return flGeneralFailure; chainBound++; if(chainBound >= DOUBLE_MAX_UNIT_CHAIN) return flGeneralFailure; } /* End of - go over chain while */ if(noneFreeFound == TRUE) { prevUnitNo = commonPrevUnitNo; /* Common free unit for both sectors */ unitNo = commonUnitNo; /* First unit with wither used sector */ }
if ((prevUnitNo == ANAND_NO_UNIT)) /* No place to write sectors */ { if(unitNo!=ANAND_NO_UNIT) /* Unit already exists */ { status = getUnitData(&vol,unitNo,&newVirtualUnitNo, &newPrevUnitNo,&ANAC,&NAC,&parityPerField); if (status != flOK) return status;
if(badParityResult(parityPerField)) /* check if unit is valid */ return flGeneralFailure;
/* Check if chain length is graeter then allowed, but remember */ /* that the first unit of the chain has invalid NAC. */ if ((NAC>=MAX_UNIT_CHAIN) && ((vol.physicalUnits[unitNo]&FIRST_IN_CHAIN)!=FIRST_IN_CHAIN)) { status = foldUnit(&vol,virtualUnitNo,FL_NORMAL_FOLDING); if (status != flOK) { status = checkFolding(&vol,status,virtualUnitNo); if (status != flOK) return status; } } } status = allocateUnit(&vol,&prevUnitNo); if (status != flOK) return status;
unitNo = vol.virtualUnits[virtualUnitNo];
if(unitNo==ANAND_NO_UNIT) /* Free virtual unit */ { /* New Virtual unit. reinitialize NAC,ANAC and sector count */ ANAC=NAC=0; /* Force FIRST_IN_CHAIN and sector count to 0. it must be done */ /* after assignUnit, so that assign unit will not change it. */ unitNo=ANAND_NO_UNIT; } else /* Read unit data to set ANAC , NAC and sector count */ { status = getUnitData(&vol,unitNo,&newVirtualUnitNo, &newPrevUnitNo, &ANAC,&NAC,&parityPerField); if (status != flOK) return status;
/* Check if unit is valid */ if((badParityResult(parityPerField)) || /* Bad unit data */ (newVirtualUnitNo!=virtualUnitNo)) /* Bad virtual unit no */ return flGeneralFailure;
if(vol.physicalUnits[unitNo]&FIRST_IN_CHAIN) NAC=1; }
status = assignUnit(&vol,prevUnitNo,virtualUnitNo,ANAC,NAC); if (status != flOK) return status;
if(unitNo==ANAND_NO_UNIT) /* First physical unit of chain */ { vol.physicalUnits[prevUnitNo]=FIRST_IN_CHAIN; } lastUnitNo = vol.virtualUnits[virtualUnitNo]; unitNo=prevUnitNo; } else { unitNo=prevUnitNo; }
/***********************************************/ /* Area has been allocated , now write sectors */ /***********************************************/
#if (defined(VERIFY_WRITE) || defined(VERIFY_ERASED_SECTOR))
vol.curSectorWrite = sectorNo; /* Store virtual sector Number */ #endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */
status = writeAndCheck(&vol,unitBaseAddress(vol,unitNo) + unitOffset,fromAddress,EDC,howMany); if (status != flOK) return status;
/* Take care of sector count */ if (vol.countsValid > virtualUnitNo) { lastUnitNo=vol.virtualUnits[virtualUnitNo];
if (countOf(lastUnitNo) + newSect <= UNIT_MAX_COUNT) { vol.physicalUnits[lastUnitNo]+=newSect; /* Increment block count */ } else /* Should never happen , but sector count is not correct */ { return flGeneralFailure; } } return flOK; }
/*----------------------------------------------------------------------*/ /* w r i t e 2 S e c t o r s */ /* */ /* Writes up to 2 consecutive sector. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* sectorNo : Virtual sector no. to write */ /* fromAddress : Data to write */ /* sectorCount : No of sectors to write */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
static FLStatus write2Sectors(Bnand vol, SectorNo sectorNo, void FAR1 *fromAddress,word sectorCount) { FLStatus status; byte i;
if ((sectorNo+sectorCount-1) >= vol.virtualSectors) return flSectorNotFound;
/* Check if cached sector is still valid */ if ((sectorNo == vol.mappedSectorNo ) && (sectorNo+sectorCount == vol.mappedSectorNo + sectorCount - 1) ) vol.flash->socket->remapped = TRUE;
#ifdef ENVIRONMENT_VARS
if(flPolicy[vol.socketNo][vol.flash->socket->curPartition]==FL_DEFAULT_POLICY) #endif /* ENVIRONMENT_VARS */
if((vol.wearLevel.currUnit!=ANAND_NO_UNIT)) { vol.wearLevel.alarm++; if(vol.wearLevel.alarm>=WLnow) { vol.wearLevel.alarm = 0; status = applyWearleveling(&vol); if (status != flOK) return status; } } status = flWriteFault; vol.sectorsWritten += sectorCount;
/* Try writing the sector up to 4 times before reporting an error */ for (i = 0; (i < 4) && (status == flWriteFault); i++) status = allocateAndWriteSectors(&vol,sectorNo,fromAddress,sectorCount);
return status; }
/*----------------------------------------------------------------------*/ /* w r i t e S e c t o r */ /* */ /* Writes a sector. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* sectorNo : Virtual sector no. to write */ /* fromAddress : Data to write */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
static FLStatus writeSector(Bnand vol, SectorNo sectorNo, void FAR1 *fromAddress) { checkStatus(discardQuickMountInfo(&vol)); return write2Sectors( &vol, sectorNo, fromAddress, ((SectorNo)1) ); }
#ifdef ENVIRONMENT_VARS
/*----------------------------------------------------------------------*/ /* w r i t e F u l l U n i t */ /* */ /* Write set of consecutive sectors that occupies a full unit. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* sectorNo : Sector no. to write */ /* fromAddress : Pointer to buffer to write */ /* */ /* Returns: */ /* status of the write operaton */ /* */ /*----------------------------------------------------------------------*/
static FLStatus writeFullUnit(Bnand vol, SectorNo sectorNo, void FAR1 *fromAddress) { ANANDUnitNo virtualUnitNo = (ANANDUnitNo)(sectorNo >> vol.sectorsPerUnitBits); ANANDUnitNo lastUnitNo,newVirtualUnitNo; ANANDUnitNo unitNo,prevUnitNo; FLStatus status; byte ANAC,NAC,parityPerField;
if(virtualUnitNo==ANAND_NO_UNIT) /* Sanity check */ return flGeneralFailure;
status = allocateUnit(&vol,&unitNo); if(status != flOK) return status;
lastUnitNo = vol.virtualUnits[virtualUnitNo]; if(lastUnitNo == ANAND_NO_UNIT) { /* First time we write to this VU */ ANAC = NAC = 0; } else { status = getUnitData(&vol,lastUnitNo,&newVirtualUnitNo, &prevUnitNo,&ANAC,&NAC,&parityPerField); if(status != flOK) return status;
/* check if unit is valid */ if((badParityResult(parityPerField) ) || (virtualUnitNo != newVirtualUnitNo) ) { DEBUG_PRINT(("\nwriteFullUnit: Found a brocken chain\r\n")); return flGeneralFailure; } /* Update NAC */ if(vol.physicalUnits[lastUnitNo]&FIRST_IN_CHAIN) { NAC = 1; /* One unit chain , set proper NAC */ } else { if(NAC>=MAX_UNIT_CHAIN) { status = foldUnit(&vol,virtualUnitNo,FL_NORMAL_FOLDING); if (status != flOK) { status = checkFolding(&vol,status,virtualUnitNo); if(status != flOK) return status; } if(vol.virtualUnits[virtualUnitNo]==ANAND_NO_UNIT) { /* Unit had no used sectors and was erased */ lastUnitNo = ANAND_NO_UNIT; ANAC = NAC = 0; } else /* After folding must be 1 unit chain */ { NAC = 1; } } } } status = assignUnit(&vol,unitNo,virtualUnitNo,ANAC,NAC); if(status != flOK) return status;
setUnitCount(unitNo,vol.sectorsPerUnit);
if(lastUnitNo==ANAND_NO_UNIT) vol.physicalUnits[unitNo] |= FIRST_IN_CHAIN; vol.virtualUnits[virtualUnitNo] = unitNo;
#if (defined(VERIFY_WRITE) || defined(VERIFY_ERASED_SECTOR))
/* Turn verify write off since this is a new unit */ vol.curSectorWrite = 0; /* Store virtual sector Number */ #endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */
#ifdef SCATTER_GATHER
/* Write in pairs (NAC is used as a simple index) */ for (NAC=0;NAC<vol.sectorsPerUnit;NAC+=2) { tffscpy(multiSectorBuf[vol.socketNo], *((byte FAR1 **)fromAddress+NAC),SECTOR_SIZE); tffscpy(multiSectorBuf[vol.socketNo]+SECTOR_SIZE, *((byte FAR1 **)fromAddress+NAC+1),SECTOR_SIZE); status = writeAndCheck(&vol,unitBaseAddress(vol,unitNo)+ (NAC<<(SECTOR_SIZE_BITS+1)), multiSectorBuf[vol.socketNo],EDC,2); if(status != flOK) return status; } return status; #else
return writeAndCheck(&vol,unitBaseAddress(vol,unitNo),fromAddress, EDC,vol.sectorsPerUnit); #endif /* SCATTER_GATHER */
}
#endif /* ENVIRONMENT_VARS */
/*----------------------------------------------------------------------*/ /* w r i t e M u l t i S e c t o r */ /* */ /* Write set of consecutive sectors */ /* */ /* Note : Special care was taken for SCATTER_GATHER option. In this */ /* user buffer is given as an array of 512 bytes buffers and not */ /* as a single large (sectors*512 Bytes) array. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* sectorNo : Sector no. to write */ /* fromAddress : pointer to buffer to write */ /* sectorCount : # of sectors to write */ /* */ /* Returns: */ /* status of the write operaton */ /* */ /*----------------------------------------------------------------------*/
static FLStatus writeMultiSector(Bnand vol, SectorNo sectorNo, void FAR1 *fromAddress,SectorNo sectorCount) { byte FAR1* curAddr = (byte FAR1 *)fromAddress; SectorNo lastSector; SectorNo i; FLStatus status = flOK;
/* Check if sector is in virtual size boundries */ if (sectorNo + sectorCount > vol.virtualSectors) return flSectorNotFound;
checkStatus(discardQuickMountInfo(&vol));
/*****************************************************/ /* Start from an odd address or only a one sector */ /*****************************************************/
if(((sectorNo & 1)!=0)||(sectorCount==1)) { status=write2Sectors(&vol, sectorNo, #ifdef SCATTER_GATHER
*(char FAR1 **) #endif /* SCATTER_GATHER */
fromAddress,1);
if((sectorCount == 1 ) || /* finished (only 1 sector) */ (status != flOK) ) /* or operation failed */ return status;
sectorNo++; sectorCount--; /* Increament user buffer */ #ifdef SCATTER_GATHER
fromAddress=(void FAR1 *)((char FAR1 **)fromAddress+1); #else
fromAddress=(byte FAR1 *)flAddLongToFarPointer(fromAddress,SECTOR_SIZE); #endif /* SCATTER_GATHER */
}
/************************************************/ /* Write all the sequantial pair of sectors. */ /************************************************/
if(((sectorNo+sectorCount-1) & 1)==0) { /* Keep last sector since it can not be written as a pair */ lastSector = sectorNo+sectorCount-1; } else { lastSector = 0; /* All sectors can be written in pairs */ }
sectorCount = (sectorCount>>1)<<1; /* round down to even no' of sectors */
for(i=0;i<sectorCount;i+=2) /* write pair of sectors*/ { #ifdef SCATTER_GATHER
curAddr = (void FAR1 *)((byte FAR1 **)fromAddress+i); #else
curAddr=(byte FAR1 *)flAddLongToFarPointer(fromAddress,SECTOR_SIZE*i); #endif /* SCATTER_GATHER */
#ifdef ENVIRONMENT_VARS
if((flPolicy[vol.socketNo][vol.flash->socket->curPartition]==FL_COMPLETE_ASAP) && /* sector is unit aligned */ (((sectorNo + i ) & vol.sectorsPerUnitMask) == 0) && /* enough sectors to fill a unit */ ( (sectorCount - i ) >= vol.sectorsPerUnit )) { status = writeFullUnit(&vol,sectorNo+i,curAddr); if(status != flOK) return status; i += vol.sectorsPerUnit-2; continue; } #endif /* ENVIRONMENT_VARS */
#ifdef SCATTER_GATHER
/* Copy scattered buffers to internal 1k buffer */ tffscpy(multiSectorBuf[vol.socketNo],*((char FAR1 **)curAddr),SECTOR_SIZE); tffscpy(multiSectorBuf[vol.socketNo]+SECTOR_SIZE,*((char FAR1 **)curAddr+1),SECTOR_SIZE); curAddr = multiSectorBuf[vol.socketNo]; #endif /* SCATTER_GATHER */
status = write2Sectors(&vol,sectorNo+i, curAddr,2); if(status != flOK) return status; }
/*********************************************/ /* Write the last sector (not full page). */ /*********************************************/
if(lastSector!=0) /* write last sector */ { #ifdef SCATTER_GATHER
fromAddress = (void FAR1 *)((byte FAR1 **)fromAddress+i); #else
fromAddress = (void FAR1 *)flAddLongToFarPointer(fromAddress,SECTOR_SIZE*i); #endif /* SCATTER_GATHER */
status=write2Sectors(&vol, lastSector, #ifdef SCATTER_GATHER
*(char FAR1 **) #endif /* SCATTER_GATHER */
fromAddress,1); } return status; }
/*----------------------------------------------------------------------*/ /* d e l e t e S e c t o r */ /* */ /* Marks contiguous sectors as deleted. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* sectorNo : First sector no. to delete */ /* noOfSectors : No. of sectors to delete */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
static FLStatus deleteSector(Bnand vol, SectorNo sectorNo, SectorNo noOfSectors) { CardAddress sectorAddress; SectorNo iSector; ANANDUnitNo virtualUnitNo; ANANDUnitNo currUnitNo; byte sectorFlags[2] = {SECTOR_DELETED,SECTOR_DELETED}; FLBoolean lastOK; /* Dummy variable */
if (sectorNo + noOfSectors > vol.virtualSectors) return flSectorNotFound;
checkStatus(discardQuickMountInfo(&vol));
for (iSector = 0; iSector < noOfSectors; iSector++, sectorNo++, vol.sectorsDeleted++) { sectorAddress = virtual2Physical(&vol,sectorNo,ANAND_UNASSIGNED_ADDRESS,&lastOK); switch(sectorAddress) { case ANAND_UNASSIGNED_ADDRESS: continue;
case ANAND_BAD_CHAIN_ADDRESS: return flGeneralFailure;
default: virtualUnitNo = (ANANDUnitNo)(sectorNo >> vol.sectorsPerUnitBits); #ifdef NFTL_CACHE
setSectorFlagsCache(&vol, sectorAddress, SECTOR_DELETED); #ifdef ENVIRONMENT_VARS
if (((flMarkDeleteOnFlash == FL_ON) && (flPolicy[vol.socketNo][vol.flash->socket->curPartition] != FL_COMPLETE_ASAP)) || (vol.scache == NULL)) #endif /* ENVIRONMENT_VARS */
#endif /* NFTL_CACHE */
{
#ifndef NT5PORT
checkStatus(vol.flash->write(vol.flash, sectorAddress + SECTOR_DATA_OFFSET, §orFlags, sizeof sectorFlags, EXTRA)); #else /*NT5PORT*/
vol.flash->write(vol.flash, sectorAddress + SECTOR_DATA_OFFSET, sectorFlags, sizeof sectorFlags, EXTRA); #endif /*NT5PORT*/
} currUnitNo = vol.virtualUnits[virtualUnitNo];
if (vol.countsValid > virtualUnitNo) { if (countOf(currUnitNo) > 0) { vol.physicalUnits[currUnitNo]--; /* Decrement block count */ } else { DEBUG_PRINT(("delete sector : Unit does not apear to have any sectors\r\n")); return flGeneralFailure; } } } /* End sectorAddress switch */ } /* End delete sector loop */
return flOK; }
#ifdef DEFRAGMENT_VOLUME
/*----------------------------------------------------------------------*/ /* d e f r a g m e n t */ /* */ /* Performs unit allocations to arrange a minimum number of writable */ /* sectors. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* sectorsNeeded : Minimum required sectors */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
static FLStatus defragment(Bnand vol, long FAR2 *sectorsNeeded) { ANANDUnitNo dummyUnitNo; ANANDUnitNo firstFreeUnit = ANAND_NO_UNIT; FLBoolean firstRound = TRUE; FLStatus status = flOK;
checkStatus(discardQuickMountInfo(&vol));
if( (*sectorsNeeded) == -1 ) /* fold single best chain */ { status = foldBestChain(&vol,&dummyUnitNo); if( (status != flOK) && (vol.freeUnits == 0) ) return status; *sectorsNeeded = (long)vol.freeUnits << vol.sectorsPerUnitBits; return flOK; }
/* Perform folding until the required number of sectors is achived */
while (((long)vol.freeUnits << vol.sectorsPerUnitBits) < *sectorsNeeded) { status = foldBestChain(&vol,&dummyUnitNo); /* make more free units */ if(status != flOK) break; }
*sectorsNeeded = (long)vol.freeUnits << vol.sectorsPerUnitBits;
return status; }
#endif /* DEFRAGMENT */
#endif /* FL_READ_ONLY */
/*----------------------------------------------------------------------*/ /* t l S e t B u s y */ /* */ /* Notifies the start and end of a file-system operation. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* state : FL_ON (1) = operation entry */ /* FL_OFF(0) = operation exit */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
static FLStatus tlSetBusy(Bnand vol, FLBoolean state) { return flOK; }
/*----------------------------------------------------------------------*/ /* s e c t o r s I n V o l u m e */ /* */ /* Gets the total number of sectors in the volume */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* */ /* Returns: */ /* Number of sectors in the volume */ /*----------------------------------------------------------------------*/
static SectorNo sectorsInVolume(Bnand vol) { return vol.virtualSectors; }
/*----------------------------------------------------------------------*/ /* p u t G e t B u f f e r */ /* */ /* Write \ Read a buffer to the flash from a specific flash offset */ /* while making sure only good units are used. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* length : Size of the buffer (always full pages) */ /* bufferPtr : Data buffer */ /* flashAddr : Physcial address on the flash */ /* bbt : Buffer containing BBT of the quick mount area */ /* readFlag : TRUE - read data , FLASE - write data */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /* bufferPtr : Increamented data buffer */ /* flashAddr : Increamented physcial address on the flash */ /*----------------------------------------------------------------------*/
#ifdef QUICK_MOUNT_FEATURE
static FLStatus putGetBuffer(Bnand vol, dword length, byte FAR1** bufferPtr, CardAddress* flashAddr, byte* bbt, FLBoolean readFlag) { FLStatus status; word writeNow = 0;
while (length > 0) { writeNow = (word)TFFSMIN(length,(((*flashAddr >> vol.unitSizeBits)+1) << vol.unitSizeBits) - *flashAddr); if (readFlag) { status = vol.flash->read(vol.flash,*flashAddr,*bufferPtr, (dword)writeNow,EDC); } #ifndef FL_READ_ONLY
else { status = vol.flash->write(vol.flash,*flashAddr,*bufferPtr, (dword)writeNow,EDC); } #endif /* FL_READ_ONLY */
if(status != flOK) return status;
length -= writeNow; *flashAddr += writeNow; *bufferPtr = BYTE_ADD_FAR(*bufferPtr,writeNow); NextGoodUnit(*flashAddr,bbt); } return flOK; }
/*----------------------------------------------------------------------*/ /* q u i c k M o u n t D a t a */ /* */ /* Saves or restores the quick mount data to and from the flash */ /* */ /* Note : the data is saved according to the mechines architecture. Big */ /* Indien is not converted into little indien like the rest of */ /* of INFTL flash data structure */ /* */ /* The following will be saved : */ /* */ /* 1) physical Units table */ /* 2) virutal Units table */ /* 3) TL strucutre (Not by this routien but by its caller */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* readFlag : TRUE for retrieve data FALSE for writing it */ /* */ /* Returns: */ /* flOK on success other error codes for erase\read\write failures */ /* start : Unit offset (from the first unit of the */ /* volume to start writting quick mount data. */ /* (remember migh be bad). */ /*----------------------------------------------------------------------*/
static FLStatus quickMountData(Bnand vol, FLBoolean readFlag, byte* start) { dword length; dword remainder; word partialSize; CardAddress flashAddr; FLStatus status; byte FAR1* bufferPtr = vol.physicalUnits; byte bbt[MAX_QUICK_MOUNT_UNITS]; /* Minimal bad blocks table */
/* Read bad block tabel and find the first good block of the volume */
status = vol.flash->readBBT(vol.flash,vol.firstQuickMountUnit, MAX_QUICK_MOUNT_UNITS,vol.blockMultiplierBits,bbt,FALSE); if(status != flOK) return status;
for(*start = 0 ; (*start<MAX_QUICK_MOUNT_UNITS) && (bbt[*start] != BBT_GOOD_UNIT) ; (*start)++); if (*start == MAX_QUICK_MOUNT_UNITS) { DEBUG_PRINT(("\nDebug: Too many Bad blocks in quick mount area\r\n")); return flBadBBT; } /* Skip first page for Bnand record */ flashAddr = ((CardAddress)(vol.firstQuickMountUnit + (*start)) << vol.unitSizeBits) + vol.flash->pageSize;
/* Only the full pages of physical table */
length = vol.noOfUnits * sizeof(ANANDPhysUnit); /* Physicals length */ remainder = length & (SECTOR_SIZE-1)/*vol.flash->pageSize*/; /* Last partial page */ length -= remainder; /* Round to pages */
status = putGetBuffer(&vol, length, &bufferPtr, &flashAddr,bbt,readFlag); if(status != flOK) return status;
/* Partial page of the physical table + begining of virtual table */
length = vol.noOfVirtualUnits * sizeof(ANANDUnitNo); /* Virtuals */ partialSize = (word)TFFSMIN(length,(dword)SECTOR_SIZE-remainder);
if (remainder > 0) { if (readFlag) { status = vol.flash->read(vol.flash,flashAddr , inftlBuffer, sizeof(inftlBuffer),EDC); tffscpy(bufferPtr,inftlBuffer,(word)remainder); tffscpy(vol.virtualUnits,inftlBuffer+(word)remainder,partialSize); } #ifndef FL_READ_ONLY
else { tffscpy(inftlBuffer,bufferPtr, (word)remainder); tffscpy(inftlBuffer+(word)remainder,vol.virtualUnits,partialSize); status = vol.flash->write(vol.flash,flashAddr,inftlBuffer, sizeof(inftlBuffer),EDC); } #endif /* FL_READ_ONLY */
if(status != flOK) return status;
bufferPtr = partialSize + (byte FAR1*)vol.virtualUnits; flashAddr += SECTOR_SIZE/*vol.flash->pageSize*/; NextGoodUnit(flashAddr,bbt); /* if needed check for next good unit */ } else { bufferPtr = (byte FAR1*)vol.virtualUnits; }
/* Only the full pages of virtual table */
length -= partialSize; /* Remaining virtuals */ remainder = length % SECTOR_SIZE/*vol.flash->pageSize*/; /* Last partial page */ length -= remainder; /* Round to pages */
status = putGetBuffer(&vol,length,&bufferPtr,&flashAddr,bbt,readFlag); if(status != flOK) return status;
/* Partial page of the virtual table */
if (remainder>0) { if(readFlag) { status = vol.flash->read(vol.flash,flashAddr,inftlBuffer, sizeof(inftlBuffer),EDC); tffscpy(bufferPtr , inftlBuffer,(word)remainder); } #ifndef FL_READ_ONLY
else { tffscpy(inftlBuffer , bufferPtr , (word)remainder); status = vol.flash->write(vol.flash,flashAddr,inftlBuffer, sizeof(inftlBuffer),EDC); } #endif /* FL_READ_ONLY */
} return status; }
#endif /* QUICK_MOUNT_FEATURE */
/*----------------------------------------------------------------------*/ /* d i s m o u n t I N F T L */ /* */ /* Dismount INFTL volume */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* */ /*----------------------------------------------------------------------*/
static void dismountINFTL(Bnand vol) { DEBUG_PRINT(("\nDebug: starting INFTL dismount.\r\n"));
#if (defined(QUICK_MOUNT_FEATURE) && !defined(FL_READ_ONLY))
if ((vol.flags & QUICK_MOUNT) && (vol.firstMediaWrite == TRUE)) { savedBnand* newVol = (savedBnand*)inftlBuffer; byte start; FLStatus status;
DEBUG_PRINT(("\nDebug: with save operation of quick mount data.\r\n"));
start = vol.firstUnit - vol.firstQuickMountUnit; status = vol.flash->erase(vol.flash,(word)(vol.firstQuickMountUnit << vol.blockMultiplierBits), (word)((1 << vol.blockMultiplierBits) * start)); if (status==flOK) { vol.flags &= ~QUICK_MOUNT; /* Prevent resaving the data */ status = quickMountData(&vol, FALSE,&start); if (status == flOK) { /* Place Bnand record */
tffsset(inftlBuffer,0,sizeof(inftlBuffer)); /* Clear inftlBuffer */
/*********************************************************/ /* Convert internal volume to little indian dword fields */ /*********************************************************/
toLE4(newVol->freeUnits , vol.freeUnits ); toLE4(newVol->roverUnit , vol.roverUnit ); toLE4(newVol->countsValid , vol.countsValid ); toLE4(newVol->sectorsRead , vol.sectorsRead ); toLE4(newVol->sectorsWritten , vol.sectorsWritten ); toLE4(newVol->sectorsDeleted , vol.sectorsDeleted ); toLE4(newVol->parasiteWrites , vol.parasiteWrites ); toLE4(newVol->unitsFolded , vol.unitsFolded ); toLE4(newVol->wearLevel_1 , vol.wearLevel.alarm ); toLE4(newVol->wearLevel_2 , vol.wearLevel.currUnit); toLE4(newVol->eraseSum , vol.eraseSum ); toLE4(newVol->validate , QUICK_MOUNT_VALID_SIGN); #if (defined(VERIFY_WRITE) || defined(VERIFY_ERASED_SECTOR))
toLE4(newVol->verifiedSectorNo , vol.verifiedSectorNo); #else
toLE4(newVol->verifiedSectorNo , 0); #endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */
status = vol.flash->write(vol.flash,((CardAddress)(vol.firstQuickMountUnit +start))<< vol.unitSizeBits,inftlBuffer, sizeof(inftlBuffer),EDC); } if (status != flOK) DEBUG_PRINT(("Debug: ERROR writing quick mount information.\r\n")); } else /* continue with dismount */ { DEBUG_PRINT(("Debug: Error erasing quick mount information.\r\n")); } } #endif /* QUICK_MOUNT_FEATURE && not FL_READ_ONLY */
#ifdef FL_MALLOC
/* Free multi sector buffers */ if (multiSectorBufCounter[vol.socketNo] == 0) { if (multiSectorBuf[vol.socketNo] != NULL) { FL_FREE(multiSectorBuf[vol.socketNo]); multiSectorBuf[vol.socketNo] = NULL; } } if (multiSectorBufCounter[vol.socketNo] >= 0) { multiSectorBufCounter[vol.socketNo]--; } /* Free convertion tables */ if( vol.physicalUnits != NULL ) { #if (defined (CHAINS_DEBUG) && !defined(CHECK_MOUNT))
FILE * out;
out = getFileHandle(&vol,0); if (out == NULL) { DEBUG_PRINT(("Debug: Can not open debug file.\r\n")); } else { checkVirtualChains(&vol,out); checkVolumeStatistics(&vol,out); } #endif /* CHAINS_DEBUG AND NOT CHECK_MOUNT*/
FL_FAR_FREE(vol.physicalUnits); } if( vol.virtualUnits != NULL ) FL_FAR_FREE(vol.virtualUnits); vol.physicalUnits = NULL; vol.virtualUnits = NULL; /* Free catche tables */ #ifdef NFTL_CACHE
if( vol.ucache != NULL ) FL_FAR_FREE(vol.ucache); if( vol.scache != NULL ) FL_FAR_FREE(vol.scache); vol.ucache = NULL; vol.scache = NULL; #endif /* NFTL_CACHE */
#endif /* FL_MALLOC */
DEBUG_PRINT(("Debug: finished INFTL dismount.\r\n")); }
/*----------------------------------------------------------------------*/ /* r e t r i e v e H e a d e r */ /* */ /* Retrieve media header by oring the headers of each floor */ /* */ /* Note: The header of each floor is read to the first half of the */ /* buffer and then ORed to the second half therefor constructing */ /* the real header in the upper half. After all copies are read */ /* the data is copied to the first half. */ /* */ /* Note: No endian format changes are made. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* originalUnits : Array to store original units locations */ /* readFullBBT : Boolean flag. When true the entire BBT will be read */ /* and media units locations will be returned through */ /* the originalUnits argument array. When FALSE only */ /* the size of HEADER_SEARCH_BOUNDRY of each floor */ /* be read. */ /* retrieveData : Boolean flag. When true the header will be read */ /* */ /* Returns: */ /* flOK on success any other value on error */ /* flBadFormat if header was not found */ /*----------------------------------------------------------------------*/
FLStatus retrieveHeader (Bnand vol , ANANDUnitNo * originalUnits, FLBoolean readFullBBT , FLBoolean retrieveData) { ANANDUnitNo iUnit,index; ANANDUnitNo noOfUnitsPerFloor; byte headerSize; byte floorNo; FLStatus status=flOK; byte bbt[HEADER_SEARCH_BOUNDRY]; byte FAR1* BBT;
noOfUnitsPerFloor = (ANANDUnitNo)(vol.flash->chipSize >> vol.unitSizeBits) * ((vol.flash->noOfChips + (vol.flash->noOfChips % vol.flash->noOfFloors)) / vol.flash->noOfFloors);
headerSize = sizeof(BNANDBootRecord)+MAX_TL_PARTITIONS*sizeof(BNANDVolumeHeaderRecord); tffsset(originalUnits,0,sizeof(ANANDUnitNo) * MAX_NO_OF_FLOORS);
if (readFullBBT == TRUE) /* read entire BBT into vol records (format) */ { status = vol.flash->readBBT(vol.flash,0,vol.noOfUnits, vol.blockMultiplierBits, vol.physicalUnits,TRUE); if(status != flOK) return status; BBT = vol.physicalUnits; } else { BBT = bbt; }
/* Go over all of the media floors and find header location */
for (floorNo = 0 ; floorNo < vol.flash->noOfFloors ; floorNo++) { iUnit = (ANANDUnitNo)floorNo * noOfUnitsPerFloor; if (readFullBBT == FALSE) /* read small part of the floors BBT */ { status = vol.flash->readBBT(vol.flash,iUnit, HEADER_SEARCH_BOUNDRY,vol.blockMultiplierBits,BBT,FALSE); if(status != flOK) return status;
iUnit=0; }
/* find and save location of the first good block of the floor */
index = iUnit + HEADER_SEARCH_BOUNDRY; while ((iUnit<index)&&(BBT[iUnit]!=BBT_GOOD_UNIT)) { iUnit++; } if (iUnit==index) { DEBUG_PRINT(("Debug: ERROR too many bad blocks (can not find place for INFTL header.\r\n")); return flBadBBT; }
if (readFullBBT == FALSE) /* Restore iUnit pointer to the physical media */ { iUnit += (ANANDUnitNo)floorNo * noOfUnitsPerFloor; } originalUnits[floorNo] = iUnit; /* Save origial unit location */ }
if (retrieveData == FALSE) return flOK;
/* Need to read the previous header */
tffsset(inftlBuffer,0,SECTOR_SIZE); for (floorNo = 0 ; floorNo < vol.flash->noOfFloors ; floorNo++) { for (index=0;index<NO_OF_MEDIA_HEADERS;index++) /* both 2 copies */ { status = vol.flash->read(vol.flash,((CardAddress)originalUnits[floorNo] << vol.unitSizeBits) + index * HEADERS_SPACING, inftlBuffer + headerSize,headerSize,PARTIAL_EDC); if (status != flOK) { DEBUG_PRINT(("Debug: ERROR reading original unit header.\r\n")); } else { if (tffscmp(inftlBuffer + headerSize, "BNAND", sizeof("BNAND")) == 0) break; } } if (index>=NO_OF_MEDIA_HEADERS) { DEBUG_PRINT(("Debug: Media header was not found on all copies.\r\n")); return flBadFormat; }
/* merge with previous headers */ for (index = 0 ; headerSize > index ; index++) { inftlBuffer[index] |= inftlBuffer[index + headerSize]; } } /* loop of the floors */
return flOK; }
/*----------------------------------------------------------------------*/ /* I N F T L I n f o */ /* */ /* get INFTL information. */ /* */ /* Parameters: */ /* vol : Pointer discribing volume. */ /* tlInfo : Pointer to user record */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /* tlInfo : Record containing tl infromation. */ /*----------------------------------------------------------------------*/
static FLStatus INFTLInfo(Bnand vol, TLInfo *tlInfo) { tlInfo->sectorsInVolume = vol.virtualSectors; tlInfo->bootAreaSize = (dword)vol.bootUnits << vol.unitSizeBits; tlInfo->eraseCycles = vol.eraseSum; tlInfo->tlUnitBits = vol.unitSizeBits; return flOK; }
#ifndef NO_READ_BBT_CODE
/*----------------------------------------------------------------------*/ /* r e a d B B T */ /* */ /* Returns a pointer to the BBT of the device. */ /* Note: Bad unit are marked with a 4 bytes address of the unit. */ /* Note: A unit can contain several blocks */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* buf : pointer to buffer to read into */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /* noOfBB : returns the number of bad unit of the media */ /* meidaSize : returns the media size in bytes */ /*----------------------------------------------------------------------*/
static FLStatus readBBT(Bnand vol, CardAddress FAR1 * buf, long FAR2 * mediaSize, unsigned FAR2 * noOfBB) { dword iUnit; dword noOfUnits = (ANANDUnitNo)(((dword)vol.flash->noOfChips * vol.flash->chipSize) >> vol.unitSizeBits); dword index,curRead; ANANDUnitNo maxBad = (ANANDUnitNo)(noOfUnits * ANAND_BAD_PERCENTAGE / 100); CardAddress FAR1 * ptr = buf; *noOfBB = 0;
if ( vol.flash->readBBT == NULL) { DEBUG_PRINT(("Debug: ERROR unerasable BBT not supported by MTD.\r\n")); return flGeneralFailure; } else { for (iUnit=vol.flash->firstUsableBlock;iUnit<noOfUnits;iUnit+=curRead) { curRead = TFFSMIN(SECTOR_SIZE,noOfUnits-iUnit); vol.flash->readBBT(vol.flash,iUnit,curRead,vol.blockMultiplierBits,multiSectorBuf[vol.socketNo],FALSE); for ( index = 0 ; (index < curRead) && (*noOfBB < maxBad); index++) if ((*(multiSectorBuf[vol.socketNo] + index) != BBT_GOOD_UNIT) && /* Not good unit */ (*(multiSectorBuf[vol.socketNo] + index) != BBT_UNAVAIL_UNIT)) /* Not used for a special purpose */ { *ptr = (iUnit+index) << vol.unitSizeBits; ptr = (CardAddress FAR1*)flAddLongToFarPointer((byte FAR1 *)ptr, sizeof(CardAddress)); (*noOfBB)++; } if ( *noOfBB == maxBad) { DEBUG_PRINT(("Debug: ERROR to many bad blocks.\r\n")); return flVolumeTooSmall; } } } *mediaSize = (long) noOfUnits << vol.unitSizeBits; return flOK; }
#endif /* NO_READ_BBT_CODE */
/*----------------------------------------------------------------------*/ /* c o n v e r t C h a i n */ /* */ /* Convert candidate chain to given value. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* newestUnitNo : newest unit in chain */ /* oldestUnitNo : oldest unit in chain */ /* virtualUnitNo : virtual unit no */ /* chainsMark : new value */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
static FLStatus convertChain(Bnand vol, ANANDUnitNo newestUnitNo, ANANDUnitNo oldestUnitNo, ANANDUnitNo virtualUnitNo, byte chainsMark) { ANANDUnitNo chainBound = 0;
for(;newestUnitNo != oldestUnitNo ; chainBound++, newestUnitNo = getPrevUnit(&vol,newestUnitNo,virtualUnitNo)) { if((newestUnitNo == ANAND_BAD_CHAIN_UNIT ) || /* Brocken chain */ (chainBound >= DOUBLE_MAX_UNIT_CHAIN)) /* Infinit loop */ return flGeneralFailure;
vol.physicalUnits[newestUnitNo] = chainsMark; } vol.physicalUnits[oldestUnitNo] = chainsMark; return flOK; }
#ifndef FL_READ_ONLY
/*----------------------------------------------------------------------*/ /* e r a s e O r p h a n U n i t */ /* */ /* Erase one unit. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* unitNo : Unit to format */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
static FLStatus eraseOrphanUnit(Bnand vol, ANANDUnitNo unitNo) { word eraseMark; dword eraseCount; FLStatus status;
status = getUnitTailer(&vol,unitNo,&eraseMark,&eraseCount,UNIT_TAILER_OFFSET); if(status != flOK) return status;
if(unitNo+(ANANDUnitNo)vol.firstUnit<(ANANDUnitNo)vol.firstUnit) { return flGeneralFailure; }
status = vol.flash->erase(vol.flash, (word)(((dword)unitNo+(dword)vol.firstUnit) << (vol.unitSizeBits - vol.erasableBlockSizeBits)), (word)(1 << vol.blockMultiplierBits));
if (status != flOK) { markUnitBad(&vol,unitNo); /* make sure unit format is not valid */ return status; }
vol.eraseSum++; eraseCount++; if (eraseCount == 0) /* was hex FF's */ eraseCount++;
return setUnitTailer(&vol,unitNo,ERASE_MARK,eraseCount,UNIT_TAILER_OFFSET); }
#endif /* FL_READ_ONLY */
/*----------------------------------------------------------------------*/ /* c h e c k U n i t H e a d */ /* */ /* Compare 2 copies of unit header. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* unitNo : Physical unit number */ /* */ /* Returns: */ /* flOK on success, flFormattingError on miscompare. */ /*----------------------------------------------------------------------*/
static FLStatus checkUnitHead(Bnand vol, ANANDUnitNo unitNo) { ANANDUnitHeader unitData; SecondANANDUnitHeader secondUnitData;
/* Read first unit data */ checkStatus(vol.flash->read(vol.flash, unitBaseAddress(vol,unitNo) + UNIT_DATA_OFFSET, &unitData, sizeof(ANANDUnitHeader), EXTRA)); checkStatus(vol.flash->read(vol.flash, unitBaseAddress(vol,unitNo) + SECOND_HEADER_OFFSET + UNIT_DATA_OFFSET, &secondUnitData, sizeof(SecondANANDUnitHeader), EXTRA)); if((LE2(secondUnitData.virtualUnitNo) != LE2(unitData.virtualUnitNo)) || (LE2(secondUnitData.prevUnitNo ) != LE2(unitData.prevUnitNo )) || (secondUnitData.ANAC != secondUnitData.ANAC ) ) return flFormattingError;
return flOK; }
/*----------------------------------------------------------------------*/ /* g o A l o n g C h a i n */ /* */ /* Go along the INFTL chaine while marking the chain in the convertion */ /* tables. This routine is called by the mount routine in order to */ /* initialize the volumes convertion tables. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* unitNo : Physical unit to check. */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
static FLStatus goAlongChain(Bnand vol,ANANDUnitNo unitNo) { ANANDUnitNo origVirtualNo,virtualUnitNo,prevUnitNo; ANANDUnitNo lastCurrent,nextUnitNo,lastUnitNo; byte ANAC, NAC, prevANAC, parityPerField; FLStatus status; word eraseMark; dword eraseCount;
/* Check if already been here */ if((vol.physicalUnits[unitNo]==FL_VALID) || (vol.physicalUnits[unitNo]==FL_FIRST_VALID) || (vol.physicalUnits[unitNo]==FL_PRELIMINARY) || (vol.physicalUnits[unitNo]==FL_FIRST_PRELIMINARY)) return flOK;
/* Read unit tailor to check the erase mark */
status = getUnitTailer(&vol,unitNo,&eraseMark,&eraseCount,UNIT_TAILER_OFFSET); if(status != flOK) return status; vol.eraseSum+=eraseCount; if (eraseMark != ERASE_MARK) { /* Do not perform erase in the mount. The allocateUnit routine */ /* rechecks for the erase mark and it will erase this unit. */ vol.physicalUnits[unitNo] = ANAND_UNIT_FREE; return flOK; }
status = getUnitData(&vol,unitNo,&virtualUnitNo, &prevUnitNo, &ANAC,&NAC,&parityPerField); if(status != flOK) return status;
/* Check parity result of values returned by getUnitData */
if(badParityResult(parityPerField)) { vol.physicalUnits[unitNo]=FL_PRELIMINARY; return flOK; }
/* Check if the unit is free (all fields are FF) */
if((virtualUnitNo == ANAND_NO_UNIT ) && (prevUnitNo == ANAND_NO_UNIT ) && (ANAC == ANAND_UNIT_FREE) && (NAC == ANAND_UNIT_FREE)) return flOK; /* free unit */
/* Check virtual unit number that was returned */ if((virtualUnitNo == ANAND_NO_UNIT) || (vol.noOfVirtualUnits <= virtualUnitNo) ) { vol.physicalUnits[unitNo]=FL_PRELIMINARY; return flOK; }
/* Save location of currently known newest unit of our chain */ lastUnitNo = vol.virtualUnits[virtualUnitNo];
/* If older unit is none existing then there is only one unit in this */ /* chain so lets not complicate things. */
if(prevUnitNo >= vol.noOfUnits) { if(lastUnitNo == ANAND_NO_UNIT ) { /* First access to this unit therefore a one unit chain */
vol.virtualUnits[virtualUnitNo] = unitNo; vol.physicalUnits[unitNo] = FL_VALID | FIRST_IN_CHAIN; return flOK; } else { /* One unit chain that has 2 ends mark and deal later */ if(checkUnitHead(&vol,unitNo)!=flOK) /* Invalid header */ { vol.physicalUnits[unitNo] = FL_PRELIMINARY; return flOK; } else { if(checkUnitHead(&vol,lastUnitNo)!=flOK) { vol.physicalUnits[lastUnitNo] = FL_PRELIMINARY; vol.virtualUnits[virtualUnitNo] = unitNo; vol.physicalUnits[unitNo] = FL_VALID | FIRST_IN_CHAIN; /* Might want to check rest of chain - but not neccesary */ return flOK; } } vol.physicalUnits[unitNo] = FL_PRELIMINARY; DEBUG_PRINT(("Debug: We have reached a unit twice while mounting.\r\n")); return flOK; } }
/* We know that our unit points to a valid unit , now check if we */ /* already checked that older unit */
if((vol.physicalUnits[prevUnitNo] == FL_VALID)|| (vol.physicalUnits[prevUnitNo] == FL_FIRST_VALID)) { if(lastUnitNo == prevUnitNo) { /* Our older unit is the head of the current chain. All we need */ /* to do is append our newer unit and mark it as the new head */
vol.physicalUnits[unitNo] = FL_VALID; vol.virtualUnits[virtualUnitNo] = unitNo; /* Might be nice to check for ANAC consistency */ return flOK; } else /* The previous unit is not the newest unit of our chain */ { if(lastUnitNo == ANAND_NO_UNIT) { /* This is the first time we accessed this chain, but the */ /* unit indicated by the previous unit field is taken. We */ /* must assume that it no longer belongs to our chain. */
vol.virtualUnits[virtualUnitNo] = unitNo; vol.physicalUnits[unitNo] = FL_VALID | FIRST_IN_CHAIN; return flOK; } else /* Virtual chain already has a head - 2 ends of chain */ { /* If we reached this point we have a problem - its bad. */ /* We were never visited, so we are'nt a part of a known chain. */ /* Our previous unit is used and was visited so: */ /* a) It belong to our chain - so why is it not it's head */ /* b) It does not belong to our chain - so it will not lead */ /* us to the rest of our chain which was already found. */
if(checkUnitHead(&vol,unitNo)!=flOK) /* Invalid header */ { vol.physicalUnits[unitNo] = FL_PRELIMINARY; return flOK; } else { if(checkUnitHead(&vol,lastUnitNo)!=flOK) { vol.physicalUnits[lastUnitNo] = FL_PRELIMINARY; vol.virtualUnits[virtualUnitNo] = unitNo; vol.physicalUnits[unitNo] = FL_VALID | FIRST_IN_CHAIN; /* Might want to check rest of chain - but not neccesary */ return flOK; } } vol.physicalUnits[unitNo] = FL_PRELIMINARY; DEBUG_PRINT(("Debug: We have reached a unit twice while mounting.\r\n")); return flOK; } } }
/* If we reached this point , we have a valid older unit pointer */ /* and it points to a unit we did not mark as visited yet. We need */ /* to go along the chain and reconstruct it in the RAM tables. */
/* Save location of our unit and virtual unit number */ lastCurrent = unitNo; origVirtualNo = virtualUnitNo;
/* Mark unit as Orphane until we shall verify the 2 ends connect */ vol.physicalUnits[unitNo] = FL_PRELIMINARY;
/************************************************************/ /* Go over the chain starting the unit previous to our unit */ /************************************************************/
while(1) { nextUnitNo = unitNo; unitNo = prevUnitNo; prevANAC = ANAC; if(unitNo == ANAND_NO_UNIT) break;
/* If already been to this unit */ if((vol.physicalUnits[unitNo] == FL_VALID)|| (vol.physicalUnits[unitNo] == FL_FIRST_VALID)) { if(lastUnitNo == unitNo) { /* We have returned to the chains head , so the unit is valid */ /* Convert all the units we passed as valid and mark new head */ status = convertChain(&vol,lastCurrent,nextUnitNo, origVirtualNo,FL_VALID); vol.virtualUnits[origVirtualNo] = lastCurrent; return flOK; } /* We have reached a unit that was already checked, but was not */ /* registeredour as the chains head. We can safely assume it does */ /* not belong to our virtual unit */ break; }
/* Read unit header of our previous unit */
status = getUnitData(&vol,unitNo,&virtualUnitNo, &prevUnitNo, &ANAC,&NAC,&parityPerField); if(status != flOK) return status;
if(badParityResult(parityPerField)) /* Bad unit header */ { /* We can no longer follow the chain */ vol.physicalUnits[unitNo] = FL_PRELIMINARY; /* Delete later */ break; }
/* Check if unit belongs to our chain */
if((virtualUnitNo != origVirtualNo ) || /* Correct virtual unit no */ (!consecutiveNumbers(prevANAC,ANAC)) ) /* ANAC is consecutive */ { /* Note : none consecutive ANAC might still be connected to the end */ /* the chain , on the next time we will read it. */ break; }
/* We have verified that unit belongs to our chain */ /* Mark unit as Orphane until we shall verify the 2 ends connect */ vol.physicalUnits[unitNo]=FL_PRELIMINARY; }
/* Chain reached a unit pointing to an invalid unit. */ if(lastUnitNo == ANAND_NO_UNIT) { /* Chain did not have a head so mark it as a valid chain */ status = convertChain(&vol,lastCurrent,nextUnitNo, origVirtualNo,FL_VALID); vol.physicalUnits[nextUnitNo] = FL_VALID | FIRST_IN_CHAIN; vol.virtualUnits[origVirtualNo] = lastCurrent; } else { /* Chain had a head. Check if previous head is valid. */
if(checkUnitHead(&vol,lastUnitNo)!=flOK) /* Invalid header */ { /* The unit we found earlier was a result of power failure */ vol.physicalUnits[lastUnitNo] = FL_PRELIMINARY; vol.virtualUnits[virtualUnitNo] = lastCurrent; status = convertChain(&vol,lastCurrent,nextUnitNo, origVirtualNo,FL_VALID); vol.physicalUnits[nextUnitNo] = FL_VALID | FIRST_IN_CHAIN; return status; } DEBUG_PRINT(("Debug: We have reached a unit twice while mounting.\r\n")); status = convertChain(&vol,lastCurrent,nextUnitNo, origVirtualNo,FL_PRELIMINARY); }
return status; }
#ifdef QUICK_MOUNT_FEATURE
/*----------------------------------------------------------------------*/ /* c h e c k Q u i c k M o u n t I n f o */ /* */ /* Read the quick mount information and verfiy its itegrity. */ /* */ /* Note : If the data is valid it will be read to the vol record and */ /* will mark the current data as invalid. */ /* */ /* Note : checksum will be added in future versions. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* */ /* Returns: TRUE if data is valid otherwise FALSE. */ /*----------------------------------------------------------------------*/
static FLBoolean checkQuickMountInfo(Bnand vol) { byte start; /* The first goot unit of the quick mount data */ FLStatus status; savedBnand *newVol;
DEBUG_PRINT(("Debug: trying to read quick mount information.\r\n"));
status = quickMountData(&vol, TRUE,&start); if (status==flOK) { status = vol.flash->read(vol.flash,(((CardAddress)(vol.firstQuickMountUnit +start))<< vol.unitSizeBits),inftlBuffer, sizeof(inftlBuffer),EDC); if (status == flOK) { /* Convert the dword fields to the internal volume */
newVol = (savedBnand *) inftlBuffer; if (LE4(newVol->validate) == QUICK_MOUNT_VALID_SIGN) { vol.freeUnits = (ANANDUnitNo)LE4(newVol->freeUnits ); vol.roverUnit = (ANANDUnitNo)LE4(newVol->roverUnit ); vol.countsValid = (ANANDUnitNo)LE4(newVol->countsValid ); vol.sectorsRead = LE4(newVol->sectorsRead ); vol.sectorsWritten = LE4(newVol->sectorsWritten); vol.sectorsDeleted = LE4(newVol->sectorsDeleted); vol.parasiteWrites = LE4(newVol->parasiteWrites); vol.unitsFolded = LE4(newVol->unitsFolded ); vol.wearLevel.alarm = (word)LE4(newVol->wearLevel_1 ); vol.wearLevel.currUnit = (ANANDUnitNo)LE4(newVol->wearLevel_2 ); vol.eraseSum = LE4(newVol->eraseSum ); #if (defined(VERIFY_WRITE) || defined(VERIFY_ERASED_SECTOR))
vol.verifiedSectorNo = LE4(newVol->verifiedSectorNo); #endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */
DEBUG_PRINT(("Debug: quick mount information was successfuly restored.\r\n")); return TRUE; } } } DEBUG_PRINT(("Debug: Error getting quick mount information.\r\n")); return FALSE; }
#endif /* QUICK_MOUNT_FEATURE */
#ifdef NFTL_CACHE
/*----------------------------------------------------------------------*/ /* i n i t C a t c h */ /* */ /* Initialize and allocate the unit and sector catche. */ /* */ /* Note - need to add check for not enough static memory. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* ramForCache : Cache offset of previous volumes on device */ /* */ /* Returns: */ /*----------------------------------------------------------------------*/
#ifndef FL_MALLOC
void initCatch(Bnand vol, dword ramForCache) #else
void initCatch(Bnand vol) #endif /* FL_MALLOC */
{ dword scacheSize = 0; /* Initialized to remove warrnings */ dword iUnit;
/* create and initialize ANANDUnitHeader cache */ #ifdef ENVIRONMENT_VARS
if( flUseNFTLCache == 1 ) /* behave according to the value of env variable */ #endif
{ #ifdef FL_MALLOC
vol.ucache = (ucacheEntry FAR1*) FL_FAR_MALLOC(vol.noOfUnits * sizeof(ucacheEntry)); #else
vol.ucache = &socketUcache[flSocketNoOf(vol.flash->socket)][ramForCache]; #endif /* FL_MALLOC */
} #ifdef ENVIRONMENT_VARS
else { vol.ucache = NULL; } #endif /* ENVIRONMENT_VARS */
if (vol.ucache != NULL) { for (iUnit = 0; iUnit < vol.noOfUnits; iUnit++) { vol.ucache[iUnit].virtualUnitNo = 0xDEAD; vol.ucache[iUnit].prevUnitNo = 0xDEAD; } } else { DEBUG_PRINT(("Debug: INFTL runs without U-cache\r\n")); }
/* create and initialize SectorFlags cache */ #ifdef ENVIRONMENT_VARS
if( flUseNFTLCache == 1 ) /* behave according to the value of env variable */ #endif /* ENVIRONMENT_VARS */
{ scacheSize = (dword)vol.noOfUnits << (vol.unitSizeBits - SECTOR_SIZE_BITS - 2); #ifdef FL_MALLOC
if( (sizeof(unsigned) < sizeof(scacheSize)) && (scacheSize >= 0x10000L) ) /* Out of Segment Boundary */ { vol.scache = NULL; } else { vol.scache = (byte FAR1*) FL_FAR_MALLOC(scacheSize); } #else
vol.scache = &socketScache[flSocketNoOf(vol.flash->socket)][ramForCache << (vol.unitSizeBits - SECTOR_SIZE_BITS - 2)]; #endif /* FL_MALLOC */
} #ifdef ENVIRONMENT_VARS
else { vol.scache = NULL; } #endif /* ENVIRONMENT_VARS */
if (vol.scache != NULL) { /*
* Whenever SECTOR_IGNORE is found in Sector Flags cache it is double * checked by reading actual sector flags from flash-> This is way * all the cache entries are initially set to SECTOR_IGNORE. */ byte val = (S_CACHE_SECTOR_IGNORE << 6) | (S_CACHE_SECTOR_IGNORE << 4) | (S_CACHE_SECTOR_IGNORE << 2) | S_CACHE_SECTOR_IGNORE; dword iC;
for(iC=0;( iC < scacheSize );iC++) vol.scache[iC] = val; } else { DEBUG_PRINT(("Debug: INFTL runs without S-cache\r\n")); } } #endif /* NFTL_CACHE */
/*----------------------------------------------------------------------*/ /* m o u n t I N F T L */ /* */ /* Mount the volume. Initialize data structures and conversion tables */ /* */ /* Parameters: */ /* volNo : Volume serial no. */ /* tl : Mounted translation layer on exit */ /* flash : Flash media mounted on this socket */ /* volForCallback : Pointer to FLFlash structure for power on */ /* callback routine. */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/ static FLStatus mountINFTL(unsigned volNo, TL *tl, FLFlash *flash, FLFlash **volForCallback) { Bnand vol = &vols[volNo]; ANANDUnitNo iUnit; ANANDUnitNo originalUnits[MAX_NO_OF_FLOORS]; BNANDBootRecord * mediaHeader; /* Disk header record */ BNANDVolumeHeaderRecord * volumeHeader; /* volume header record */ FLStatus status; byte index; #ifndef FL_MALLOC
dword ramForUnits=0; #ifdef NFTL_CACHE
dword ramForCache=0; #endif /* NFTL_CACHE */
#endif /* FL_MALLOC */
#ifdef EXTRA_LARGE
word moreUnitBits; #endif /* EXTRA_LARGE */
#ifdef CHAINS_DEBUG
FILE * out; #endif /* CHAINS_DEBUG */
DEBUG_PRINT(("Debug: starting INFTL mount.\r\n"));
/*************************/ /* Find the media header */ /*************************/
tffsset(&vol,0,sizeof(vol)); status = initINFTL(&vol,flash); if(status == flOK) status = retrieveHeader(&vol,originalUnits,FALSE,TRUE); if(status != flOK) return status;
mediaHeader = (BNANDBootRecord *)inftlBuffer; if (tl->partitionNo >= LE4(mediaHeader->noOfBDTLPartitions)) { DEBUG_PRINT(("Debug: wrong partition number.\r\n")); return flBadDriveHandle; } *volForCallback = vol.flash; vol.eraseSum = 0;
/* Get media information from unit header */
volumeHeader = (BNANDVolumeHeaderRecord *)(inftlBuffer + sizeof(BNANDBootRecord) + (word)((LE4(mediaHeader->noOfBinaryPartitions) * sizeof(BNANDVolumeHeaderRecord)))); vol.bootUnits = (ANANDUnitNo)LE4(volumeHeader->firstQuickMountUnit);
#ifndef FL_MALLOC
/* calculate the memory offset for static allocation */
for (index = tl->partitionNo;index>0;index--,volumeHeader++) { ramForUnits += LE4(volumeHeader->virtualUnits) * sizeof(ANANDUnitNo); /* virtual size */ iUnit = (word)(LE4(volumeHeader->lastUnit) - LE4(volumeHeader->firstUnit) + 1); ramForUnits += iUnit * sizeof(ANANDPhysUnit); #ifdef NFTL_CACHE
ramForCache += iUnit; #endif /* NFTL_CACHE */
} #else
volumeHeader += tl->partitionNo; #endif /* FL_MALLOC */
vol.noOfVirtualUnits = (ANANDUnitNo)LE4(volumeHeader->virtualUnits); vol.flags = (byte)LE4(mediaHeader->formatFlags); vol.firstQuickMountUnit = (ANANDUnitNo)LE4(volumeHeader->firstQuickMountUnit); vol.firstUnit =(ANANDUnitNo) LE4(volumeHeader->firstUnit); #ifdef NFTL_CACHE
vol.firstUnitAddress = (dword)vol.firstUnit << vol.unitSizeBits; #endif /* NFTL_CACHE */
vol.virtualSectors = (SectorNo)((LE4(volumeHeader->virtualUnits)<< vol.unitSizeBits) >> SECTOR_SIZE_BITS); vol.noOfUnits = (ANANDUnitNo)(LE4(volumeHeader->lastUnit) - LE4(volumeHeader->firstUnit) + 1);
/* Validy check */
if((ANANDUnitNo)(vol.noOfVirtualUnits > vol.noOfUnits)) { DEBUG_PRINT(("Reported no of virtual unit is larger then no of physical units\r\n")); return flBadFormat; }
#ifdef FL_MALLOC
status = initTables(&vol); #else
status = initTables(&vol,ramForUnits); #endif /* MALLOCK */
if(status != flOK) return status;
#ifdef NFTL_CACHE
#ifndef FL_MALLOC
initCatch(&vol, ramForCache); #else
initCatch(&vol); #endif /* FL_MALLOC */
#endif /* NFTL_CACHE */
#if (defined(VERIFY_WRITE) || defined(VERIFY_ERASED_SECTOR))
/* Default for INFTL is FL_OFF */ flVerifyWrite[vol.socketNo][tl->partitionNo] = FL_OFF; #endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */
/******************************************/ /* Try mounting from the quick mount data */ /******************************************/
#ifdef QUICK_MOUNT_FEATURE
#if (!defined(RAM_MTD) && !defined(CHECK_MOUNT))
if (((LE4(mediaHeader->formatFlags) & QUICK_MOUNT) == 0) || ( checkQuickMountInfo(&vol) == FALSE ) ) #endif /* not RAM_MTD && not CHECK_MOUNT */
#endif /* QUICK_MOUNT_FORMAT */
{ vol.firstMediaWrite = TRUE; /* Force writing quick mount information */
/***************************************/ /* Read BBT to internal representation */ /***************************************/
status = flash->readBBT(vol.flash,vol.firstUnit, vol.noOfUnits,vol.blockMultiplierBits, vol.physicalUnits,FALSE); if( status != flOK ) { DEBUG_PRINT(("Debug: Error reading BBT.\r\n")); dismountINFTL(&vol); /* Free tables must be done after call to initTables */ return status; } /* Translate bad unit table to internal representation */ for (iUnit = 0; iUnit < vol.noOfUnits; iUnit++) { if (vol.physicalUnits[iUnit] != BBT_GOOD_UNIT) { vol.physicalUnits[iUnit] = UNIT_BAD; } else { vol.physicalUnits[iUnit] = ANAND_UNIT_FREE; } } /* Translate original units to bad blocks */ for (index=0;index < vol.flash->noOfFloors;index++) { iUnit = originalUnits[index]-vol.firstUnit; if ((iUnit < vol.noOfUnits) && (originalUnits[index] > vol.firstUnit)) vol.physicalUnits[iUnit] = UNIT_BAD; }
/*************************/ /* Mount the unit chains */ /*************************/
/* Initialize virutal units table */
for (iUnit = 0; iUnit < vol.noOfVirtualUnits; iUnit++) vol.virtualUnits[iUnit] = ANAND_NO_UNIT;
#ifdef CHECK_MOUNT
status = checkMountINFTL(&vol); if (status != flOK) { TL_DEBUG_PRINT(tl_out,"Failed check Mount routine with status %d\n",status); SET_EXIT(INFTL_FAILED_MOUNT); } #endif /* CHECK_MOUNT */
for (iUnit = 0; iUnit < vol.noOfUnits; iUnit++) { if (vol.physicalUnits[iUnit] != UNIT_BAD) { status = goAlongChain(&vol,iUnit); if(status != flOK) { DEBUG_PRINT(("Debug: Error going along INFTL chains.\r\n")); dismountINFTL(&vol); /*Free tables must be done after call to initTables*/ return status; } } } #ifdef CHAINS_DEBUG
out = getFileHandle(&vol,0); if (out == NULL) { if (DID_MOUNT_FAIL) DEBUG_PRINT(("Debug: Can not open debug file.\r\n")); } else { checkVirtualChains(&vol,out); } #endif /* CHAINS_DEBUG */
vol.freeUnits = 0; for (iUnit = 0; iUnit < vol.noOfUnits; iUnit++) { switch( vol.physicalUnits[iUnit] ) { case ANAND_UNIT_FREE: vol.freeUnits++; break;
case FL_FIRST_VALID: /* Mark as first in chain */ case FL_VALID: vol.physicalUnits[iUnit] &= FIRST_IN_CHAIN; break;
case FL_FIRST_PRELIMINARY: /* Vadim :erase this unit*/ case FL_PRELIMINARY: DEBUG_PRINT(("Orphan unit found\r\n")); TL_DEBUG_PRINT(tl_out,"Orphan units found , unit no %d\n",iUnit); SET_EXIT(INFTL_FAILED_MOUNT); #ifndef FL_READ_ONLY
if( eraseOrphanUnit(&vol,iUnit) == flOK) { vol.physicalUnits[iUnit] = ANAND_UNIT_FREE; vol.freeUnits++; } break; #else
#ifndef CHECK_MOUNT
dismountINFTL(&vol); /*Free tables must be done after call to initTables*/ return flWriteFault; #endif /* CHECK_MOUNT */
#endif /* FL_READ_ONLY */
default: /* nothing here */ break; } } /* Initialize allocation rover */ vol.roverUnit = 0;
/* Initialize statistics */ vol.sectorsRead = vol.sectorsWritten = vol.sectorsDeleted = 0; vol.parasiteWrites = vol.unitsFolded = 0;
vol.wearLevel.alarm = (word)(vol.eraseSum % WLnow); vol.wearLevel.currUnit = (ANANDUnitNo)(vol.eraseSum % vol.noOfVirtualUnits); } /* end quick mounted succesful if */
#ifdef CHAINS_DEBUG
if (out != NULL) { checkVolumeStatistics(&vol,out); } #endif /* CHAINS_DEBUG */
#ifdef CHECK_MOUNT
dismountINFTL(&vol); /*Free tables must be done after call to initTables*/ if(vol.debugState & INFTL_FAILED_MOUNT) { TL_DEBUG_PRINT(tl_out,"Test failed\n\n"); fclose(tl_out); exit(EXIT_FAILURE); } else { TL_DEBUG_PRINT(tl_out,"Test success\n\n"); exit(EXIT_SUCCESS); } #endif /* CHECK_MOUNT */
#ifndef FL_READ_ONLY
/* Make sure there are at least 2 free units */ if(vol.freeUnits == 0) { status = foldBestChain(&vol,&iUnit); switch(status) { case flNotEnoughMemory: DEBUG_PRINT(("Debug: Not enough free units. Media is read only.\r\n")); case flOK: break; default: return status; } } tl->writeSector = writeSector; tl->deleteSector = deleteSector; tl->writeMultiSector = writeMultiSector; #ifdef DEFRAGMENT_VOLUME
tl->defragment = defragment; #endif /* DEFRAGMENT */
#if (defined(VERIFY_WRITE) || defined (VERIFY_VOLUME) || defined(VERIFY_ERASED_SECTOR))
tl->checkVolume = checkVolume; #endif /* VERIFY_WRITE || VERIFY_VOLUME || VERIFY_ERASED_SECTOR */
#else /* FL_READ_ONLY */
tl->writeSector = NULL; tl->deleteSector = NULL; tl->writeMultiSector = NULL; #ifdef DEFRAGMENT_VOLUME
tl->defragment = NULL; #endif /* DEFRAGMENT */
#if (defined(VERIFY_WRITE) || defined (VERIFY_VOLUME) || defined(VERIFY_ERASED_SECTOR))
tl->checkVolume = NULL; #endif /* VERIFY_WRITE || VERIFY_VOLUME || VERIFY_ERASED_SECTOR */
#endif /* FL_READ_ONLY */
tl->rec = &vol; tl->mapSector = mapSector; tl->sectorsInVolume = sectorsInVolume; tl->getTLInfo = INFTLInfo; tl->tlSetBusy = tlSetBusy; tl->dismount = dismountINFTL; tl->readSectors = readSectors; #ifndef NO_READ_BBT_CODE
tl->readBBT = readBBT; #endif
DEBUG_PRINT(("Debug: finished INFTL mount.\r\n")); return flOK; }
#ifdef HW_PROTECTION
/*----------------------------------------------------------------------*/ /* p r o t e c t i o n I N F T L */ /* */ /* Common entry point to all protection routines. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* volume : Pointer to partition record of the media header */ /* */ /* Returns: */ /* FLStatus : 0 on success, otherwise failure */ /* */ /*----------------------------------------------------------------------*/
static FLStatus protectionINFTL(Bnand vol,BNANDVolumeHeaderRecord* volume, IOreq FAR2* ioreq , FLFunctionNo callType) { FLFlash * flash = vol.flash; FLStatus status; #ifndef FL_READ_ONLY
CardAddress low; CardAddress high; byte floorNo; #endif /* FL_READ_ONLY */
byte tempFlags = 0; /* Initialized to remove warrnings */ byte area; word returnedFlags;
/* Save protection area since "volume" is overwritten by protectionSet */
area = (byte)LE4(volume->protectionArea); /* Protection area */ if ((LE4(volume->flags) & PROTECTABLE) == 0) return flNotProtected;
/* Routine that need to get the partition type before executing */ if ((callType != FL_PROTECTION_INSERT_KEY) && (callType != FL_PROTECTION_REMOVE_KEY)) { tempFlags = (byte)ioreq->irFlags; if (flash->protectionType == NULL) return flFeatureNotSupported;
status = flash->protectionType(flash,area,&returnedFlags); ioreq->irFlags = (unsigned)returnedFlags; if (status != flOK) return status;
/* Routines that need to change the protection attributes */
#ifndef FL_READ_ONLY
if (callType!=FL_PROTECTION_GET_TYPE) { if((returnedFlags & KEY_INSERTED) == 0) /* Make sure the key is inserted */ { DEBUG_PRINT(("Please insert key before trying to change protection attributes\r\n")); return flHWProtection; }
if ((flash->protectionBoundries == NULL ) || (flash->protectionSet == NULL ) || (flash->protectionKeyInsert == NULL )) { DEBUG_PRINT(("Protection routine are NULL\r\n")); return flFeatureNotSupported; }
if (!(LE4(volume->flags) & CHANGEABLE_PROTECTION)) return flUnchangeableProtection;
/* The DPS of unprotected partitions is protected by a default key */ flash->protectionKeyInsert(flash,area,(byte *)DEFAULT_KEY); } #endif /* FL_READ_ONLY */
} /* End of protection change routine */
/* Execute each of the posible protection routines */
switch (callType) { case FL_PROTECTION_GET_TYPE:
ioreq->irFlags |= PROTECTABLE; if (LE4(volume->flags) & CHANGEABLE_PROTECTION) { if ((ioreq->irFlags & CHANGEABLE_PROTECTION)==0) { DEBUG_PRINT(("Debug: INFTL reported CHANGEALE protection, but MTD does not allow it.\r\n")); return flBadFormat; } } else { ioreq->irFlags &= (~CHANGEABLE_PROTECTION); } break;
#ifndef FL_READ_ONLY
case FL_PROTECTION_SET_LOCK:
if (tempFlags & LOCK_ENABLED) { ioreq->irFlags |= LOCK_ENABLED; } else { ioreq->irFlags &=~LOCK_ENABLED; } for (floorNo = 0 ; floorNo < flash->noOfFloors ; floorNo++) { /* Find boundries */ status = flash->protectionBoundries(flash, area,&low,&high,floorNo); if(status == flOK) /* Set new protection values */ { status = flash->protectionSet(flash,area, (word)((high == 0) ? PROTECTABLE : ioreq->irFlags), low,high,NULL, (byte)((floorNo == flash->noOfFloors - 1) ? COMMIT_PROTECTION : DO_NOT_COMMIT_PROTECTION),floorNo); } if(status != flOK) return status; } break;
case FL_PROTECTION_CHANGE_KEY:
for (floorNo = 0 ; floorNo < flash->noOfFloors ; floorNo++) { /* Find boundries */ status = flash->protectionBoundries(flash,area, &low,&high,floorNo); if(status == flOK) /* Set new protection values */ { status = flash->protectionSet(flash,area, (word)((high == 0) ? PROTECTABLE : ioreq->irFlags), low,high,(byte FAR1*)ioreq->irData, (byte)((floorNo == flash->noOfFloors - 1) ? COMMIT_PROTECTION : DO_NOT_COMMIT_PROTECTION),floorNo); } if(status != flOK) return status; } break;
case FL_PROTECTION_CHANGE_TYPE:
/* Only read and or write protected types are available */ if (((tempFlags & (READ_PROTECTED | WRITE_PROTECTED | PROTECTABLE)) != tempFlags) || ((tempFlags & PROTECTABLE) == 0)) return flBadParameter;
for (floorNo = 0 ; floorNo < flash->noOfFloors ; floorNo++) { /* Find boundries */ status = flash->protectionBoundries(flash,area, &low,&high,floorNo); if(status == flOK) /* Set new protection values */ { status = flash->protectionSet(flash,area, (word)((high == 0) ? PROTECTABLE : tempFlags), low,high,NULL,(byte)((floorNo == flash->noOfFloors - 1) ? COMMIT_PROTECTION : DO_NOT_COMMIT_PROTECTION),floorNo); } if(status != flOK) return status; } break;
#endif /* FL_READ_ONLY */
case FL_PROTECTION_REMOVE_KEY:
if (flash->protectionKeyRemove == NULL) { DEBUG_PRINT(("Protection routine is NULL\r\n")); return flFeatureNotSupported; } return flash->protectionKeyRemove(flash,area);
case FL_PROTECTION_INSERT_KEY:
if (flash->protectionKeyInsert == NULL) { DEBUG_PRINT(("Protection routine is NULL\r\n")); return flFeatureNotSupported; } return flash->protectionKeyInsert(flash,area,(byte FAR1*)ioreq->irData); default: break; } /* protection routines */ return flOK; }
#endif /* HW_PROTECTION */
/*----------------------------------------------------------------------*/ /* p r e M o u n t I N F T L */ /* */ /* Common entry point to all tl routines that may be perfomed before */ /* the volume is mounted (except for the format routine */ /* */ /* Parameters: */ /* callType : Enum type of posible routines */ /* ioreq : Input and output request packet */ /* flash : Flash media mounted on this socket */ /* */ /* Returns: */ /* FLStatus : 0 on success, otherwise failure */ /* */ /*----------------------------------------------------------------------*/
static FLStatus preMountINFTL(FLFunctionNo callType, IOreq FAR2* ioreq ,FLFlash* flash, FLStatus* status) { BNANDVolumeHeaderRecord* volume; ANANDUnitNo originalUnits[MAX_NO_OF_FLOORS]; Bnand vol = vols + FL_GET_SOCKET_FROM_HANDLE(ioreq); byte partition = FL_GET_PARTITION_FROM_HANDLE(ioreq); FLStatus tmpStatus;
DEBUG_PRINT(("Debug: starting INFTL preMount operation.\r\n"));
/*************************/ /* Find the media header */ /*************************/
tmpStatus = initINFTLbasic(&vol,flash); if(tmpStatus == flOK) tmpStatus = retrieveHeader(&vol,originalUnits,FALSE,TRUE); if(tmpStatus != flOK) return tmpStatus;
*status = flOK;
if (callType == FL_COUNT_VOLUMES) /* get number of BDTL volumes routine */ { ioreq->irFlags = (byte)LE4(((BNANDBootRecord *)inftlBuffer)-> noOfBDTLPartitions); return flOK; }
/* Check media header for the specific partition */ volume = (BNANDVolumeHeaderRecord*)(inftlBuffer + sizeof(BNANDBootRecord));
if (partition > LE4(((BNANDBootRecord *)inftlBuffer)->noOfBDTLPartitions)) { *status = flBadDriveHandle; } else { volume += (LE4(((BNANDBootRecord *)inftlBuffer)->noOfBinaryPartitions)+ partition); }
switch (callType) { #if (defined(QUICK_MOUNT_FEATURE) && !defined(FL_READ_ONLY))
case FL_CLEAR_QUICK_MOUNT_INFO: if(*status != flBadDriveHandle) /* Valid partition number */ *status = flash->erase(flash, (word)(LE4(volume->firstQuickMountUnit) << vol.blockMultiplierBits), (word)((LE4(volume->firstUnit) - LE4(volume->firstQuickMountUnit)) << vol.blockMultiplierBits)); break; #endif /* QUICK_MOUNT_FEATURE AND NOT FL_READ_ONLY */
#ifdef HW_PROTECTION
case FL_PROTECTION_GET_TYPE: /* Protection routines */ case FL_PROTECTION_SET_LOCK: case FL_PROTECTION_CHANGE_KEY: case FL_PROTECTION_CHANGE_TYPE: case FL_PROTECTION_INSERT_KEY: case FL_PROTECTION_REMOVE_KEY: if(*status == flBadDriveHandle) /* Valid partition number */ break; *status = protectionINFTL(&vol,volume,ioreq,callType); break; #endif /* HW_PROTECTION */
default: /* not supported pre mount routine */ return flBadParameter; } /* end of callType switch */ return flOK; /* This TL took responsibility of this call */ }
#if (defined(FORMAT_VOLUME) && !defined(FL_READ_ONLY))
/*----------------------------------------------------------------------*/ /* e r a s e U n i t */ /* */ /* Erase the unit while retaining the erase count. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* unitNo : Physical unit to format */ /* */ /* the progress is repored by the progressCallBack routine */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
static FLStatus eraseUnit(Bnand vol, ANANDUnitNo unitNo, FLProgressCallback progressCallback) { word eraseMark; dword eraseCount; FLStatus status;
if (progressCallback) { status = (*progressCallback)((word)(vol.flash->noOfChips * (vol.flash->chipSize >> vol.unitSizeBits)),(word)(unitNo+1)); if(status != flOK) { DFORMAT_PRINT(("Debug: ERROR failed reporting progress callback.\r\n")); dismountINFTL(&vol); /*Free tables must be done after call to initTables*/ return status; } } status = getUnitTailer(&vol,unitNo,&eraseMark,&eraseCount,UNIT_TAILER_OFFSET); if(status == flOK) { status = vol.flash->erase(vol.flash,(word)(unitNo << vol.blockMultiplierBits), (word)(1 << vol.blockMultiplierBits)); } if (status == flOK) { eraseCount++; if (eraseCount == 0) /* was hex FF's */ eraseCount++; status = setUnitTailer(&vol,unitNo,ERASE_MARK,eraseCount,UNIT_TAILER_OFFSET); } if (status != flOK) { DEBUG_PRINT(("Debug: ERROR failed formating unit.\r\n")); markUnitBad(&vol,unitNo); /* make sure unit format is not valid */ dismountINFTL(&vol); /*Free tables must be done after call to initTables*/ }
return status; }
/*----------------------------------------------------------------------*/ /* f o r m a t I N F T L */ /* */ /* Perform INFTL Format. */ /* */ /* Parameters: */ /* volNo : Volume serial no. */ /* fp : Address of FormatParams structure to use */ /* flash : Flash media mounted on this socket */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/
static FLStatus formatINFTL(unsigned volNo, TLFormatParams *fp, FLFlash *flash) { Bnand vol = &vols[volNo]; /* TL record */ BDTLPartitionFormatParams FAR2* bdtl = fp->BDTLPartitionInfo; /* bdtl input */ BinaryPartitionFormatParams FAR2* binary = fp->binaryPartitionInfo; /* binary input */ BNANDBootRecord * mediaHeader; /* Disk header record */ BNANDVolumeHeaderRecord * volumeHeader; /* volume header record */ BNANDVolumeHeaderRecord * volumeHeader2; /* volume header record */ CardAddress iBlock; /* Block counter index */ ANANDUnitNo iUnit; /* unit index for loops */ ANANDUnitNo unitsNeededForVolume; /* good units needed for volume */ ANANDUnitNo floorGarantiedUnitsLeft; /* garantied units not yet distributed */ ANANDUnitNo binaryUnitsInFloor; /* number of binary unit in this floor */ ANANDUnitNo noOfUnitsPerFloor[MAX_NO_OF_FLOORS]; ANANDUnitNo floorGarantiedUnits[MAX_NO_OF_FLOORS]; /* garantied good units in floor */ ANANDUnitNo originalUnits[MAX_NO_OF_FLOORS]; /* unit no' of the original units */ ANANDUnitNo goodUnits[MAX_NO_OF_FLOORS]; /* no of good blocks in floor */ ANANDUnitNo skipedUnits; /* number of good units to leave as unfrmated */ ANANDUnitNo goodBlocks; FLStatus status; /* status of TrueFFS routines */ Sbyte volumeNo; /* current volume index */ Sbyte noOfVolumes = fp->noOfBDTLPartitions + fp->noOfBinaryPartitions; dword index; /* general loops index */ dword sizeOfLastBinary; byte * firstVolumePtr; byte headersBuffer[sizeof(inftlBuffer)]; byte floorNo; /* current floor index */ byte noOfFloors; byte temp; byte lastBinaryFloor; #ifdef HW_PROTECTION
ANANDUnitNo volumeStart; word protectionType = 0; /* Initialized to remove warrnings */ /* Highest floor to leave DPS untouched - per DPS */ byte binaryFloorOfDPS[MAX_PROTECTED_PARTITIONS]; byte changeableProtection = 0; byte unchangeableProtection = 0; byte protectionKey[PROTECTION_KEY_LENGTH]; #endif /* HW_PROTECTION */
#ifdef WRITE_EXB_IMAGE
BinaryPartitionFormatParams exbBinaryPartition; byte exbSign[BINARY_SIGNATURE_LEN]; byte noOfBinary = fp->noOfBinaryPartitions; #endif /* WRITE_EXB_IMAGE */
#ifdef QUICK_MOUNT_FEATURE
Sword quickMount[MAX_VOLUMES_PER_DOC];
/* fp->flags |= TL_QUICK_MOUNT_FORMAT; */ for (volumeNo=0;volumeNo<MAX_VOLUMES_PER_DOC;volumeNo++) { quickMount[volumeNo]=0; } #endif /* QUICK_MOUNT_FEATURE */
/*-------------------------------------------------------
* Media header || Binary 0 + exb file || Binary 1,.. || *-------------------------------------------------------*/
/*-----------------------------------------------------
* quick mount + BDTL 0 || quick mount + BDTL 1, ... || *-----------------------------------------------------*/
DEBUG_PRINT(("Debug: starting INFTL format by verifying arguments.\r\n"));
tffsset(&vol,0,sizeof(vol));
/* Check that there is up to 4 volumes on the device provided one is a
* BDTL volume. If there is an exb file to be placed it would require * at least 1 binary volume */
if ((fp->noOfBDTLPartitions < 1) || #ifdef WRITE_EXB_IMAGE
((fp->exbLen > 0) && (fp->noOfBDTLPartitions == MAX_VOLUMES_PER_DOC)) || #endif /* WRITE_EXB_IMAGE */
(noOfVolumes > MAX_VOLUMES_PER_DOC)) { DFORMAT_PRINT(("ERROR - There can be up to 4 volumes while at least one is a BDTL.\r\n")); return flBadParameter; }
/*******************/ /* Initialization */ /*******************/
checkStatus(initINFTL(&vol,flash)); /* Initialize variables */ noOfFloors = flash->noOfFloors; vol.noOfVirtualUnits = 0;
#ifdef FL_MALLOC
status = initTables(&vol); /* Allocate tables */ #else
status = initTables(&vol,0); #endif /* FL_MALLOC */
if(status != flOK) { DFORMAT_PRINT(("ERROR - Failed allocating memory for INFTL tables.\r\n")); dismountINFTL(&vol); /*Free tables must be done after call to initTables*/ return status; } /* Calculate units per floor */ noOfUnitsPerFloor[0] = (ANANDUnitNo)(vol.flash->chipSize >> vol.unitSizeBits) * ((vol.flash->noOfChips + (vol.flash->noOfChips % vol.flash->noOfFloors)) / vol.flash->noOfFloors); floorGarantiedUnits[0] = (ANANDUnitNo)((dword)((dword)fp->percentUse * (dword)noOfUnitsPerFloor[0]) / 100 - 1); /* - header */ for (index=0;index+1<vol.flash->noOfFloors;index++) { noOfUnitsPerFloor[index] = noOfUnitsPerFloor[0]; floorGarantiedUnits[index] = floorGarantiedUnits[0]; } /* Last floor might have diffrent number of chips */ noOfUnitsPerFloor[index] = (ANANDUnitNo)(vol.noOfUnits - (index*noOfUnitsPerFloor[0])); floorGarantiedUnits[index] = (ANANDUnitNo)((dword)fp->percentUse * noOfUnitsPerFloor[index] / 100 - 1);
/********************************************************************/ /* Read BBT , find headers location and count number of good blocks */ /********************************************************************/
status = retrieveHeader (&vol ,originalUnits,TRUE, (fp->flags & FL_LEAVE_BINARY_AREA) ? TRUE : FALSE); tffscpy(headersBuffer,inftlBuffer,sizeof(headersBuffer)); mediaHeader = (BNANDBootRecord *)headersBuffer; firstVolumePtr= headersBuffer + sizeof(BNANDBootRecord);
/* If previous header was not found it is not possible to leave
* the previous binary partition. */
if(status == flBadFormat) { if(fp->flags & FL_LEAVE_BINARY_AREA) { DFORMAT_PRINT(("NOTE - Previous binary partition data could not be found.\r\n")); fp->flags &= ~FL_LEAVE_BINARY_AREA; } }
if(status == flBadBBT) { DFORMAT_PRINT(("ERROR - Unreadable Bad Blocks Table.\r\n")); dismountINFTL(&vol); /*Free tables must be done after call to initTables*/ return status; }
if(vol.physicalUnits[0] == BBT_BAD_UNIT) { DFORMAT_PRINT(("ERROR - IPL block is bad.\r\n")); dismountINFTL(&vol); /*Free tables must be done after call to initTables*/ return flBadIPLBlock; }
/* Loop over the floors of the media while counting the good units
* the good units are needed for the transfere unit calculation. * In addition change the MTD values of Bad unit ro INFTL */
for (floorNo = 0 , iUnit = 0 , index = 0 ; floorNo<noOfFloors ; floorNo++) { index += noOfUnitsPerFloor[floorNo]; goodBlocks = noOfUnitsPerFloor[floorNo]; for (;iUnit<index;iUnit++) { if (vol.physicalUnits[iUnit]!=BBT_GOOD_UNIT) { goodBlocks--; vol.physicalUnits[iUnit] = UNIT_BAD; } else { vol.physicalUnits[iUnit] = ANAND_UNIT_FREE; } } goodBlocks--; /* Do not count one unit for floor header */ if (goodBlocks < floorGarantiedUnits[floorNo]) { DFORMAT_PRINT(("ERROR - Too many bad block on flash->\r\n")); dismountINFTL(&vol); /*Free tables must be done after call to initTables*/ return flVolumeTooSmall; } /* Save amount of good blocks for later */ goodUnits[floorNo] = goodBlocks; }
/************************************/ /* Construct binary volumes headers */ /************************************/
volumeHeader = (BNANDVolumeHeaderRecord *) firstVolumePtr; goodBlocks = 0; /* good units already used (counting headers) */ skipedUnits = 0; /* good units to leave unformated (not counting headers) */ #ifdef HW_PROTECTION
for(index = 0 ; index < MAX_PROTECTED_PARTITIONS ; index++) binaryFloorOfDPS[index] = MAX_NO_OF_FLOORS; /* Invalid floor no */ #endif /* HW_PROTECTION */
if(fp->flags & FL_LEAVE_BINARY_AREA) /* Previous Boot area is kept */ { if(fp->bootImageLen == -1) /* kept entirely */ { for (index = 0;index < LE4(mediaHeader->noOfBinaryPartitions); index++,volumeHeader++) { /* not including headers */ skipedUnits += (ANANDUnitNo)LE4(volumeHeader->virtualUnits); #ifdef HW_PROTECTION
if (LE4(volumeHeader->flags) & PROTECTABLE) { if (LE4(volumeHeader->protectionArea) >= flash->totalProtectedAreas) { tffsset(headersBuffer,0,sizeof(headersBuffer)); DFORMAT_PRINT(("ERROR - Previous Binary partition had a bad protection area field.\r\n")); dismountINFTL(&vol); /*Free tables must be done after call to initTables*/ return flBadFormat; } binaryFloorOfDPS[LE4(volumeHeader->protectionArea)] = skipedUnits / floorGarantiedUnits[0]; } #endif /* HW_PROTECTION */
} } else /* erase all previous binary partitions */ { tffsset(headersBuffer,0,sizeof(headersBuffer)); if(fp->bootImageLen != 0) DFORMAT_PRINT(("ERROR - Requested Binary partition size is diffrent then previous one.\r\n")); } /* clean the bdtl entries */ tffsset(volumeHeader, 0,(word)(sizeof(headersBuffer)-((byte *)volumeHeader-headersBuffer))); goodBlocks = skipedUnits; /* Update the number of partitions with the binary partitions */ fp->noOfBinaryPartitions = (byte)(volumeHeader - (BNANDVolumeHeaderRecord *) firstVolumePtr); noOfVolumes = fp->noOfBDTLPartitions + fp->noOfBinaryPartitions; if(noOfVolumes > MAX_VOLUMES_PER_DOC) { DFORMAT_PRINT(("ERROR - There can be up to 4 volumes while at least one is a BDTL.\r\n")); dismountINFTL(&vol); /*Free tables must be done after call to initTables*/ return flBadParameter; } } else /* Apply binary area format parameters */ { tffsset(headersBuffer,0,sizeof(headersBuffer)); /* reset all binary area */ for (volumeNo=0;volumeNo<fp->noOfBinaryPartitions; volumeNo++,binary++,volumeHeader++) { binary->length = roundToUnits(binary->length); if (binary->length == 0) { #ifdef WRITE_EXB_IMAGE
if(((fp->exbLen == 0) && (volumeNo == 0)) || ( volumeNo != 0 ) ) #endif /* WRITE_EXB_IMAGE */
{ DFORMAT_PRINT(("ERROR - BINARY partition length should not be 0.\r\n")); dismountINFTL(&vol); /*Free tables must be done after call to initTables*/ return flBadParameter; } } toLE4(volumeHeader->virtualUnits,binary->length); #ifdef HW_PROTECTION
toLE4(volumeHeader->flags ,INFTL_BINARY | binary->protectionType); #else
toLE4(volumeHeader->flags ,INFTL_BINARY); #endif /* HW_PROTECTION */
goodBlocks += (ANANDUnitNo)LE4(volumeHeader->virtualUnits); /* In Units */ } binary = fp->binaryPartitionInfo;
/* Add EXB area */ #ifdef WRITE_EXB_IMAGE
if (fp->exbLen>0) { fp->exbLen = roundToUnits(fp->exbLen); goodBlocks += (ANANDUnitNo)fp->exbLen; tffscpy(exbSign,SIGN_SPL,BINARY_SIGNATURE_NAME); tffsset(exbSign+BINARY_SIGNATURE_NAME,'F',BINARY_SIGNATURE_NAME); if (fp->noOfBinaryPartitions > 0) /* Add firmware blocks */ { toLE4(((BNANDVolumeHeaderRecord*)firstVolumePtr)->virtualUnits, LE4(((BNANDVolumeHeaderRecord*)firstVolumePtr)->virtualUnits) + fp->exbLen); } else /* Must create a binary partition just for firmware */ { fp->noOfBinaryPartitions = 1; toLE4(volumeHeader->virtualUnits,fp->exbLen ); #ifdef HW_PROTECTION
if(noOfBinary) /* Do not use binary record unless it was allocated */ { toLE4(volumeHeader->flags ,INFTL_BINARY | binary->protectionType); exbBinaryPartition.protectionType = binary->protectionType; } else #endif /* HW_PROTECTION */
toLE4(volumeHeader->flags ,INFTL_BINARY); binary = &exbBinaryPartition; volumeHeader++; noOfVolumes++; } fp->exbLen <<= vol.blockMultiplierBits; } #endif /* WRITE_EXB_IMAGE */
toLE4(mediaHeader->noOfBinaryPartitions, fp->noOfBinaryPartitions); }
binaryUnitsInFloor = goodBlocks % floorGarantiedUnits[0]; lastBinaryFloor = goodBlocks / floorGarantiedUnits[0]; goodBlocks += noOfFloors;
/********************************/ /* Construct Main media header */ /********************************/
tffscpy(mediaHeader->bootRecordId,"BNAND", TL_SIGNATURE); tffscpy(&(mediaHeader->osakVersion),TrueFFSVersion,sizeof(TrueFFSVersion)); toLE4(mediaHeader->percentUsed , fp->percentUse ); toLE4(mediaHeader->blockMultiplierBits , vol.blockMultiplierBits); toLE4(mediaHeader->formatFlags , 0 ); toLE4(mediaHeader->noOfBDTLPartitions , fp->noOfBDTLPartitions ); /* noOfBinaryPartitions was already determinded */
/**********************************/ /* Construct BDTL volumes headers */ /**********************************/
for (volumeNo=1;(byte)volumeNo<LE4(mediaHeader->noOfBDTLPartitions); bdtl++,volumeNo++,volumeHeader++) { if (bdtl->length < flash->erasableBlockSize) { DFORMAT_PRINT(("ERROR - INFTL partition length should not be least one unit long.\r\n")); dismountINFTL(&vol); /*Free tables must be done after call to initTables*/ return flBadParameter; } toLE4(volumeHeader->virtualUnits , roundToUnits(bdtl->length)); toLE4(volumeHeader->spareUnits , bdtl->noOfSpareUnits ); toLE4(volumeHeader->flags , INFTL_BDTL ); goodBlocks += (ANANDUnitNo)LE4(volumeHeader->virtualUnits) + bdtl->noOfSpareUnits; #ifdef HW_PROTECTION
toLE4(volumeHeader->flags,LE4(volumeHeader->flags) | bdtl->protectionType); #endif /* HW_PROTECTION */
#ifdef QUICK_MOUNT_FEATURE
/* if (fp->flags & TL_QUICK_MOUNT_FORMAT) */ { dword quickMountBytes;
quickMountBytes = (ANANDUnitNo)(LE4(volumeHeader->virtualUnits) + LE4(volumeHeader->spareUnits)); quickMountBytes += ((quickMountBytes / noOfUnitsPerFloor[0] + 1) * noOfUnitsPerFloor[0] * (100L-fp->percentUse) / 100L); quickMountBytes *= RAM_FACTOR; quickMountBytes += flash->pageSize; quickMount[volumeNo-1] = roundToUnits(quickMountBytes); goodBlocks += quickMount[volumeNo-1]; } #endif /* QUICK_MOUNT_FEATURE */
}
/* The size of the last partition is defined by the media itself */
goodBlocks += fp->noOfSpareUnits; for(index=0,floorNo=0;floorNo<noOfFloors;floorNo++) { index+=floorGarantiedUnits[floorNo]; } toLE4(volumeHeader->flags , INFTL_BDTL | INFTL_LAST); toLE4(volumeHeader->spareUnits , fp->noOfSpareUnits ); toLE4(volumeHeader->lastUnit , vol.noOfUnits-1); toLE4(volumeHeader->virtualUnits , index + noOfFloors - goodBlocks); #ifdef QUICK_MOUNT_FEATURE
/* if (fp->flags & TL_QUICK_MOUNT_FORMAT) */ { dword quickMountBytes;
quickMountBytes = (ANANDUnitNo)(LE4(volumeHeader->virtualUnits) + LE4(volumeHeader->spareUnits)); quickMountBytes += ((quickMountBytes / noOfUnitsPerFloor[0] + 1) * noOfUnitsPerFloor[0] * (100L-fp->percentUse) / 100L); quickMountBytes *= RAM_FACTOR; quickMountBytes += flash->pageSize;
toLE4(mediaHeader->formatFlags , QUICK_MOUNT); quickMount[volumeNo-1] = roundToUnits(quickMountBytes); toLE4(volumeHeader->virtualUnits , LE4(volumeHeader->virtualUnits) - quickMount[volumeNo-1]); } #endif /* QUICK_MOUNT_FEATURE */
if ((LE4(volumeHeader->virtualUnits) < 1) || (LE4(volumeHeader->virtualUnits) > vol.noOfUnits)) { DFORMAT_PRINT(("ERROR - Partition lengths could not be placed on the media.\r\n")); dismountINFTL(&vol); /*Free tables must be done after call to initTables*/ return flVolumeTooSmall; }
/*******************************************/ /* Allocate protection area to the volumes */ /*******************************************/
#ifdef HW_PROTECTION
toLE4(volumeHeader->flags,LE4(volumeHeader->flags) | /* Ignore other flags */ fp->protectionType);
for (volumeNo = noOfVolumes, volumeHeader = (BNANDVolumeHeaderRecord *)firstVolumePtr; volumeNo>0;volumeNo--,volumeHeader++) { if (LE4(volumeHeader->flags) & PROTECTABLE) { if (flash->protectionSet == NULL) { DFORMAT_PRINT(("ERROR - setting protection routines are not available.\r\n")); dismountINFTL(&vol); /*Free tables must be done after call to initTables*/ return flFeatureNotSupported; } if (LE4(volumeHeader->flags) & CHANGEABLE_PROTECTION) /* last areas (n,n-1,n-2..)*/ { changeableProtection++; /* Number of protected partitions */ toLE4(volumeHeader->protectionArea , flash->totalProtectedAreas - changeableProtection); } else /* first areas (0,1,2,..) */ { toLE4(volumeHeader->protectionArea , unchangeableProtection); unchangeableProtection++; } } }
/* Make sure the device support enough protected areas */
if ((changeableProtection > flash->changeableProtectedAreas) || (unchangeableProtection + changeableProtection > flash->totalProtectedAreas)) { DFORMAT_PRINT(("ERROR - too many protected areas.\r\n")); dismountINFTL(&vol); /*Free tables must be done after call to initTables*/ return flBadParameter; }
/* Clear previous protection areas and write protect them */
for (index=0;index<flash->totalProtectedAreas;index++) { /* Send default key to open all protected areas */ status = flash->protectionKeyInsert(flash,(byte)index,(byte FAR1 *)DEFAULT_KEY); }
/* Clear all floors execpt for those that have only binary partitions */ for (floorNo=skipedUnits/floorGarantiedUnits[0];floorNo<noOfFloors;floorNo++) { for (index=0;index<flash->totalProtectedAreas;index++) { if(binaryFloorOfDPS[index] == floorNo) break; /* Belongs to a binary partition that must be left */ status = flash->protectionSet(flash,(byte)index, WRITE_PROTECTED, /* Write protect */ (index+1) << flash->erasableBlockSizeBits, /* Low address */ (index+1) << flash->erasableBlockSizeBits, /* High address */ (byte *)DEFAULT_KEY,DO_NOT_COMMIT_PROTECTION,floorNo); if (status != flOK) { DFORMAT_PRINT(("ERROR - FAILED clearing previous protection areas.\r\n")); dismountINFTL(&vol); /*Free tables must be done after call to initTables*/ return status; } } } #endif /* HW_PROTECTION */
/***********************************/ /* Actual format of the partitions */ /***********************************/
DEBUG_PRINT(("Debug: INFTL format arguments have been verified, starting format.\r\n"));
unitsNeededForVolume = 0; floorNo = skipedUnits / floorGarantiedUnits[0]; volumeNo = -1; /* bdtl volume number */ bdtl = fp->BDTLPartitionInfo; iUnit = originalUnits[floorNo]+1; /* write after the first header */ volumeHeader = (BNANDVolumeHeaderRecord *) firstVolumePtr; /* location of first partition header */ floorGarantiedUnitsLeft = floorGarantiedUnits[floorNo]; /* floors units left */
/* Skip the boot image area if the FL_LEAVE_BINARY_AREA was set */
if (skipedUnits > 0 ) { skipedUnits %= floorGarantiedUnits[0]; /* skipped units on the current floor */ floorGarantiedUnitsLeft -= skipedUnits; /* floors units left */ volumeHeader += (word)LE4(mediaHeader->noOfBinaryPartitions); sizeOfLastBinary = LE4((volumeHeader-1)->virtualUnits);
/* Clear previous BDTL entries and adjust binary partition */ for (index = 0 ; index < floorNo ; index++) { /* read previous floor header */ status = flash->read(flash,((CardAddress)originalUnits[index] << vol.unitSizeBits) + sizeof(BNANDBootRecord), firstVolumePtr, sizeof(BNANDVolumeHeaderRecord) * (fp->noOfBinaryPartitions) ,0);
if(status != flOK) { DFORMAT_PRINT(("ERROR - FAILED reading previous INFTL header.\r\n")); dismountINFTL(&vol); /*Free tables must be done after call to initTables*/ return status; } toLE4((volumeHeader-1)->virtualUnits , sizeOfLastBinary); /* adjust virtual size */ if((index == (dword)(floorNo - 1)) && (skipedUnits == 0)) toLE4((volumeHeader-1)->lastUnit , ((index+1) * noOfUnitsPerFloor[0]) - 1); /* adjust last block */
/* Erase and write the new updated header */ status = eraseUnit(&vol,originalUnits[index],fp->progressCallback); if(status != flOK) { DFORMAT_PRINT(("ERROR - FAILED erasing previous INFTL header.\r\n")); return status; } for (temp = 0 ; temp < NO_OF_MEDIA_HEADERS ; temp++) { status = flash->write(flash,((CardAddress)originalUnits[index] << vol.unitSizeBits) + temp * HEADERS_SPACING, headersBuffer, sizeof(headersBuffer),EDC); if (status != flOK) { DFORMAT_PRINT(("ERROR - FAILED rewriting INFTL header.\r\n")); dismountINFTL(&vol); /*Free tables must be done after call to initTables*/ return status; } } }
/* Skip to first BDTL block */ for (;skipedUnits > 0;skipedUnits--) { do { iUnit++; }while (vol.physicalUnits[iUnit] == UNIT_BAD); }
/* Save last binary unit for floors header */ if(iUnit != originalUnits[floorNo]+1) toLE4((volumeHeader-1)->lastUnit , iUnit - 1); /* adjust last block */ }
do /* loop over floors */ { goodBlocks = goodUnits[floorNo]; /* Good blocks of the floor */
/* Format as many volumes as posible in this floor */
while((floorGarantiedUnitsLeft > 0) && (noOfVolumes >= 0)) { if (unitsNeededForVolume == 0) /* Read header of next volume */ { noOfVolumes--; toLE4(volumeHeader->firstQuickMountUnit , iUnit); unitsNeededForVolume = (ANANDUnitNo)(LE4(volumeHeader->virtualUnits) + LE4(volumeHeader->spareUnits)); #ifdef QUICK_MOUNT_FEATURE
if (LE4(volumeHeader->flags) & INFTL_BDTL) { volumeNo++; unitsNeededForVolume +=quickMount[volumeNo]; } #else
toLE4(volumeHeader->firstUnit , iUnit); #endif /* QUICK_MOUNT_FEATURE */
/* Copy protection key and type */
#ifdef HW_PROTECTION
if (LE4(volumeHeader->flags) & INFTL_BDTL) { if (LE4(volumeHeader->flags) & INFTL_LAST) { tffscpy(protectionKey,fp->protectionKey,PROTECTION_KEY_LENGTH); protectionType = fp->protectionType; } else { tffscpy(protectionKey,bdtl->protectionKey,PROTECTION_KEY_LENGTH); protectionType = bdtl->protectionType; } } else /* Binary */ { tffscpy(protectionKey,binary->protectionKey,PROTECTION_KEY_LENGTH); protectionType = binary->protectionType; } #endif /* HW_PROTECTION */
}
/* update format indexs. */
if (unitsNeededForVolume > floorGarantiedUnitsLeft) { /* Volume does not fits in floor */ index = floorGarantiedUnitsLeft; unitsNeededForVolume -= floorGarantiedUnitsLeft; floorGarantiedUnitsLeft = 0; } else { /* Entire volume fits in floor */ index = unitsNeededForVolume; floorGarantiedUnitsLeft -= unitsNeededForVolume; unitsNeededForVolume = 0; }
/*******************************************/ /* Format the volumes blocks of this floor */ /*******************************************/
#ifdef HW_PROTECTION
volumeStart = iUnit; #endif /* HW_PROTECTION */
if (LE4(volumeHeader->flags) & INFTL_BDTL) /* BDTL VOLUME FORMAT */ { /* Add transfer units */
if (floorNo == lastBinaryFloor) { index *= (goodBlocks - binaryUnitsInFloor); index /= (floorGarantiedUnits[floorNo] - binaryUnitsInFloor); } else { index *= goodBlocks; index /= floorGarantiedUnits[floorNo]; }
/* Format units */
while (index > 0) { if (vol.physicalUnits[iUnit] == ANAND_UNIT_FREE) { #ifdef QUICK_MOUNT_FEATURE
/* Set the real first unit after the quick mount data */
if (quickMount[volumeNo] >= 0) { if(quickMount[volumeNo] == 0) { toLE4(volumeHeader->firstUnit , iUnit); } (quickMount[volumeNo])--; } #endif /* QUICK_MOUNT_FEATURE */
index--; status = eraseUnit(&vol, iUnit,fp->progressCallback); if(status != flOK) return status; } else { if (fp->progressCallback) { (*fp->progressCallback)((word)(flash->noOfChips * (flash->chipSize >> vol.unitSizeBits)),(word)(iUnit+1)); } } iUnit++; } if (unitsNeededForVolume == 0) bdtl++; } if (LE4(volumeHeader->flags) & INFTL_BINARY) /* BINARY VOLUME FORMAT */ { toLE4(volumeHeader->firstUnit , LE4(volumeHeader->firstQuickMountUnit)); for (; (index > 0) ; iUnit++) { if (vol.physicalUnits[iUnit] == ANAND_UNIT_FREE) { index--; status = eraseUnit(&vol, iUnit,fp->progressCallback); if(status != flOK) return status;
for (iBlock=0;iBlock<(1UL<<vol.unitSizeBits);iBlock+=(1L<<vol.erasableBlockSizeBits)) { #ifdef WRITE_EXB_IMAGE
if (fp->exbLen > 0) { fp->exbLen--; status = flash->write(flash, 8 + iBlock + ((CardAddress)(iUnit) << vol.unitSizeBits), exbSign,BINARY_SIGNATURE_LEN,EXTRA); } else #endif /* WRITE_EXB_IMAGE */
{ status = flash->write(flash, binary->signOffset + iBlock + ((CardAddress)(iUnit) << vol.unitSizeBits), binary->sign,BINARY_SIGNATURE_NAME,EXTRA); } if (status != flOK) { DFORMAT_PRINT(("ERROR - FAILED formating binary unit.\r\n")); dismountINFTL(&vol); /*Free tables must be done after call to initTables*/ return status; } } } else /* unit is bad */ { if (fp->progressCallback) { (*fp->progressCallback)((word)(flash->noOfChips * (flash->chipSize >> vol.unitSizeBits)),(word)(iUnit+1)); } } } if (unitsNeededForVolume == 0) binary++; } #ifdef HW_PROTECTION
/* Place protection attributes to the volume */
if (LE4(volumeHeader->flags) & PROTECTABLE) {
#ifndef NT5PORT
status = flash->protectionSet(flash,(byte)LE4(volumeHeader->protectionArea), protectionType, ((CardAddress)volumeStart % noOfUnitsPerFloor[floorNo]) << vol.unitSizeBits, ((CardAddress)((iUnit-1) % noOfUnitsPerFloor[floorNo])) << vol.unitSizeBits , (byte *)&protectionKey,DO_NOT_COMMIT_PROTECTION,floorNo); #else /*NT5PORT*/
status = flash->protectionSet(flash,(byte)LE4(volumeHeader->protectionArea), protectionType, ((CardAddress)volumeStart % noOfUnitsPerFloor[floorNo]) << vol.unitSizeBits, ((CardAddress)((iUnit-1) % noOfUnitsPerFloor[floorNo])) << vol.unitSizeBits , protectionKey,DO_NOT_COMMIT_PROTECTION,floorNo); #endif /*NT5PORT*/
if (status != flOK) { DFORMAT_PRINT(("ERROR - FAILED setting protection.\r\n")); dismountINFTL(&vol); /*Free tables must be done after call to initTables*/ return status; } } #endif /* HW_PROTECTION */
if (unitsNeededForVolume == 0) { toLE4(volumeHeader->lastUnit , iUnit - 1); volumeHeader++; } } /* loop until no more units in this floor */
/* Convert any leftovers to BDTL units */
index = noOfUnitsPerFloor[floorNo] + floorNo*noOfUnitsPerFloor[0]; while (iUnit < index) { if (vol.physicalUnits[iUnit] == ANAND_UNIT_FREE) { status = eraseUnit(&vol, iUnit,fp->progressCallback); if(status != flOK) return status; } else { if (fp->progressCallback) { (*fp->progressCallback)((word)(flash->noOfChips * (flash->chipSize >> vol.unitSizeBits)),(word)(iUnit+1)); } } iUnit++; }
/* Update last unit of BDTL partition */ if ((unitsNeededForVolume == 0) && (LE4((volumeHeader-1)->flags) & INFTL_BDTL)) { toLE4((volumeHeader-1)->lastUnit , iUnit-1); }
/***************************/ /* Place the floors header */ /***************************/
iUnit = originalUnits[floorNo];
status = eraseUnit(&vol, iUnit,NULL); if(status != flOK) return status;
for (index = 0 ; index < NO_OF_MEDIA_HEADERS;index++) { status = flash->write(flash,((CardAddress)iUnit << vol.unitSizeBits) + index * HEADERS_SPACING,headersBuffer, sizeof(headersBuffer),EDC); if (status != flOK) { DFORMAT_PRINT(("ERROR - FAILED writing INFTL header.\r\n")); dismountINFTL(&vol); /*Free tables must be done after call to initTables*/ return status; } }
/**************************/ /* Prepare next iteration */ /**************************/
floorNo++; if (floorNo < noOfFloors) { iUnit=originalUnits[floorNo]+1; floorGarantiedUnitsLeft = floorGarantiedUnits[floorNo];
/* Erase physical unit locations to enable independet unit headers */
volumeHeader2 = (BNANDVolumeHeaderRecord *) firstVolumePtr; /* location of first partition header */ while (volumeHeader2<=volumeHeader) { toLE4(volumeHeader2->firstUnit , 0); toLE4(volumeHeader2->lastUnit , 0); toLE4(volumeHeader2->firstQuickMountUnit , 0); volumeHeader2++; } } else { break; } } while (1); /* loop over floors */
DEBUG_PRINT(("Debug: finished INFTL format.\r\n"));
dismountINFTL(&vol); /*Free tables must be done after call to initTables*/ return flOK; }
#endif /* FORMAT_VOLUME && not FL_READ_ONLY */
/*----------------------------------------------------------------------*/ /* f l R e g i s t e r I N F T L */ /* */ /* Register this translation layer */ /* */ /* Parameters: */ /* None */ /* */ /* Returns: */ /* FLStatus : 0 on success, otherwise failure */ /*----------------------------------------------------------------------*/
FLStatus flRegisterINFTL(void) { #ifdef FL_MALLOC
int i; #endif /* FL_MALLOC */
if (noOfTLs >= TLS) return flTooManyComponents;
tlTable[noOfTLs].mountRoutine = mountINFTL; tlTable[noOfTLs].preMountRoutine = preMountINFTL;
#if (defined(FORMAT_VOLUME) && !defined(FL_READ_ONLY))
tlTable[noOfTLs].formatRoutine = formatINFTL; #else
tlTable[noOfTLs].formatRoutine = noFormat; #endif /* FORMAT_VOLUME && not FL_READ_ONLY */
noOfTLs++;
#ifdef FL_MALLOC
/* reset multi sector buffer */ for(i=0;( i < SOCKETS );i++) { multiSectorBufCounter[i] = -1; } /* reset convertion tables */ for(i=0;( i < VOLUMES );i++) { vols[i].physicalUnits = NULL; vols[i].virtualUnits = NULL; #ifdef NFTL_CACHE
/* reset catche tables */ vols[i].ucache = NULL; vols[i].scache = NULL; #endif /* NFTL_CACHE */
} #endif /* FL_MALLOC */
return flOK; }
|