/* * $Log: V:/Flite/archives/TrueFFS5/Src/NFTLLITE.C_V $ * * Rev 1.27 Apr 15 2002 07:38:22 oris * 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. * Improved power failure resistant: * - Added support for VERIFY_ERASED_SECTOR compilation flag - make sure a sector is free before writing it. * - writeAndCheck() - * 1) Added an option to verify that the sector is erased before writing. * It subject to the FL_UPS/FL_OFF/FL_ON modes of the flVerifyWrite environment variable. * 2) Added support for partition > 0. * - getUnitData() - Bug fix - in case of invalid replacement unit if we are mounting or formatting store physical unit number in * invalidReplacement field of the Anand record otherwise mark head of chain as frozen and not the unit itself. * - Bug fix - Added logic to find end of endless loops - findEndOfEndlessLoop() * - virtual2Physical() - Added call to findEndOfEndlessLoop() if needed and return address of next free sector (used verifySector()). * - initNFTL() - Added initialization of TL verifyBuffer buffer and invalidReplacement fields. * - Separated the search for a free unit in physical table from allocateUnit() into dedicated routine findFreeUnit(). * - foldUnit() * 1) Added support for partition > 0. * 2) Prevent a recursive call to foldUnit by changing allocateUnit() with findFreeUnit(). * 3) Added logic for endless loops. * 4) Added check that last sector of chain can be used. * - foldBestChain() - revised the entire routine * 1) If folding not in place was forced free all frozen units. * 2) Validate that a unit was actually freed. * - mountUnit() - * 1) In case of bad virtual unit number format the unit . An invalid virtual unit might be a result of power failures. * - allocateAndWrite() - Bug fix - sector count might be incorrectly updated in case folding found a sector with bad EDC. * - mountNFTL - * 1) Moved badFormat initialization to the end of the routine so that getUnitData() might know if it was called by mount routine. * 2) Made sure there is at least 1 free unit. * 3) Fold chains with invalid pointer in their end of chain - caused by power failures. * - Added MAKE_SURE_IGNORE_HAS_BAD_EDC compilation flag - make sure that the sector is really marked * - verifySector() - was completely revised. * * Rev 1.26 Feb 19 2002 21:00:56 oris * Compilation errors - NO_READ_BBT_CODE * Removed warnings. * Replaced TL_LEAVE_BINARY_AREA with FL_LEAVE_BINARY_AREA * * Rev 1.25 Jan 29 2002 20:09:58 oris * Bug fix - if an invalid sector flag is found in getSectorFlags routine and read operation failed, SECTOR_IGNORED should have been returned. * * Rev 1.24 Jan 23 2002 23:33:56 oris * Improved markAsIgnored routine. * Changed DFORMAT_PRINT syntax. * * Rev 1.23 Jan 20 2002 20:29:26 oris * Remove warnings * Bug fix - Missing cache buffers allocation check in formatUnit. * * Rev 1.22 Jan 17 2002 23:04:22 oris * Added onesCount routine - number of bits in a byte. * Improved FL_READ_ONLY compilation mode. * Flash field of the TL was changed to a pointer. * Added check verifyVolume routine to issue a longer, but safer mount. * Moved RAM tables to far heap to allow BIOS driver to allocate far memory heap even when compiled as tiny model: * - InitTables : allocation was change from FL_MALLOC to FL_FAR_MALLOC. * - mountUnit : changes pointer to virtual table to be FAR. * - mountNFTL : * - changes pointer to virtual table to be FAR. * - allocation of cache tables was change from FL_MALLOC to FL_FAR_MALLOC. * - dismount : use FL_FAR_FREE instead of FL_FREE * formatUnit routine improved re-initialization of sector cache. * Changed flPolicy to be socket specific. * Added \r to all DEBUG_PRINT. * writeSector routine if this sector is the cached sector then force re-mapping. * Bug fix in formatNFTL : * - Add binary length to percentage of bad blocks. * - Report error if there is not enough good blocks. * - Report error if block 0 is bad. * New power failures protection algorithm * - setUnitData : When verify write is on , no need to reread unit data * - getSectorFlags : if flags is not valid (including SECTOR_IGNORE) check EDC and bit distance instead of simple or. * - virtual2Physical : Added endAddress parameter to allow retrieval of not the newest sector. * - writeAndCheck : * - Toggle socket verify write mode according to global verify write mode. * - Moved the marking of the sector as bad into a dedicated routine (markAsIgnored) * - initNftl : * - Init FLFlash pointer (and not structure) * - Init socket number of the TL * - Init verifiedSectorNo for FL_OFF verify write mode * - swapUnits : Catch failed folding operation * - Moved copySector routine into foldUnit. * - Changed chain bound for all chains "for loops" to the constant DOUBLE_MAX_UNIT_CHAIN (support MAX_UNIT_CHAIN of 1 in case of folding not in place). * - foldUnit : * - When verify write is set to FL_OFF call verifySectors routine to gradually scan the entire media * - Force re-mapping of cached sector * - Add force folding parameter * - Toggle socket verify write mode according to verify write mode and currently scanned sector number * - Verify copied sector EDC even for last sector of chain (no need to copy it) and if bad freeze chain and return flCanNotFold (unless forced folding is issued) * - In case of bad EDC use older sector. * - In case write operation failed (could be partially written sector that was discovered by verify write mode) freeze chain and return flCanNotFold (unless forced folding is issued) * - foldBestChain : if folding of the chosen unit failed, freeze and try another unit MAX_FOLDING_TRIES times. * - allocateUnit : free media space even if there is a free unit available * - mapSector : if sector has bad EDC read older sector until no sectors were found or chain bound has been reached * - allocateAndWriteSector : * - Catch failed folding operation. * - Set curSectorWrite to current sector so that writeAndCheck routine will be able to decide whether to verify this sector or not while in FL_OFF verify mode. * - deleteSectors : Catch failed folding operation. * - mountNFTL : * - Set default verify write policy to FL_UPS. * - Set checkVolme routine pointer. * - Added checkVolume / checkFolding / verifySectors and markAsIgnored routines. * * Rev 1.21 Nov 21 2001 11:38:04 oris * Changed FL_MARK_DELETE to FL_ON. * * Rev 1.20 15 Nov 2001 16:28:24 dimitrys * Fix Big BDK partition with Bad Units problem in formatNFTL() * * Rev 1.19 Sep 25 2001 15:39:58 oris * Removed warnings. * * Rev 1.18 Jul 29 2001 18:48:50 oris * Bug fix - unit 0 of floors > 0 were not marked as bad blocks therefore future write operation might force the device into external eprom mode. * * Rev 1.17 Jul 15 2001 20:45:26 oris * Changed DFORMAT_PRINT syntax to be similar to DEBUG_PRINT. * * Rev 1.16 Jul 13 2001 01:08:38 oris * Removed EDC for media header. * Added dformat debug print for virgin media. * * Rev 1.15 Jun 17 2001 16:39:12 oris * Improved documentation and remove warnings. * * Rev 1.14 Jun 17 2001 08:17:34 oris * Removed warnings. * Changed erase routine to formatUnit. * Bug fix - original meida header was not written. * Added NO_READ_BBT_CODE compilation flag to reduce code size. * * Rev 1.13 May 29 2001 23:12:28 oris * Added EDC check of the media header both in the mount and format routines. * * Rev 1.12 May 16 2001 21:21:26 oris * Added EDC check for media header. * Added the FL_ prefix to the following defines: ON , OFF, MALLOC and FREE. * Changed wear level counter from 0xFF to 0xFFF0 * * Rev 1.11 May 06 2001 22:42:26 oris * Removed warnings. * * Rev 1.10 May 01 2001 16:33:46 oris * Bug fix - readBBT routine returned used blocks as bad. * * Rev 1.9 Apr 30 2001 18:03:30 oris * Bug fix - marking binary partition blocks of devices that use artifitial large erase blocks (units). * Added support for the flMarkDeleteOnFlash environment variable. * * Rev 1.8 Apr 24 2001 17:10:14 oris * Bug fix - readBBT routine missing casting. * * Rev 1.7 Apr 16 2001 13:56:46 oris * Removed warrnings. * * Rev 1.6 Apr 09 2001 15:04:42 oris * End with an empty line. * * Rev 1.5 Apr 01 2001 07:55:42 oris * copywrite notice. * changed SEPERATED to SEPARATED. * Changed SEPARATED_CASCADED ifdef to a runtime if. * * Rev 1.3 Feb 14 2001 02:03:38 oris * Changed readBBT to return media size. * * Rev 1.2 Feb 12 2001 12:10:56 oris * Moved SEPARATED cascaded to include more uneeded code. * Rewritten readBBT to support far pointers. * * Rev 1.1 Feb 07 2001 17:46:26 oris * Added SEPARATED_CASCADED compilation flag * * Rev 1.0 Feb 05 2001 12:24:18 oris * Initial revision. * */ /***********************************************************************************/ /* 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 = info@m-sys.com */ /***********************************************************************************/ #include "nftllite.h" #include "nanddefs.h" #ifndef FL_READ_ONLY #if (defined(VERIFY_WRITE) || defined (VERIFY_VOLUME) || defined(VERIFY_ERASED_SECTOR)) static FLStatus checkVolume(Anand vol); static FLStatus verifySectors(Anand vol, dword sectorCount); #endif /* VERIFY_WRITE || VERIFY_VOLUME || VERIFY_ERASED_SECTOR */ static void markAsIgnored(Anand vol,CardAddress addr); static FLStatus foldBestChain(Anand vol, ANANDUnitNo *unitNo); static FLStatus foldUnit(Anand vol, ANANDUnitNo virtualUnitNo, FLBoolean); static FLStatus checkFolding(Anand vol, ANANDUnitNo virtualUnitNo); static FLStatus allocateUnit(Anand vol, ANANDUnitNo *); #endif /* FL_READ_ONLY */ static Anand vols[VOLUMES]; #ifdef NFTL_CACHE /* translation table for Sector Flags cache */ static unsigned char scacheTable[4] = { SECTOR_DELETED, /* 0 */ SECTOR_IGNORE, /* 1 */ SECTOR_USED, /* 2 */ SECTOR_FREE }; /* 3 */ #endif /* NFTL_CACHE */ #ifdef FORMAT_VOLUME byte ff[ANAND_SPARE_SIZE]; #endif /* FORMAT_VOLUME */ /*------------------------------------------------------*/ /* 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; } /*----------------------------------------------------------------------*/ /* u n i t B a s e A d d r e s s */ /* */ /* Returns the physical address of a unit. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* unitNo : Physical unit number */ /* */ /* Returns: */ /* physical address of unitNo */ /*----------------------------------------------------------------------*/ static CardAddress unitBaseAddress(Anand vol, ANANDUnitNo unitNo) { return (CardAddress)unitNo << vol.unitSizeBits; } /*----------------------------------------------------------------------*/ /* 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 : Receives the virtual unit no. */ /* replacementUnitNo : Receives the replacement unit no. */ /* */ /*----------------------------------------------------------------------*/ static void getUnitData(Anand vol, ANANDUnitNo unitNo, ANANDUnitNo *virtualUnitNo, ANANDUnitNo *replacementUnitNo) { ANANDUnitHeader unitData; #ifdef NFTL_CACHE /* check ANANDUnitHeader cache first */ if (vol.ucache != NULL) { /* on cache miss read ANANDUnitHeader from flash and re-fill cache */ if((vol.ucache[unitNo].virtualUnitNo == 0xDEAD) && (vol.ucache[unitNo].replacementUnitNo == 0xDEAD)) { vol.flash->read(vol.flash, unitBaseAddress(&vol,unitNo) + UNIT_DATA_OFFSET, &unitData, sizeof(ANANDUnitHeader), EXTRA); vol.ucache[unitNo].virtualUnitNo = LE2(unitData.virtualUnitNo) | LE2(unitData.spareVirtualUnitNo); vol.ucache[unitNo].replacementUnitNo = LE2(unitData.replacementUnitNo) | LE2(unitData.spareReplacementUnitNo); } *virtualUnitNo = vol.ucache[unitNo].virtualUnitNo; *replacementUnitNo = vol.ucache[unitNo].replacementUnitNo; } else #endif /* NFTL_CACHE */ { /* no ANANDUnitHeader cache */ vol.flash->read(vol.flash, unitBaseAddress(&vol,unitNo) + UNIT_DATA_OFFSET, &unitData, sizeof(ANANDUnitHeader), EXTRA); /* Mask out any 1 -> 0 bit faults by or'ing with spare data */ *virtualUnitNo = LE2(unitData.virtualUnitNo) | LE2(unitData.spareVirtualUnitNo); *replacementUnitNo = LE2(unitData.replacementUnitNo) | LE2(unitData.spareReplacementUnitNo); } if( !isLegalUnit(*replacementUnitNo) ) { vol.invalidReplacement = unitNo; if(vol.badFormat == FALSE) /* Not called by mount operation */ { ANANDUnitNo firstUnits = vol.virtualUnits[*virtualUnitNo]; if(firstUnits != ANAND_NO_UNIT) setUnavail(firstUnits & (~ANAND_REPLACING_UNIT)); /* freeze unit chain */ } *replacementUnitNo = ANAND_NO_UNIT; } } #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. */ /* replacementUnitNo : Replacement unit no. */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /* */ /*----------------------------------------------------------------------*/ static FLStatus setUnitData(Anand vol, ANANDUnitNo unitNo, ANANDUnitNo virtualUnitNo, ANANDUnitNo replacementUnitNo) { ANANDUnitHeader unitData; ANANDUnitNo newVirtualUnitNo, newReplacementUnitNo; if( replacementUnitNo == unitNo ) /* prevent chain loop */ return flGeneralFailure; toLE2(unitData.virtualUnitNo,virtualUnitNo); toLE2(unitData.spareVirtualUnitNo,virtualUnitNo); toLE2(unitData.replacementUnitNo,replacementUnitNo); toLE2(unitData.spareReplacementUnitNo,replacementUnitNo); checkStatus(vol.flash->write(vol.flash, unitBaseAddress(&vol,unitNo) + UNIT_DATA_OFFSET, &unitData, sizeof(ANANDUnitHeader), EXTRA)); #ifdef VERIFY_WRITE if (vol.flash->socket->verifyWrite==FL_ON) { #ifdef NFTL_CACHE /* Set new entries for ANANDUnitHeader cache */ if (vol.ucache != NULL) { vol.ucache[unitNo].virtualUnitNo = virtualUnitNo; vol.ucache[unitNo].replacementUnitNo = replacementUnitNo; } #endif /* NFTL_CACHE */ return flOK; } #endif /* VERIFY_WRITE */ #ifdef NFTL_CACHE /* purge ANANDUnitHeader cache to force re-filling from flash */ if (vol.ucache != NULL) { vol.ucache[unitNo].virtualUnitNo = 0xDEAD; vol.ucache[unitNo].replacementUnitNo = 0xDEAD; } #endif /* NFTL_CACHE */ /* Verify the new unit data */ getUnitData(&vol,unitNo,&newVirtualUnitNo, &newReplacementUnitNo); if (virtualUnitNo != newVirtualUnitNo || replacementUnitNo != newReplacementUnitNo) return flWriteFault; else return flOK; } #endif /* FL_READ_ONLY */ /*----------------------------------------------------------------------*/ /* g e t N e x t U n i t */ /* */ /* Get next unit in chain. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* unitNo : Physical unit number */ /* virUnitNo : Virtual unit number of the chain. */ /* */ /* Returns: */ /* Physical unit number of the unit following unitNo in the chain. */ /* If such unit do not exist, return ANAND_NO_UNIT. */ /*----------------------------------------------------------------------*/ static ANANDUnitNo getNextUnit(Anand vol, ANANDUnitNo unitNo, ANANDUnitNo virUnitNo) { ANANDUnitNo virtualUnitNo, replacementUnitNo; if (!(vol.physicalUnits[unitNo] & UNIT_REPLACED)) return ANAND_NO_UNIT; getUnitData(&vol,unitNo,&virtualUnitNo,&replacementUnitNo); if( virUnitNo != (virtualUnitNo & ~ANAND_REPLACING_UNIT) ) { unitNo = vol.virtualUnits[virUnitNo]; setUnavail(unitNo); /* freeze unit chain */ return ANAND_NO_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 unsigned char getSectorFlagsFromCache(Anand vol, CardAddress address) { return scacheTable[(vol.scache[address >> (SECTOR_SIZE_BITS+2)] >> (((unsigned int)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(Anand vol, CardAddress address, unsigned char sectorFlags) { register unsigned char tmp, val; if (vol.scache == NULL) return; tmp = vol.scache[address >> (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 (((unsigned int)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 >> (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 unsigned char getSectorFlags(Anand 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 */ { /* 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, nftlBuffer, SECTOR_SIZE, EDC); 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; } /*----------------------------------------------------------------------*/ /* f i n d E n d O f E n d l e s s C h a i n */ /* */ /* Find end of endless chain - last unit of unit chain that points to */ /* itself. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* virUnitNo : Virtual unit number */ /* */ /* Returns: */ /* Physical unit number of oldest unit of the chain */ /*----------------------------------------------------------------------*/ static ANANDUnitNo findEndOfEndlessChain(Anand vol, ANANDUnitNo virUnitNo) { ANANDUnitNo chainsUnit[DOUBLE_MAX_UNIT_CHAIN/2+2]; ANANDUnitNo unitNo; int i; int chainBound; for (unitNo = vol.virtualUnits[virUnitNo] , chainBound = 0 ; chainBound < MAX_UNIT_CHAIN ; unitNo = getNextUnit(&vol,unitNo,virUnitNo) , chainBound++) { chainsUnit[chainBound] = unitNo; /* Save location of current unit */ for(i = 0 ; i < chainBound ; i++) /* Check if already been to this unit */ { if(chainsUnit[chainBound] == unitNo) /* Bad next unit number pointer */ break; } } return chainsUnit[chainBound-1]; } /*----------------------------------------------------------------------*/ /* 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 */ /* endAddress : End address for sector search. NULL for no end */ /* */ /* Returns: */ /* endAddress : Next free sector address */ /* physical address of sectorNo */ /*----------------------------------------------------------------------*/ static CardAddress virtual2Physical(Anand vol, SectorNo sectorNo , CardAddress* endAddress) { unsigned unitOffset = (unsigned)((sectorNo % vol.sectorsPerUnit) << SECTOR_SIZE_BITS); CardAddress prevSectorAddress = ANAND_UNASSIGNED_ADDRESS; CardAddress sectorAddress; CardAddress *freeSectorAddressPtr; ANANDUnitNo unitNo, virUnitNo; ANANDUnitNo chainBound = 0; ANANDUnitNo endUnit; byte sectorFlags; FLBoolean badPointerFound = FALSE; /* Set end unit and nextFreeSectorAddress */ if(endAddress == NULL) { endUnit = ANAND_NO_UNIT; } else { endUnit = (ANANDUnitNo)(*endAddress>>vol.unitSizeBits); *endAddress = ANAND_UNASSIGNED_ADDRESS; } /* follow the chain */ virUnitNo = (ANANDUnitNo)(sectorNo / vol.sectorsPerUnit); for (unitNo = vol.virtualUnits[virUnitNo]; ( (unitNo != endUnit) && (chainBound < DOUBLE_MAX_UNIT_CHAIN) ); unitNo = getNextUnit(&vol,unitNo,virUnitNo)) { sectorAddress = unitBaseAddress(&vol,unitNo) + unitOffset; sectorFlags = getSectorFlags(&vol,sectorAddress); if (sectorFlags == SECTOR_FREE) { if(endAddress != NULL) *endAddress = sectorAddress; break; } if (sectorFlags != SECTOR_IGNORE) prevSectorAddress = sectorFlags != SECTOR_DELETED ? sectorAddress : ANAND_UNASSIGNED_ADDRESS; chainBound++; } if(chainBound < DOUBLE_MAX_UNIT_CHAIN) return prevSectorAddress; /* Infint loop caused by power failure */ if(endAddress == NULL) { freeSectorAddressPtr = §orAddress; } else { freeSectorAddressPtr = endAddress; } *freeSectorAddressPtr = unitOffset + unitBaseAddress(&vol, findEndOfEndlessChain(&vol,virUnitNo)); return virtual2Physical(&vol, sectorNo , freeSectorAddressPtr); } /*----------------------------------------------------------------------*/ /* g e t F o l d M a r k */ /* */ /* Get the fold mark a unit. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* unitNo : Physical unit number */ /* */ /* Returns: */ /* Return the OR of the two words in the fold mark area (the words */ /* should be identical) */ /*----------------------------------------------------------------------*/ static unsigned short getFoldMark(Anand vol, ANANDUnitNo unitNo) { unsigned short foldMark[2]; vol.flash->read(vol.flash, unitBaseAddress(&vol,unitNo) + FOLD_MARK_OFFSET, foldMark, sizeof foldMark, EXTRA); return foldMark[0] | foldMark[1]; } /*----------------------------------------------------------------------*/ /* 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 */ /* */ /*----------------------------------------------------------------------*/ static void getUnitTailer(Anand vol, ANANDUnitNo unitNo, unsigned short *eraseMark, unsigned long *eraseCount) { UnitTailer unitTailer; vol.flash->read(vol.flash, unitBaseAddress(&vol,unitNo) + UNIT_TAILER_OFFSET, &unitTailer, sizeof(UnitTailer), EXTRA); /* Mask out any 1 -> 0 bit faults by or'ing with spare data */ *eraseMark = LE2(unitTailer.eraseMark) | LE2(unitTailer.eraseMark1); *eraseCount = LE4(unitTailer.eraseCount); } /*----------------------------------------------------------------------*/ /* 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 */ /* */ /*----------------------------------------------------------------------*/ static FLStatus setUnitTailer(Anand vol, ANANDUnitNo unitNo, unsigned short eraseMark, unsigned long eraseCount) { UnitTailer unitTailer; toLE2(unitTailer.eraseMark,eraseMark); toLE2(unitTailer.eraseMark1,eraseMark); toLE4(unitTailer.eraseCount,eraseCount); return vol.flash->write(vol.flash, unitBaseAddress(&vol,unitNo) + UNIT_TAILER_OFFSET, &unitTailer, sizeof(UnitTailer), EXTRA); } /*----------------------------------------------------------------------*/ /* 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 initNFTL(Anand vol, FLFlash *flash) { dword size = 1; if (flash == NULL || !(flash->flags & NFTL_ENABLED)) { DEBUG_PRINT(("Debug: media is not fit for NFTL format.\r\n")); return flUnknownMedia; } vol.flash = flash; #ifdef NT5PORT vol.socketNo = (byte)(flSocketNoOf(vol.flash->socket)); #else vol.socketNo = flSocketNoOf(vol.flash->socket); #endif NT5PORT vol.physicalUnits = NULL; vol.virtualUnits = NULL; #ifdef NFTL_CACHE vol.ucache = NULL; vol.scache = NULL; #endif for (vol.erasableBlockSizeBits = 0; size < vol.flash->erasableBlockSize; vol.erasableBlockSizeBits++, size <<= 1); vol.unitSizeBits = vol.erasableBlockSizeBits; vol.noOfUnits = (unsigned short)((vol.flash->noOfChips * vol.flash->chipSize) >> vol.unitSizeBits); /* Adjust unit size so header unit fits in one unit */ while (vol.noOfUnits * sizeof(ANANDPhysUnit) + SECTOR_SIZE > (1UL << vol.unitSizeBits)) { vol.unitSizeBits++; vol.noOfUnits >>= 1; } /* Bound number of units to find room in 64 Kbytes Segment */ if( (vol.noOfUnits >= MAX_UNIT_NUM) && (vol.unitSizeBits < MAX_UNIT_SIZE_BITS) ) { vol.unitSizeBits++; vol.noOfUnits >>= 1; } vol.badFormat = TRUE; /* until mount completes*/ vol.mappedSectorNo = UNASSIGNED_SECTOR; vol.countsValid = 0; /* No units have a valid count yet */ /*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 */ #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 */ vol.invalidReplacement = ANAND_NO_UNIT; /* a unit with bad RUN */ 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 */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/ static FLStatus initTables(Anand vol) { /* Allocate the conversion tables */ #ifdef FL_MALLOC vol.physicalUnits = (ANANDPhysUnit FAR1*) FL_FAR_MALLOC (vol.noOfUnits * sizeof(ANANDPhysUnit)); vol.virtualUnits = (ANANDUnitNo FAR1*) FL_FAR_MALLOC (vol.noOfVirtualUnits * sizeof(ANANDUnitNo)); if (vol.physicalUnits == NULL || vol.virtualUnits == NULL) { DEBUG_PRINT(("Debug: failed allocating conversion tables for NFTL.\r\n")); return flNotEnoughMemory; } #else char *heapPtr; heapPtr = vol.heap; vol.physicalUnits = (ANANDPhysUnit *) heapPtr; heapPtr += vol.noOfUnits * sizeof(ANANDPhysUnit); vol.virtualUnits = (ANANDUnitNo *) heapPtr; heapPtr += vol.noOfVirtualUnits * sizeof(ANANDUnitNo); if (heapPtr > vol.heap + sizeof vol.heap) { DEBUG_PRINT(("Debug: not enough memory for NFTL conversion tables.\r\n")); return flNotEnoughMemory; } #endif return flOK; } /*----------------------------------------------------------------------*/ /* 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(Anand vol, ANANDUnitNo unitNo) { unsigned short eraseMark; unsigned long eraseCount; vol.physicalUnits[unitNo] = UNIT_BAD_MOUNT; getUnitTailer(&vol,unitNo,&eraseMark,&eraseCount); return setUnitTailer(&vol,unitNo,0,eraseCount); } /*----------------------------------------------------------------------*/ /* 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 : Unit to format */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/ static FLStatus formatUnit(Anand vol, ANANDUnitNo unitNo) { unsigned short eraseMark; unsigned long eraseCount; FLStatus status; if (!isAvailable(unitNo)) return flWriteFault; if (vol.physicalUnits[unitNo] == ANAND_UNIT_FREE) vol.freeUnits--; setUnavail(unitNo); getUnitTailer(&vol,unitNo,&eraseMark,&eraseCount); #ifdef NFTL_CACHE /* purge ANANDUnitHeader cache to force re-filling from flash */ if (vol.ucache != NULL) { vol.ucache[unitNo].virtualUnitNo = 0xDEAD; vol.ucache[unitNo].replacementUnitNo = 0xDEAD; } /* * 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 */ status = vol.flash->erase(vol.flash, (word)(unitNo << (vol.unitSizeBits - vol.erasableBlockSizeBits)), (word)(1 << (vol.unitSizeBits - vol.erasableBlockSizeBits))); 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++; checkStatus(setUnitTailer(&vol,unitNo,ERASE_MARK,eraseCount)); vol.physicalUnits[unitNo] = ANAND_UNIT_FREE; vol.freeUnits++; return flOK; } #ifndef FL_READ_ONLY /*----------------------------------------------------------------------*/ /* w r i t e A n d C h e c k */ /* */ /* Write one sector. */ /* */ /* 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.) */ /* */ /* Returns: */ /* Status : 0 on success, failed otherwise. */ /*----------------------------------------------------------------------*/ static FLStatus writeAndCheck(Anand vol, CardAddress address, void FAR1 *fromAddress, unsigned flags) { FLStatus status; #ifdef VERIFY_ERASED_SECTOR register int noOfDword; int i; #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) 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,0)); noOfDword = SECTOR_SIZE/sizeof(dword); for(i = 0;iwrite(vol.flash,address,fromAddress,SECTOR_SIZE,(word)flags); if (status == flWriteFault) { /* write failed, ignore this sector */ markAsIgnored(&vol,address); } #ifdef NFTL_CACHE else { setSectorFlagsCache(&vol, address, SECTOR_USED); } #endif #ifdef VERIFY_WRITE /* Restore verify write mode */ if(flVerifyWrite[flSocketNoOf(vol.flash->socket)][vol.flash->socket->curPartition] != FL_ON) vol.flash->socket->verifyWrite = FL_OFF; #endif /* VERIFY_WRITE */ return status; } #endif /* FL_READ_ONLY */ /*----------------------------------------------------------------------*/ /* l a s t I n C h a i n */ /* */ /* Find last unit in chain. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* unitNo : Start the search from this unit */ /* */ /* Returns: */ /* Physical unit number of the last unit in chain. */ /*----------------------------------------------------------------------*/ static ANANDUnitNo lastInChain(Anand vol, ANANDUnitNo unitNo) { ANANDUnitNo firstVirtualUnitNo, firstReplacementUnitNo; ANANDUnitNo lastUnit = unitNo, nextUnitNo; ANANDUnitNo chainBound = 0; if(unitNo == ANAND_NO_UNIT) return ANAND_NO_UNIT; getUnitData(&vol,unitNo,&firstVirtualUnitNo,&firstReplacementUnitNo); nextUnitNo = firstReplacementUnitNo; while( (nextUnitNo < vol.noOfUnits) && /* Validate replacement unit no. */ (chainBound < DOUBLE_MAX_UNIT_CHAIN) ) { ANANDUnitNo nextVirtualUnitNo, nextReplacementUnitNo; if( !isAvailable(nextUnitNo) ) break; getUnitData(&vol,nextUnitNo,&nextVirtualUnitNo,&nextReplacementUnitNo); if( nextVirtualUnitNo != (firstVirtualUnitNo | ANAND_REPLACING_UNIT) ) break; /* Virtual unit no. not validated */ lastUnit = nextUnitNo; nextUnitNo = nextReplacementUnitNo; chainBound++; } return lastUnit; } #ifndef FL_READ_ONLY /*----------------------------------------------------------------------*/ /* 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 */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/ static FLStatus assignUnit(Anand vol, ANANDUnitNo unitNo, ANANDUnitNo virtualUnitNo) { ANANDUnitNo newVirtualUnitNo, newReplacementUnitNo; ANANDUnitNo oldVirtualUnitNo, oldReplacementUnitNo; FLStatus status; ANANDUnitNo newestUnitNo = vol.virtualUnits[virtualUnitNo]; ANANDUnitNo oldUnitNo; /* Assign the new unit */ newVirtualUnitNo = virtualUnitNo; if (newestUnitNo != ANAND_NO_UNIT) newVirtualUnitNo |= ANAND_REPLACING_UNIT; newReplacementUnitNo = ANAND_NO_UNIT; vol.physicalUnits[unitNo] = 0; vol.freeUnits--; status = setUnitData(&vol,unitNo,newVirtualUnitNo,newReplacementUnitNo); if (status != flOK) { markUnitBad(&vol,unitNo); return status; } /* Add unit to chain */ if (newestUnitNo != ANAND_NO_UNIT) { /* If unit is frozen, don't attempt to chain (folding not-in-place) */ if (!isAvailable(newestUnitNo)) return flOK; oldUnitNo = lastInChain(&vol,newestUnitNo); getUnitData(&vol,oldUnitNo,&oldVirtualUnitNo,&oldReplacementUnitNo); if (oldReplacementUnitNo != ANAND_NO_UNIT) status = flWriteFault; /* can't write here, so assume failure */ else { vol.physicalUnits[oldUnitNo] |= UNIT_REPLACED; status = setUnitData(&vol,oldUnitNo,oldVirtualUnitNo,unitNo); } if (status != flOK) { formatUnit(&vol,unitNo); /* Get rid of the allocated unit quickly */ setUnavail(newestUnitNo); /* freeze the chain */ return status; } if (vol.countsValid > virtualUnitNo && newestUnitNo != oldUnitNo){ if (countOf(newestUnitNo) + countOf(oldUnitNo) <= UNIT_MAX_COUNT) vol.physicalUnits[newestUnitNo] += countOf(oldUnitNo); else return flGeneralFailure; } } else vol.virtualUnits[virtualUnitNo] = unitNo; return flOK; } #endif /* FL_READ_ONLY */ /*----------------------------------------------------------------------*/ /* 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(Anand vol, ANANDUnitNo unitNo) { /* Erase the chain from end to start */ ANANDUnitNo chainBound; setUnitCount(unitNo,0); /* Reenable erase of this unit */ for (chainBound=0;( chainBound < DOUBLE_MAX_UNIT_CHAIN ); chainBound++) { /* Find last unit in chain */ ANANDUnitNo unitToErase = lastInChain(&vol,unitNo); if( formatUnit(&vol,unitToErase) != flOK ) break; if (unitToErase == unitNo) break; /* Erased everything */ } return flOK; } #ifndef FL_READ_ONLY /*----------------------------------------------------------------------*/ /* S w a p U n i t s */ /* */ /* Applies wear leveling. */ /* A rover unit dictates the current unit to level. If a unit chain */ /* fold, if single unit chain , append a unit and then fold. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/ FLStatus swapUnits(Anand vol) { ANANDUnitNo i,unitNo,virtualUnitNo,replacementUnitNo; FLStatus status; if(vol.wearLevel.currUnit>=vol.noOfVirtualUnits) return flOK; for(i=0,unitNo=vol.virtualUnits[vol.wearLevel.currUnit]; (unitNo==ANAND_NO_UNIT) && (i=vol.noOfVirtualUnits) vol.wearLevel.currUnit = 0; unitNo=vol.virtualUnits[vol.wearLevel.currUnit]; } if(unitNo==ANAND_NO_UNIT) /*The media is empty*/ return flOK; virtualUnitNo = vol.wearLevel.currUnit; vol.wearLevel.currUnit++; if(vol.wearLevel.currUnit>=vol.noOfVirtualUnits) vol.wearLevel.currUnit = 0; if((vol.physicalUnits[unitNo] & UNIT_REPLACED) || (!isAvailable(unitNo) ) ) { status = foldUnit(&vol,virtualUnitNo,FALSE); } else { checkStatus(allocateUnit(&vol,&replacementUnitNo)); return assignUnit(&vol,replacementUnitNo,virtualUnitNo); } /* If folding failed make sure there are enough units and call again */ if(status == flCanNotFold) return checkFolding(&vol,virtualUnitNo); return status; } /*----------------------------------------------------------------------*/ /* f i n d F r e e U n i t */ /* */ /* Find a free unit from the physical unit pool. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* unitNo : Receives the physical unit no. */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/ static FLStatus findFreeUnit(Anand vol, ANANDUnitNo *unitNo) { ANANDUnitNo originalUnit = vol.roverUnit; unsigned short eraseMark; unsigned long eraseCount; do { if (++vol.roverUnit >= vol.noOfUnits) vol.roverUnit = vol.bootUnits; if (vol.physicalUnits[vol.roverUnit] == ANAND_UNIT_FREE) { /* found a free unit, if not erased, */ getUnitTailer(&vol,vol.roverUnit,&eraseMark,&eraseCount); if (eraseMark != ERASE_MARK) { if (formatUnit(&vol,vol.roverUnit) != flOK) continue; /* this unit is bad, find another */ } *unitNo = vol.roverUnit; return flOK; } } while (vol.roverUnit != originalUnit); return flNotEnoughMemory; /* Report no space at all */ } /*----------------------------------------------------------------------*/ /* 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. */ /* forceFolding : Boolean flag stating wether to force folding even */ /* at the cost of loosing sector data. */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/ static FLStatus foldUnit(Anand vol, ANANDUnitNo virtualUnitNo, FLBoolean forceFolding) { ANANDUnitNo unitNo = vol.virtualUnits[virtualUnitNo]; ANANDUnitNo targetUnitNo, chainBound; unsigned long foldMark; SectorNo virtualSectorNo = (SectorNo)virtualUnitNo * vol.sectorsPerUnit; CardAddress endSectorAddress; CardAddress targetSectorAddress; CardAddress sourceSectorAddress; unsigned newSectorCount, i; FLBoolean partialFoldingFlag = FALSE; FLStatus status; /* Force remapping of internal catched sector */ vol.flash->socket->remapped = TRUE; vol.unitsFolded++; /* When using FL_OFF option the media is scanned in the folding operation */ #if (defined(VERIFY_WRITE) || defined(VERIFY_ERASED_SECTOR)) if(flVerifyWrite[flSocketNoOf(vol.flash->socket)][vol.flash->socket->curPartition]==FL_OFF) { checkStatus(verifySectors(&vol,SECTORS_VERIFIED_PER_FOLDING)); vol.curSectorWrite = virtualSectorNo; } #endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */ /* Find target unit */ if (!isAvailable(unitNo)) /* If this unit is frozen, */ { if(vol.freeUnits > 0) { /* allocate a new unit to fold into */ checkStatus(findFreeUnit(&vol,&targetUnitNo)); checkStatus(assignUnit(&vol,targetUnitNo,virtualUnitNo)); } else { if(forceFolding==FALSE) return flCanNotFold; partialFoldingFlag = TRUE; } } if((isAvailable(unitNo)) || (partialFoldingFlag == TRUE)) { /* Default. Fold into end of chain */ targetUnitNo = unitNo; for (chainBound=0;( chainBound < DOUBLE_MAX_UNIT_CHAIN );chainBound++) { ANANDUnitNo nextUnitNo = getNextUnit(&vol,targetUnitNo,virtualUnitNo); if (nextUnitNo == ANAND_NO_UNIT) break; targetUnitNo = nextUnitNo; } if(chainBound == DOUBLE_MAX_UNIT_CHAIN) { targetUnitNo = findEndOfEndlessChain(&vol, virtualUnitNo); } } /***********************************/ /* Copy all sectors to target unit */ /***********************************/ /* Mark unit as currently folded */ foldMark = FOLDING_IN_PROGRESS * 0x10001l; if( getFoldMark(&vol,unitNo) != FOLDING_IN_PROGRESS ) vol.flash->write(vol.flash, unitBaseAddress(&vol,unitNo) + FOLD_MARK_OFFSET, &foldMark, sizeof foldMark, EXTRA); setUnavail(unitNo); /* Freeze this unit chain */ /* Copy all sectors to target unit */ targetSectorAddress = unitBaseAddress(&vol,targetUnitNo); newSectorCount = 0; for (i = 0; i < vol.sectorsPerUnit; i++, virtualSectorNo++, #if (defined(VERIFY_WRITE) || defined(VERIFY_ERASED_SECTOR)) vol.curSectorWrite++, #endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */ targetSectorAddress += SECTOR_SIZE) { endSectorAddress = ANAND_UNASSIGNED_ADDRESS; for(chainBound=0;chainBoundsocket)][vol.flash->socket->curPartition]) { case FL_UPS: newSectorCount++; goto nextSectorLable; case FL_OFF: if(vol.verifiedSectorNo > virtualSectorNo) { newSectorCount++; goto nextSectorLable; } default: /* FL_ON */ break; } /* Validate the sector has valid EDC/ECC */ status = vol.flash->read(vol.flash,sourceSectorAddress,nftlBuffer,SECTOR_SIZE,EDC); if(status!=flOK) { /* Last sector of chain has EDC errors - can not fold there */ if(forceFolding!=TRUE) { return flCanNotFold; } else { goto nextSectorLable; } } newSectorCount++; goto nextSectorLable; #endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */ } else if(sourceSectorAddress != ANAND_UNASSIGNED_ADDRESS) { /* Check that area is free (ignore flag) */ if(getSectorFlags(&vol,targetSectorAddress) != SECTOR_FREE) { if(forceFolding!=TRUE) { return flCanNotFold; } else { break; } } /* Copy sector to target sector */ status = vol.flash->read(vol.flash,sourceSectorAddress,nftlBuffer,SECTOR_SIZE,EDC); if (status != flOK) /* Try reading previous sector */ { endSectorAddress = sourceSectorAddress; continue; } status = writeAndCheck(&vol,targetSectorAddress,nftlBuffer,EDC); switch (status) { case flOK: /* Success */ vol.parasiteWrites++; newSectorCount++; goto nextSectorLable; case flWriteFault: /* Faild in verify write */ if (forceFolding == FALSE) return flCanNotFold; goto nextSectorLable; default : /* Protection error or any other */ return status; } } else /* ANAND_UNASSIGNED_ADDRESS - Sector not used */ { goto nextSectorLable; } } return flGeneralFailure; nextSectorLable:; } /* End of copy sector loop */ /*****************************/ /* Add unit to virtual chain */ /*****************************/ if (newSectorCount > 0) { /* Some sectors remaining*/ /* Mark target unit as original */ if( (setUnitData(&vol,targetUnitNo,virtualUnitNo,ANAND_NO_UNIT) != flOK ) || (partialFoldingFlag == TRUE)) { setUnavail(targetUnitNo); /* freeze this unit */ } else { setUnitCount(targetUnitNo,newSectorCount); } /* Set target unit in virtual unit table */ vol.virtualUnits[virtualUnitNo] = targetUnitNo; } else { if (unitNo != targetUnitNo) { /* If there is a chain to delete ... */ /* mark unit as completed folding, pending erase */ #ifndef NT5PORT unsigned long foldMark = FOLDING_COMPLETE * 0x10001l; #else /*NT5PORT*/ foldMark = FOLDING_COMPLETE * 0x10001l; #endif /*NT5PORT*/ vol.flash->write(vol.flash, unitBaseAddress(&vol,unitNo) + FOLD_MARK_OFFSET, &foldMark, sizeof foldMark, EXTRA); } vol.virtualUnits[virtualUnitNo] = ANAND_NO_UNIT; } /* Erase source units */ return formatChain(&vol,unitNo); } /*----------------------------------------------------------------------*/ /* 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(Anand vol, ANANDUnitNo unitNo) { unsigned int i; SectorNo sectorNo; CardAddress sectorAddress; ANANDUnitNo physicalUnitNo = vol.virtualUnits[unitNo]; if (physicalUnitNo == ANAND_NO_UNIT) return; if (!isAvailable(physicalUnitNo)) return; /* Get a count of the valid sector in this unit */ setUnitCount(physicalUnitNo,0); sectorNo = (SectorNo)unitNo * vol.sectorsPerUnit; for (i = 0; i < vol.sectorsPerUnit; i++, sectorNo++) { sectorAddress = virtual2Physical(&vol,sectorNo,NULL); if (sectorAddress != ANAND_UNASSIGNED_ADDRESS) { ANANDUnitNo currUnitNo = (ANANDUnitNo)(sectorAddress >> vol.unitSizeBits); if (vol.physicalUnits[currUnitNo] & UNIT_REPLACED) currUnitNo = physicalUnitNo; /* Increament sector count - Assumed EDC OK , was not verified */ vol.physicalUnits[currUnitNo]++; } } } /*----------------------------------------------------------------------*/ /* 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(Anand vol, ANANDUnitNo *unitNo) { unsigned leastCount; unsigned longestChain; unsigned unitCount; ANANDUnitNo virtualUnitNo; ANANDUnitNo u; ANANDUnitNo firstUnitNo; unsigned int maxCounter; FLStatus status = flCanNotFold; *unitNo = ANAND_NO_UNIT; /* Loop as long as can not fold in place or until MAX_FOLDING_TRIES time */ for (maxCounter = 0 ; maxCounter < MAX_FOLDING_TRIES ; maxCounter++) { leastCount = vol.sectorsPerUnit + 1; /* Set to invalid sector count */ longestChain = 0; /* Set to invalid unit length */ virtualUnitNo = ANAND_NO_UNIT; /*************************************************************/ /* Loop over all virtual units until best candidate is found */ /*************************************************************/ for (u = 0; u < vol.noOfVirtualUnits; u++) { /* If virtual unit does not exist continue to next unit */ firstUnitNo = vol.virtualUnits[u]; if( firstUnitNo == ANAND_NO_UNIT ) continue; /* Make sure sector count of unit is valid */ if (vol.countsValid <= u) { createUnitCount(&vol,u); /* Update sector count */ vol.countsValid = u + 1; } if( isAvailable(firstUnitNo) ) /* store sector count */ { unitCount = countOf(firstUnitNo); } else { unitCount = vol.sectorsPerUnit; /* set low priority */ } /* This is an empty unit. We can simply erase it */ if (unitCount == 0) { leastCount = 0; virtualUnitNo = u; break; } if(/* Smallest sector count found so far */ (leastCount >= unitCount ) && /* And this unit is not a single unit chain */ (vol.physicalUnits[firstUnitNo] & UNIT_REPLACED) ) { unsigned chainLength = 0; /* Compare chain length */ ANANDUnitNo nextUnitNo = getNextUnit(&vol,firstUnitNo,u); if(isAvailable(firstUnitNo)) { while((nextUnitNo != ANAND_NO_UNIT ) && (chainLength < DOUBLE_MAX_UNIT_CHAIN)) { chainLength++; nextUnitNo = getNextUnit(&vol,nextUnitNo,u); } } else { chainLength = 0; /* Set lowest priority to frozen chain */ } /* set low priority to neverending loop chain */ if(chainLength == DOUBLE_MAX_UNIT_CHAIN) chainLength = 0; if((leastCount == unitCount ) && (longestChain >= chainLength)) continue; longestChain = chainLength; leastCount = unitCount; virtualUnitNo = u; } /* End - unit has less (or eqaul) no' of used sectors found so far */ } /* End - Loop over all virtual units and find best candidate */ /*************************************************************/ /* Candidate virtual chain has been chosen - try and fold it */ /*************************************************************/ if ((leastCount > vol.sectorsPerUnit) || (virtualUnitNo == ANAND_NO_UNIT)) { /* Only single units chains were found */ if(maxCounter==0) /* And no chain was frozen while searching */ return flNotEnoughMemory; /* Report no space at all */ break; /* Try and fold the frozen units that have been found */ } else /* Try and fold the candidate unit */ { if(!isAvailable(vol.virtualUnits[virtualUnitNo])) /* This is a frozen unit */ { /* Frozen chains have the lowest priority - stop searching */ break; } else /* Try and fold the unit */ { /* Store the first unit as the new free unit */ *unitNo = vol.virtualUnits[virtualUnitNo]; /* Store number of free Units */ u = vol.freeUnits; /* Fold the candidate unit */ status = foldUnit(&vol,virtualUnitNo,FALSE); switch(status) { case flOK: if(vol.freeUnits == u) /* Did not free any units */ continue; break; case flCanNotFold: /* Need to fold not in place */ continue; default: return status; } break; } } } /* End MAX_FOLDING_TRIES loop */ /***************************************************************/ /* Check folding status - might need a special kind of folding */ /***************************************************************/ if(maxCounter) { *unitNo = ANAND_NO_UNIT; /* Unfreeze all units */ for (u = vol.spareOrgUnit + 1; u < vol.noOfUnits; u++) { if(!isAvailable(u)) /* This is a frozen unit */ { /* Get virtual unit of frozen physical unit */ getUnitData(&vol,u,&virtualUnitNo,&firstUnitNo); if(virtualUnitNo < vol.noOfVirtualUnits) { /* Store the first unit as the new free unit */ firstUnitNo = vol.virtualUnits[virtualUnitNo]; /* Force folding no matter what */ if(firstUnitNo != ANAND_NO_UNIT) { checkStatus(foldUnit(&vol,virtualUnitNo,TRUE)); /* Recalculate sector count */ createUnitCount(&vol, virtualUnitNo); *unitNo = firstUnitNo; } } } } } if((*unitNo!= ANAND_NO_UNIT) && (vol.freeUnits > 0)) /* A unit was freed */ return flOK; else return flGeneralFailure; } /*----------------------------------------------------------------------*/ /* 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(Anand vol, ANANDUnitNo *unitNo) { ANANDUnitNo originalUnit = vol.roverUnit; FLStatus status; if (vol.freeUnits < 2) /* Try to make sure not to use the last unit */ { status = foldBestChain(&vol,unitNo); if(status != flNotEnoughMemory) return status; } return findFreeUnit(&vol, unitNo); } #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(Anand vol, SectorNo sectorNo, CardAddress *physAddress) { if (sectorNo != vol.mappedSectorNo || vol.flash->socket->remapped) { if (sectorNo >= vol.virtualSectors) { vol.mappedSector = NULL; } else { int chainBound = 0; for(vol.mappedSectorAddress=ANAND_UNASSIGNED_ADDRESS; chainBoundread(vol.flash,vol.mappedSectorAddress,nftlBuffer,SECTOR_SIZE,EDC) == flOK) break; } } vol.mappedSectorNo = sectorNo; vol.flash->socket->remapped = FALSE; } } if (physAddress) *physAddress = vol.mappedSectorAddress; return vol.mappedSector; } /* Mounting and formatting */ /*----------------------------------------------------------------------*/ /* m o u n t U n i t */ /* */ /* Mount one unit. Read the relevant data from the unit header and */ /* update the conversion tables. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* unitNo : Unit to mount */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/ static FLStatus mountUnit(Anand vol, ANANDUnitNo unitNo, unsigned long* eraseCount) { ANANDUnitNo virtualUnitNo, replacementUnitNo; unsigned short eraseMark; ANANDPhysUnit FAR1 *pU = &vol.physicalUnits[unitNo]; getUnitData(&vol,unitNo,&virtualUnitNo,&replacementUnitNo); getUnitTailer(&vol,unitNo,&eraseMark,eraseCount); if (virtualUnitNo == ANAND_NO_UNIT || eraseMark != ERASE_MARK) { /* this unit is not assigned */ *pU = ANAND_UNIT_FREE; } else { /* this unit is assigned */ *pU &= UNIT_ORPHAN; if (replacementUnitNo < vol.noOfUnits) { *pU |= UNIT_REPLACED; if (isAvailable(replacementUnitNo) || isReplaced(replacementUnitNo)) /* Mark replacement unit as non-orphan */ vol.physicalUnits[replacementUnitNo] &= ~UNIT_ORPHAN; } if (!(virtualUnitNo & ANAND_REPLACING_UNIT)) { unsigned short foldMark; ANANDUnitNo physUnitNo; if (virtualUnitNo >= vol.noOfVirtualUnits) return formatUnit(&vol,unitNo); foldMark = getFoldMark(&vol,unitNo); physUnitNo = vol.virtualUnits[virtualUnitNo]; if (foldMark == FOLDING_COMPLETE) formatChain(&vol,unitNo); else if (physUnitNo == ANAND_NO_UNIT || !isAvailable(physUnitNo)) { /* If we have duplicates, it's OK if one of them is currently folded */ vol.virtualUnits[virtualUnitNo] = unitNo; *pU &= ~UNIT_ORPHAN; if (foldMark == FOLDING_IN_PROGRESS) { setUnavail(unitNo); } if (physUnitNo != ANAND_NO_UNIT) formatChain(&vol,physUnitNo); /* Get rid of old chain */ } else if (foldMark == FOLDING_IN_PROGRESS) formatChain(&vol,unitNo); else return flBadFormat; /* We have a duplicate to a unit that */ /* is not currently folded. That's bad. */ } } return flOK; } #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 */ /* */ /* 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. */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/ static FLStatus allocateAndWriteSector(void* rec, SectorNo sectorNo, void FAR1 *fromAddress) { Anand vol = (Anand*)rec; ANANDUnitNo virtualUnitNo = (ANANDUnitNo)(sectorNo / vol.sectorsPerUnit); ANANDUnitNo firstUnitNo = vol.virtualUnits[virtualUnitNo]; ANANDUnitNo unitNo; unsigned unitOffset = (unsigned)((sectorNo % vol.sectorsPerUnit) << SECTOR_SIZE_BITS); unsigned unitChainLength = 0; FLBoolean sectorExists = FALSE; FLBoolean unitWasFoldedOutOfPlace = FALSE; FLStatus status; /* If we can't write to this unit, must fold it first */ if (firstUnitNo != ANAND_NO_UNIT && !isAvailable(firstUnitNo)) { status = foldUnit(&vol,virtualUnitNo,FALSE); switch(status) { case flOK: break; case flCanNotFold: checkStatus(checkFolding(&vol,virtualUnitNo)); break; default: return status; } firstUnitNo = vol.virtualUnits[virtualUnitNo]; } /* Find a unit to write this sector */ unitNo = firstUnitNo; while ((unitNo != ANAND_NO_UNIT) && (unitChainLength < DOUBLE_MAX_UNIT_CHAIN)) { unsigned char sectorFlags = getSectorFlags(&vol,unitBaseAddress(&vol,unitNo) + unitOffset); if (sectorFlags == SECTOR_FREE) break; if (sectorFlags != SECTOR_IGNORE) sectorExists = sectorFlags == SECTOR_USED; unitNo = getNextUnit(&vol,unitNo,virtualUnitNo); unitChainLength++; } if (unitChainLength == DOUBLE_MAX_UNIT_CHAIN) { /* unit points to itself */ unitNo = ANAND_NO_UNIT; /* force folding not in place */ setUnavail(firstUnitNo); } if (unitNo == ANAND_NO_UNIT) { /* Can not write in chain - must add a unit */ firstUnitNo = vol.virtualUnits[virtualUnitNo]; if (unitChainLength >= MAX_UNIT_CHAIN) { status = foldUnit(&vol,virtualUnitNo,FALSE); switch(status) { case flOK: break; case flCanNotFold: checkStatus(checkFolding(&vol,virtualUnitNo)); break; default: return status; } } if(vol.virtualUnits[virtualUnitNo] != firstUnitNo) { firstUnitNo = vol.virtualUnits[virtualUnitNo]; unitWasFoldedOutOfPlace = TRUE; } checkStatus(allocateUnit(&vol,&unitNo)); checkStatus(assignUnit(&vol,unitNo,virtualUnitNo)); if(vol.virtualUnits[virtualUnitNo] != firstUnitNo) { firstUnitNo = vol.virtualUnits[virtualUnitNo]; unitWasFoldedOutOfPlace = TRUE; } #if (defined(VERIFY_WRITE) || defined(VERIFY_ERASED_SECTOR)) /* Folding might have discovered that the sector we are using is invalid */ if(sectorExists) { if( unitWasFoldedOutOfPlace ) { /* Unit was folded out of place, so if sector exists it must be in * the first unit of the chain */ if(getSectorFlags(&vol,unitBaseAddress(&vol,firstUnitNo) + unitOffset) != SECTOR_USED) { /* The sector we saw before had bad EDC */ sectorExists = FALSE; unitNo = firstUnitNo; } } } #endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */ } #if (defined(VERIFY_WRITE) || defined(VERIFY_ERASED_SECTOR)) vol.curSectorWrite = sectorNo; #endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */ checkStatus(writeAndCheck(&vol,unitBaseAddress(&vol,unitNo) + unitOffset,fromAddress,EDC)); if (vol.countsValid > virtualUnitNo) { if (unitNo != firstUnitNo && !(vol.physicalUnits[unitNo] & UNIT_REPLACED)) { if (countOf(unitNo) < UNIT_MAX_COUNT) /* Increment block count */ vol.physicalUnits[unitNo]++; else return flGeneralFailure; if (sectorExists) /* Decrement block count */ { if (countOf(firstUnitNo) > 0) vol.physicalUnits[firstUnitNo]--; else return flGeneralFailure; } } else if (!sectorExists) { if (countOf(firstUnitNo) < UNIT_MAX_COUNT) /* Increment block count */ vol.physicalUnits[firstUnitNo]++; else return flGeneralFailure; } } return flOK; } /*----------------------------------------------------------------------*/ /* 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(Anand vol, SectorNo sectorNo, void FAR1 *fromAddress) { FLStatus status = flWriteFault; int i; if (vol.badFormat) return flBadFormat; if (sectorNo >= vol.virtualSectors) return flSectorNotFound; if(vol.wearLevel.currUnit!=ANAND_NO_UNIT) { vol.wearLevel.alarm++; if(vol.wearLevel.alarm>=WLnow) { vol.wearLevel.alarm = 0; checkStatus(swapUnits(&vol)); } } if (sectorNo == vol.mappedSectorNo) { /* Force remapping of internal catched sector */ vol.flash->socket->remapped = TRUE; } vol.sectorsWritten++; for (i = 0; i < 4 && status == flWriteFault; i++) { if (vol.mappedSectorNo == sectorNo) vol.mappedSectorNo = UNASSIGNED_SECTOR; status = allocateAndWriteSector(&vol,sectorNo,fromAddress); } 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(Anand vol, SectorNo sectorNo, SectorNo noOfSectors) { SectorNo iSector; FLStatus status; if (vol.badFormat) return flBadFormat; if (sectorNo + noOfSectors > vol.virtualSectors) return flSectorNotFound; for (iSector = 0; iSector < noOfSectors; iSector++, sectorNo++, vol.sectorsDeleted++) { CardAddress sectorAddress = virtual2Physical(&vol,sectorNo,NULL); if (sectorAddress != ANAND_UNASSIGNED_ADDRESS) { byte sectorFlags[2]; ANANDUnitNo currUnitNo; /* Check that the unit is writable, and if not, fold it first */ ANANDUnitNo virtualUnitNo = (ANANDUnitNo)(sectorNo / vol.sectorsPerUnit); ANANDUnitNo unitNo = vol.virtualUnits[virtualUnitNo]; if (!isAvailable(unitNo)) { status = foldUnit(&vol,virtualUnitNo,FALSE); switch(status) { case flOK: break; case flCanNotFold: checkStatus(checkFolding(&vol,virtualUnitNo)); break; default: return status; } sectorAddress = virtual2Physical(&vol,sectorNo,NULL); } /* Mark sector deleted */ sectorFlags[0] = sectorFlags[1] = SECTOR_DELETED; #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 vol.flash->write(vol.flash, sectorAddress + SECTOR_DATA_OFFSET, #ifndef NT5PORT §orFlags, #else /*NT5PORT*/ sectorFlags, #endif /*NT5PORT*/ sizeof sectorFlags, EXTRA); currUnitNo = (ANANDUnitNo)(sectorAddress >> vol.unitSizeBits); if ( isAvailable(currUnitNo) ) { if (vol.physicalUnits[currUnitNo] & UNIT_REPLACED) currUnitNo = vol.virtualUnits[virtualUnitNo]; if (vol.countsValid > virtualUnitNo) { if (countOf(currUnitNo) > 0) vol.physicalUnits[currUnitNo]--; /* Decrement block count */ else return flGeneralFailure; } } } } return flOK; } #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(Anand vol, FLBoolean state) { return flOK; } #ifndef FL_READ_ONLY #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(Anand vol, long FAR2 *sectorsNeeded) { ANANDUnitNo dummyUnitNo, firstFreeUnit = ANAND_NO_UNIT; FLBoolean firstRound = TRUE; FLStatus status = flOK; if( (*sectorsNeeded) == -1 ) { /* fold single chain */ if (vol.badFormat) return flBadFormat; status = foldBestChain(&vol,&dummyUnitNo); if( (status != flOK) && (vol.freeUnits == 0) ) return status; *sectorsNeeded = vol.freeUnits * vol.sectorsPerUnit; return flOK; } while ((SectorNo)vol.freeUnits * vol.sectorsPerUnit < ((SectorNo)(*sectorsNeeded))) { if (vol.badFormat) return flBadFormat; status = allocateUnit(&vol,&dummyUnitNo); if( status != flOK ) break; if (firstRound) { /* remember the first free unit */ firstFreeUnit = dummyUnitNo; firstRound = FALSE; } else if (firstFreeUnit == dummyUnitNo) { /* We have wrapped around, all the units that were marked as free */ /* are now erased, and we still don't have enough space. */ status = foldBestChain(&vol,&dummyUnitNo); /* make more free units */ if( status != flOK ) break; } } *sectorsNeeded = (long)vol.freeUnits * vol.sectorsPerUnit; return status; } #endif /* DEFRAGMENT_VOLUME */ #endif /* FL_READ_ONLY */ /*----------------------------------------------------------------------*/ /* 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(Anand vol) { return vol.virtualSectors; } /*----------------------------------------------------------------------*/ /* d i s m o u n t N F T L */ /* */ /* Dismount NFTL volume */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* */ /*----------------------------------------------------------------------*/ static void dismountNFTL(Anand vol) { #ifdef FL_MALLOC if( vol.physicalUnits != NULL ) FL_FAR_FREE (vol.physicalUnits); if( vol.virtualUnits != NULL ) FL_FAR_FREE (vol.virtualUnits); vol.physicalUnits = NULL; vol.virtualUnits = NULL; #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 */ } Anand* getAnandRec(unsigned driveNo) { return (&vols[driveNo]); } #ifdef FORMAT_VOLUME /*----------------------------------------------------------------------*/ /* i s E r a s e d U n i t */ /* */ /* Check if a unit is erased. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* unitNo : unit to check */ /* */ /* Returns: */ /* TRUE if unit is erased, FALSE otherwise */ /*----------------------------------------------------------------------*/ static FLBoolean isErased(Anand vol, ANANDUnitNo unitNo) { CardAddress addr; CardAddress endAddr; word offset; /* Force remapping of internal catched sector */ vol.flash->socket->remapped = TRUE; addr = unitBaseAddress(&vol,unitNo); endAddr = addr + (1L << vol.unitSizeBits); for (;addr < endAddr; addr += SECTOR_SIZE) { /* Check area a and b */ vol.flash->read(vol.flash, addr, nftlBuffer,SECTOR_SIZE, 0); for (offset=0;offsetread(vol.flash, addr, nftlBuffer,ANAND_SPARE_SIZE, EXTRA); if (tffscmp( nftlBuffer, ff, ANAND_SPARE_SIZE )) return FALSE; } return TRUE; } /*----------------------------------------------------------------------*/ /* f o r m a t N F T L */ /* */ /* Perform NFTL Format. */ /* */ /* Parameters: */ /* volNo : Volume serial no. */ /* formatParams : Address of FormatParams structure to use */ /* flash : Flash media mounted on this socket */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/ static FLStatus formatNFTL(unsigned volNo, TLFormatParams *formatParams, FLFlash *flash) { Anand vol = &vols[volNo]; long int unitSize; unsigned long prevVirtualSize; ANANDUnitNo iUnit, prevOrgUnit; ANANDUnitNo noOfBootUnits=0; ANANDBootRecord bootRecord; int noOfBadUnits = 0; FLStatus status = flOK; FLBoolean forceHeaderUpdate = FALSE; static unsigned char checkSum[EXTRA_LEN] = { 0x4B, 0x00, 0xE2, 0x0E, 0x93, 0xF7, 0x55, 0x55 }; #ifdef EXTRA_LARGE int moreUnitBits; unsigned char anandFlagsTmp; #endif /* EXTRA_LARGE */ DEBUG_PRINT(("Debug: starting NFTL format.\r\n")); checkStatus(initNFTL(&vol,flash)); tffsset(&bootRecord,0,sizeof(ANANDBootRecord)); /* Find the medium boot record */ for (vol.orgUnit = 0; vol.orgUnit < vol.noOfUnits; vol.orgUnit++) { vol.flash->read(vol.flash, unitBaseAddress(&vol,vol.orgUnit), &bootRecord, sizeof bootRecord, 0); if (tffscmp(bootRecord.bootRecordId,"ANAND",sizeof bootRecord.bootRecordId) == 0) break; } #ifdef EXTRA_LARGE if (vol.orgUnit >= vol.noOfUnits) { /* first time formatting */ bootRecord.anandFlags = 0xFF; moreUnitBits = 0; while( ((vol.noOfUnits >> moreUnitBits) > 4096) && ((vol.unitSizeBits + moreUnitBits) < MAX_UNIT_SIZE_BITS) && (bootRecord.anandFlags > 0xFC) ) { moreUnitBits++; bootRecord.anandFlags--; } } moreUnitBits = ~bootRecord.anandFlags & MORE_UNIT_BITS_MASK; if (moreUnitBits > 0) { vol.unitSizeBits += moreUnitBits; vol.noOfUnits >>= moreUnitBits; vol.orgUnit >>= moreUnitBits; } #endif /* EXTRA_LARGE */ /* adjust number of boot area units include the EXB area */ if (formatParams->flags & FL_LEAVE_BINARY_AREA) { /* Leave binary area (except for the area between the old original unit and the new one */ if (formatParams->bootImageLen >= 0) { noOfBootUnits += (ANANDUnitNo)((formatParams->bootImageLen - 1) >> vol.unitSizeBits) + 1; } else /* Leave binary area excatly as it was */ { if (vol.orgUnit >= vol.noOfUnits) /* first time formatting */ { noOfBootUnits = 0; } else { if (LE2(bootRecord.bootUnits) > noOfBootUnits ) noOfBootUnits = LE2(bootRecord.bootUnits); } } } else /* Actualy format binary area with a signature */ { #ifdef WRITE_EXB_IMAGE if (formatParams->exbLen > 0) { noOfBootUnits = (ANANDUnitNo)((formatParams->exbLen - 1) >> vol.unitSizeBits) + 1; formatParams->exbLen = noOfBootUnits; } else { formatParams->exbLen = 0; } #endif /* WRITE_EXB_IMAGE */ if(formatParams->noOfBinaryPartitions > 0) { noOfBootUnits += (ANANDUnitNo)((formatParams->binaryPartitionInfo ->length - 1) >> vol.unitSizeBits) + 1; } } prevOrgUnit = vol.orgUnit; /* save previous Original Unit */ prevVirtualSize = UNAL4(bootRecord.virtualMediumSize); vol.bootUnits = noOfBootUnits; vol.unitOffsetMask = (1L << vol.unitSizeBits) - 1; vol.sectorsPerUnit = 1 << (vol.unitSizeBits - SECTOR_SIZE_BITS); /* Add 'percentUse'% of bootUnits to transfer units */ formatParams->percentUse -= (unsigned)(((long)(100 - formatParams->percentUse) * (vol.bootUnits)) / (vol.noOfUnits - vol.bootUnits)); vol.noOfTransferUnits = (ANANDUnitNo)formatParams->noOfSpareUnits; vol.noOfTransferUnits += (ANANDUnitNo)((long)(vol.noOfUnits - vol.bootUnits) * (100 - formatParams->percentUse) / 100); if (vol.noOfUnits <= vol.bootUnits + vol.noOfTransferUnits) return flVolumeTooSmall; unitSize = 1L << vol.unitSizeBits; vol.noOfVirtualUnits = vol.noOfUnits-vol.bootUnits; checkStatus(initTables(&vol)); for (iUnit = 0; iUnit < (vol.noOfUnits-vol.bootUnits); iUnit++) vol.virtualUnits[iUnit] = ANAND_NO_UNIT; if (vol.orgUnit >= vol.noOfUnits) { /* no boot record - virgin card, scan it for bad blocks */ DFORMAT_PRINT(("Virgin card rebuilding unit map.\r\n")); prevVirtualSize = 0L; tffsset(ff,0xff,ANAND_SPARE_SIZE); /* Generate the bad unit table */ /* if a unit is not erased it is marked as bad */ for (iUnit = 0; iUnit < vol.noOfUnits; iUnit++) { vol.physicalUnits[iUnit] = (unsigned char)(isErased(&vol,iUnit) ? ANAND_UNIT_FREE : UNIT_BAD_ORIGINAL); #ifndef NT5PORT DFORMAT_PRINT(("Checking unit %ld\r",(CardAddress)iUnit)); #endif/*NT5PORT*/ } DFORMAT_PRINT(("\rMedia has been scanned\r\n")); } else /* Read bad unit table from boot record */ { status = vol.flash->read(vol.flash, unitBaseAddress(&vol,vol.orgUnit) + SECTOR_SIZE, vol.physicalUnits, vol.noOfUnits * sizeof(ANANDPhysUnit), EDC); if( status != flOK ) { dismountNFTL(&vol); /*Free tables must be done after call to initTables*/ return status; } } if(vol.physicalUnits[0] == UNIT_BAD_ORIGINAL) { DFORMAT_PRINT(("ERROR - IPL block is bad.\r\n")); return flBadIPLBlock; } /* count bad units */ vol.noOfTransferUnits += 2; /* include orgUnit & spareOrgUnit */ /* Convert first unit of MDOC of any floor > 0 to BAD in order to make it * unchangeable and force internal EEprom mode */ if(flash->flags & EXTERNAL_EPROM) { long docFloorSize; int iFloor, iPage; docFloorSize = (flash->chipSize * flash->noOfChips) / flash->noOfFloors; for(iFloor=1;( iFloor < flash->noOfFloors ); iFloor++) { iUnit = (ANANDUnitNo)((docFloorSize * (long)iFloor) >> vol.unitSizeBits); if( vol.physicalUnits[iUnit] == ANAND_UNIT_FREE ) { forceHeaderUpdate = TRUE; /* force writing of NFTL Header */ vol.physicalUnits[iUnit] = UNIT_BAD_ORIGINAL; /* mark as BAD */ } status = vol.flash->erase(vol.flash, (word)(iUnit << (vol.unitSizeBits - vol.erasableBlockSizeBits)), (word)(1 << (vol.unitSizeBits - vol.erasableBlockSizeBits))); if( status != flOK ) { dismountNFTL(&vol); /*Free tables must be done after call to initTables*/ return status; } for(iPage=0;( iPage < 2 ); iPage++) { status = vol.flash->write(vol.flash, unitBaseAddress(&vol,iUnit) + iPage * SECTOR_SIZE, (const void FAR1 *)checkSum, EXTRA_LEN, EXTRA); if( status != flOK ) { dismountNFTL(&vol); /*Free tables must be done after call to initTables*/ return status; } } } } /* Translate physicalUnits[] to internal representation */ for (iUnit = 0; iUnit < vol.noOfUnits; iUnit++) { if (vol.physicalUnits[iUnit] != ANAND_UNIT_FREE) vol.physicalUnits[iUnit] = UNIT_BAD_MOUNT; } /* extend bootimage area if there are bad units in it */ for( iUnit = vol.bootUnits = 0; (vol.bootUnits < noOfBootUnits) && (iUnit < vol.noOfUnits); iUnit++ ) if (isAvailable(iUnit)) vol.bootUnits++; if (vol.bootUnits < noOfBootUnits) { dismountNFTL(&vol); /*Free tables must be done after call to initTables*/ return flVolumeTooSmall; } vol.bootUnits = iUnit; /* Discount transfer units taken by the boot image */ for (iUnit = 0; iUnit < vol.bootUnits; iUnit++) if (!isAvailable(iUnit)) { if( vol.noOfTransferUnits <= (ANANDUnitNo)formatParams->noOfSpareUnits ) { dismountNFTL(&vol); /*Free tables must be done after call to initTables*/ return flVolumeTooSmall; } vol.noOfTransferUnits--; } if (vol.noOfUnits <= vol.bootUnits + vol.noOfTransferUnits) { dismountNFTL(&vol); /*Free tables must be done after call to initTables*/ return flVolumeTooSmall; } vol.virtualSectors = (SectorNo)((vol.noOfUnits - vol.bootUnits - vol.noOfTransferUnits) * (unitSize / SECTOR_SIZE)); vol.noOfVirtualUnits = (unsigned short)((vol.virtualSectors + vol.sectorsPerUnit - 1) / vol.sectorsPerUnit); /* Find a place for the boot records and protect them */ /* NOTE : We don't erase the old orgUnits, this might cause a problem when formatting with bootImageLen = 0 and then formatting with bootImageLen = 44Kbyte */ for (vol.orgUnit = vol.bootUnits; vol.orgUnit < vol.noOfUnits; vol.orgUnit++) if (vol.physicalUnits[vol.orgUnit] == ANAND_UNIT_FREE) break; vol.physicalUnits[vol.orgUnit] = UNIT_UNAVAIL; for (vol.spareOrgUnit = vol.orgUnit + 1; vol.spareOrgUnit < vol.noOfUnits; vol.spareOrgUnit++) if (vol.physicalUnits[vol.spareOrgUnit] == ANAND_UNIT_FREE) break; vol.physicalUnits[vol.spareOrgUnit] = UNIT_UNAVAIL; for (iUnit = vol.bootUnits; iUnit < vol.noOfUnits; iUnit++) { status = formatUnit(&vol,iUnit); if(status == flWriteFault) { if ((iUnit != vol.orgUnit) && (iUnit != vol.spareOrgUnit)) { noOfBadUnits++; vol.physicalUnits[iUnit] = UNIT_BAD_MOUNT; /* Mark it bad in table */ if ((noOfBadUnits+2) >= vol.noOfTransferUnits) { dismountNFTL(&vol); /*Free tables must be done after call to initTables*/ return status; } } } else if (status != flOK) { dismountNFTL(&vol); /*Free tables must be done after call to initTables*/ return status; } if (formatParams->progressCallback) { status = (*formatParams->progressCallback) ((word)(vol.noOfUnits - vol.bootUnits), (word)((iUnit + 1) - vol.bootUnits)); if(status!=flOK) { dismountNFTL(&vol); /*Free tables must be done after call to initTables*/ return status; } } } /* Prepare the boot record header */ for(iUnit = 0; iUnit < vol.noOfUnits; iUnit++) { /* Convert Bad Block table to previous state */ if( vol.physicalUnits[iUnit] == UNIT_BAD_MOUNT ) vol.physicalUnits[iUnit] = UNIT_BAD_ORIGINAL; } #ifdef EXTRA_LARGE anandFlagsTmp = bootRecord.anandFlags; #endif /* EXTRA_LARGE */ tffsset(&bootRecord,0xff,sizeof bootRecord); #ifdef EXTRA_LARGE bootRecord.anandFlags = anandFlagsTmp; #endif /* EXTRA_LARGE */ toLE2(bootRecord.noOfUnits,vol.noOfUnits - vol.bootUnits); toLE2(bootRecord.bootUnits,vol.bootUnits); tffscpy(bootRecord.bootRecordId,"ANAND",sizeof bootRecord.bootRecordId); toUNAL4(bootRecord.virtualMediumSize,(CardAddress) vol.virtualSectors * SECTOR_SIZE); /* Write boot records, spare unit first */ vol.physicalUnits[vol.orgUnit] = ANAND_UNIT_FREE; /* Unprotect it */ vol.physicalUnits[vol.spareOrgUnit] = ANAND_UNIT_FREE; /* Unprotect it */ if( ((prevOrgUnit != vol.orgUnit) || (forceHeaderUpdate == TRUE)) || (prevVirtualSize != UNAL4(bootRecord.virtualMediumSize)) ) { /* Copy boot Record to 512 bytes buffer in order to add EDC */ tffsset(nftlBuffer,0,sizeof nftlBuffer); tffscpy(nftlBuffer,&bootRecord,sizeof bootRecord); /* Loop over the original unit and the spare (spare first) */ for (iUnit = vol.spareOrgUnit, prevOrgUnit = 0; prevOrgUnit < 2 ; prevOrgUnit ++) { status = formatUnit(&vol,iUnit); /* Erase unit */ if(status==flOK) /* Write BBT */ { status = vol.flash->write(vol.flash, unitBaseAddress(&vol,iUnit) + SECTOR_SIZE, vol.physicalUnits, vol.noOfUnits * sizeof(ANANDPhysUnit), EDC); } if(status==flOK) /* Write header */ { status = vol.flash->write(vol.flash, unitBaseAddress(&vol,iUnit), nftlBuffer, sizeof (nftlBuffer), EDC); } if(status!=flOK) { dismountNFTL(&vol); /*Free tables must be done after call to initTables*/ return status; } iUnit = vol.orgUnit; } } /* Mark Binary partition with the proper signature */ if (!(formatParams->flags & FL_LEAVE_BINARY_AREA)&& (vol.bootUnits > 0)) { byte sign[BINARY_SIGNATURE_LEN]; /* Add SPL special Signature */ #ifdef WRITE_EXB_IMAGE byte signOffset = 8; tffscpy(sign,SIGN_SPL,BINARY_SIGNATURE_NAME); #else byte signOffset = formatParams->binaryPartitionInfo->signOffset; tffscpy(sign,formatParams->binaryPartitionInfo->sign, BINARY_SIGNATURE_NAME); #endif /* WRITE_EXB_IMAGE */ tffsset(sign+BINARY_SIGNATURE_NAME,'F',BINARY_SIGNATURE_NAME); unitSize = 1L << vol.unitSizeBits; for (iUnit=0;iUnitexbLen) { signOffset = formatParams->binaryPartitionInfo->signOffset; tffscpy(sign,formatParams->binaryPartitionInfo->sign, BINARY_SIGNATURE_NAME); } #endif /* WRITE_EXB_IMAGE */ /* Each logical unit might contain several physical blocks */ for (noOfBootUnits = 0 ; (noOfBootUnits < unitSize) && (status == flOK) ; noOfBootUnits += (ANANDUnitNo)vol.flash->erasableBlockSize) { status = vol.flash->write(vol.flash, unitBaseAddress(&vol,iUnit) + noOfBootUnits + signOffset, sign, BINARY_SIGNATURE_LEN,EXTRA); } if(status != flOK) break; } #ifdef WRITE_EXB_IMAGE else { formatParams->exbLen++; } #endif /* WRITE_EXB_IMAGE */ } } else /* Erase previous Original and SpareOriginal Unit */ { for (iUnit = prevOrgUnit; iUnit < vol.orgUnit; iUnit++) if( vol.physicalUnits[iUnit] != UNIT_BAD_ORIGINAL ) formatUnit(&vol,iUnit); } if (status != flOK) { DEBUG_PRINT(("Debug: NFTL failed while formating the binary partition.\r\n")); } else { DEBUG_PRINT(("Debug: finished NFTL format.\r\n")); } dismountNFTL(&vol); /*Free tables must be done after call to initTables*/ return status; } #endif /* FORMAT_VOLUME */ /*----------------------------------------------------------------------*/ /* N F T L I n f o */ /* */ /* get NFTL information. */ /* */ /* Parameters: */ /* volNo : Volume serial no. */ /* tlInfo : Address of TLInfo record */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/ static FLStatus NFTLInfo(Anand vol, TLInfo *tlInfo) { tlInfo->sectorsInVolume = vol.virtualSectors; tlInfo->bootAreaSize = (unsigned long)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(Anand vol, CardAddress FAR1 * buf, long FAR2 * mediaSize, unsigned FAR2 * noOfBB) { ANANDUnitNo iUnit; ANANDUnitNo maxBad = vol.noOfUnits * ANAND_BAD_PERCENTAGE / 100; CardAddress FAR1* ptr = (CardAddress FAR1*) buf; *noOfBB = 0; for (iUnit=0;(iUnitpartitionNo] = FL_UPS; #endif /* VERIFY_WRITE || VERIFY_ERASED_SECTOR */ tffsset(&bootRecord,0,sizeof(ANANDBootRecord)); DEBUG_PRINT(("Debug: starting NFTL mount.\r\n")); checkStatus(initNFTL(&vol,flash)); *volForCallback = vol.flash; vol.eraseSum = 0; /* Find the medium boot record */ for (vol.orgUnit = 0; vol.orgUnit < vol.noOfUnits; vol.orgUnit++) { vol.flash->read(vol.flash, unitBaseAddress(&vol,vol.orgUnit), &bootRecord, sizeof bootRecord, 0); if (tffscmp(bootRecord.bootRecordId,"ANAND",sizeof bootRecord.bootRecordId) == 0) break; } if (vol.orgUnit >= vol.noOfUnits) { DEBUG_PRINT(("Debug: not NFTL format.\r\n")); return flUnknownMedia; } for (vol.spareOrgUnit = vol.orgUnit + 1; vol.spareOrgUnit < vol.noOfUnits; vol.spareOrgUnit++) { vol.flash->read(vol.flash, unitBaseAddress(&vol,vol.spareOrgUnit), &spareBootRecord, sizeof spareBootRecord, 0); if (tffscmp(spareBootRecord.bootRecordId,"ANAND",sizeof spareBootRecord.bootRecordId) == 0) break; } if (vol.spareOrgUnit >= vol.noOfUnits) vol.spareOrgUnit = ANAND_NO_UNIT; /* Get media information from unit header */ vol.noOfUnits = LE2(bootRecord.noOfUnits); vol.bootUnits = LE2(bootRecord.bootUnits); vol.virtualSectors = (SectorNo)(UNAL4(bootRecord.virtualMediumSize) >> SECTOR_SIZE_BITS); vol.noOfUnits += vol.bootUnits; #ifdef EXTRA_LARGE moreUnitBits = ~bootRecord.anandFlags & MORE_UNIT_BITS_MASK; if (moreUnitBits > 0) { vol.unitSizeBits += moreUnitBits; vol.orgUnit >>= moreUnitBits; if (vol.spareOrgUnit != ANAND_NO_UNIT) vol.spareOrgUnit >>= moreUnitBits; } #endif /* EXTRA_LARGE */ vol.unitOffsetMask = (1L << vol.unitSizeBits) - 1; vol.sectorsPerUnit = 1 << (vol.unitSizeBits - SECTOR_SIZE_BITS); vol.noOfVirtualUnits = (ANANDUnitNo)((vol.virtualSectors + vol.sectorsPerUnit - 1) / vol.sectorsPerUnit); if(((ANANDUnitNo)(vol.virtualSectors >> (vol.unitSizeBits - SECTOR_SIZE_BITS)) > (vol.noOfUnits - vol.bootUnits)) ) { if( vol.spareOrgUnit != ANAND_NO_UNIT ) { vol.noOfUnits = LE2(spareBootRecord.noOfUnits); vol.bootUnits = LE2(spareBootRecord.bootUnits); vol.virtualSectors = (SectorNo)(UNAL4(spareBootRecord.virtualMediumSize) >> SECTOR_SIZE_BITS); vol.noOfUnits += vol.bootUnits; #ifdef EXTRA_LARGE moreUnitBits = ~spareBootRecord.anandFlags & MORE_UNIT_BITS_MASK; if (moreUnitBits > 0) { vol.unitSizeBits += moreUnitBits; vol.orgUnit >>= moreUnitBits; if (vol.spareOrgUnit != ANAND_NO_UNIT) vol.spareOrgUnit >>= moreUnitBits; } #endif /* EXTRA_LARGE */ vol.unitOffsetMask = (1L << vol.unitSizeBits) - 1; vol.sectorsPerUnit = 1 << (vol.unitSizeBits - SECTOR_SIZE_BITS); vol.noOfVirtualUnits = (ANANDUnitNo)((vol.virtualSectors + vol.sectorsPerUnit - 1) / vol.sectorsPerUnit); if ((ANANDUnitNo)(vol.virtualSectors >> (vol.unitSizeBits - SECTOR_SIZE_BITS)) > (vol.noOfUnits - vol.bootUnits)) return flBadFormat; } else return flBadFormat; } checkStatus(initTables(&vol)); /* Read bad unit table from boot record */ status = vol.flash->read(vol.flash, unitBaseAddress(&vol,vol.orgUnit) + SECTOR_SIZE, vol.physicalUnits, vol.noOfUnits * sizeof(ANANDPhysUnit), EDC); if( status != flOK ) { if( vol.spareOrgUnit != ANAND_NO_UNIT ) { status = vol.flash->read(vol.flash, unitBaseAddress(&vol,vol.spareOrgUnit) + SECTOR_SIZE, vol.physicalUnits, vol.noOfUnits * sizeof(ANANDPhysUnit), EDC); if( status != flOK ) { dismountNFTL(&vol); /* Free tables must be done after call to initTables */ return status; } } else return status; } /* Exclude boot-image units */ for (iUnit = 0; iUnit < vol.noOfVirtualUnits; iUnit++) vol.virtualUnits[iUnit] = ANAND_NO_UNIT; /* Translate bad unit table to internal representation */ for (iUnit = 0; iUnit < vol.noOfUnits; iUnit++) { /* Exclude bad & protected units */ if (iUnit < vol.bootUnits || iUnit == vol.orgUnit || iUnit == vol.spareOrgUnit || vol.physicalUnits[iUnit] != ANAND_UNIT_FREE) { if (vol.physicalUnits[iUnit] != ANAND_UNIT_FREE) { vol.physicalUnits[iUnit] = UNIT_BAD_MOUNT; } else { vol.physicalUnits[iUnit] = UNIT_UNAVAIL; } } } /* Mount all units */ for (iUnit = 0; iUnit < vol.noOfUnits; iUnit++) { if ((vol.physicalUnits[iUnit] != UNIT_UNAVAIL) && (vol.physicalUnits[iUnit] != UNIT_BAD_MOUNT)) { status = mountUnit(&vol,iUnit,&currEraseCount); if(status!=flOK) { dismountNFTL(&vol); /*Free tables must be done after call to initTables*/ return status; } vol.eraseSum+=currEraseCount; } } /* Scan for orphan units, and count free units */ vol.freeUnits = 0; for (iUnit = vol.bootUnits; iUnit < vol.noOfUnits; iUnit++) { ANANDPhysUnit FAR1 *pU = &vol.physicalUnits[iUnit]; if (*pU == UNIT_ORPHAN || *pU == (UNIT_REPLACED | UNIT_ORPHAN)) { formatChain(&vol,iUnit); /* Get rid of orphan */ if(iUnit == vol.invalidReplacement) vol.invalidReplacement = ANAND_NO_UNIT; } else if (*pU == (ANAND_UNIT_FREE & ~UNIT_ORPHAN)) *pU = ANAND_UNIT_FREE; /* Reference to free unit. That's OK */ } /* Calculate Free Units again after formatChain */ vol.freeUnits = 0; for (iUnit = vol.bootUnits; iUnit < vol.noOfUnits; iUnit++) { if( vol.physicalUnits[iUnit] == ANAND_UNIT_FREE ) vol.freeUnits++; } /* Initialize allocation rover */ vol.roverUnit = vol.bootUnits; /* Initialize statistics */ vol.sectorsRead = vol.sectorsWritten = vol.sectorsDeleted = 0; vol.parasiteWrites = vol.unitsFolded = 0; #ifndef FL_READ_ONLY /* * Make sure no unit chain with an invalid replacemenet unit * pointer on the last unit */ if(vol.invalidReplacement != ANAND_NO_UNIT) { getUnitData(&vol,vol.invalidReplacement,&virUnit,&nextUnit); virUnit = virUnit&(~ANAND_REPLACING_UNIT); if(virUnit >= vol.noOfVirtualUnits) { DEBUG_PRINT(("ERROR - a bad unit header encountered.\r\n")); dismountNFTL(&vol); return flBadFormat; } iUnit = vol.virtualUnits[virUnit]; if(iUnit >= vol.noOfUnits) { DEBUG_PRINT(("ERROR - a bad unit header encountered.\r\n")); dismountNFTL(&vol); return flBadFormat; } setUnavail(iUnit); checkStatus(foldUnit(&vol,virUnit,TRUE)); } /* Make sure there is at least 1 free unit */ if(vol.freeUnits == 0) foldBestChain(&vol,&iUnit); /* make free units by folding the best chain */ #endif /* FL_READ_ONLY */ /* Set TL routine */ tl->rec = &vol; tl->mapSector = mapSector; #ifndef FL_READ_ONLY tl->writeSector = writeSector; tl->deleteSector = deleteSector; #ifdef DEFRAGMENT_VOLUME tl->defragment = defragment; #endif #if (defined(VERIFY_WRITE) || defined (VERIFY_VOLUME) || defined(VERIFY_ERASED_SECTOR)) tl->checkVolume = checkVolume; #endif /* VERIFY_WRITE || VERIFY_VOLUME || VERIFY_ERASED_SECTOR */ #endif /* FL_READ_ONLY */ tl->sectorsInVolume = sectorsInVolume; tl->getTLInfo = NFTLInfo; tl->tlSetBusy = tlSetBusy; tl->dismount = dismountNFTL; #ifndef NO_READ_BBT_CODE tl->readBBT = readBBT; #endif /* NO_READ_BBT_CODE */ tl->writeMultiSector = NULL; tl->readSectors = NULL; DEBUG_PRINT(("Debug: finished NFTL mount.\r\n")); #ifdef NFTL_CACHE /* 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 = vol.ucacheBuf; #endif /* FL_MALLOC */ } #ifdef ENVIRONMENT_VARS else vol.ucache = NULL; #endif if (vol.ucache != NULL) { for (iUnit = 0; iUnit < vol.noOfUnits; iUnit++) { vol.ucache[iUnit].virtualUnitNo = 0xDEAD; vol.ucache[iUnit].replacementUnitNo = 0xDEAD; } } else { DEBUG_PRINT(("Debug: NFTL 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 { scacheSize = (unsigned long)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 = (unsigned char FAR1*) FL_FAR_MALLOC (scacheSize); #else vol.scache = vol.scacheBuf; #endif /* FL_MALLOC */ } #ifdef ENVIRONMENT_VARS else vol.scache = NULL; #endif 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. */ unsigned char val = (S_CACHE_SECTOR_IGNORE << 6) | (S_CACHE_SECTOR_IGNORE << 4) | (S_CACHE_SECTOR_IGNORE << 2) | S_CACHE_SECTOR_IGNORE; unsigned long iC; for(iC=0;( iC < scacheSize );iC++) vol.scache[iC] = val; } else { DEBUG_PRINT(("Debug: NFTL runs without S-cache\r\n")); } #endif /* NFTL_CACHE */ vol.badFormat = FALSE; vol.wearLevel.alarm = (unsigned char)(vol.eraseSum % WLnow); vol.wearLevel.currUnit = (ANANDUnitNo)(vol.eraseSum % vol.noOfVirtualUnits); return flOK; } /*----------------------------------------------------------------------*/ /* f l R e g i s t e r N F T L */ /* */ /* Register this translation layer */ /* */ /* Parameters: */ /* None */ /* */ /* Returns: */ /* FLStatus : 0 on success, otherwise failure */ /*----------------------------------------------------------------------*/ FLStatus flRegisterNFTL(void) { #ifdef FL_MALLOC unsigned i; #endif if (noOfTLs >= TLS) return flTooManyComponents; tlTable[noOfTLs].mountRoutine = mountNFTL; #ifdef FORMAT_VOLUME tlTable[noOfTLs].formatRoutine = formatNFTL; #else tlTable[noOfTLs].formatRoutine = noFormat; #endif noOfTLs++; #ifdef FL_MALLOC for(i=0;( i < VOLUMES );i++) { vols[i].physicalUnits = NULL; vols[i].virtualUnits = NULL; #ifdef NFTL_CACHE vols[i].ucache = NULL; vols[i].scache = NULL; #endif /* NFTL_CACHE */ } #endif /* FL_MALLOC */ return flOK; } #ifndef FL_READ_ONLY /*----------------------------------------------------------------------*/ /* 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(Anand 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")); /* Force remapping of internal catched sector */ vol.flash->socket->remapped = TRUE; #ifdef NFTL_CACHE setSectorFlagsCache(&vol, addr, SECTOR_IGNORE); #endif /* NFTL_CACHE */ vol.flash->write(vol.flash,addr+SECTOR_DATA_OFFSET,sectorFlags,sizeof(sectorFlags),EXTRA); #if 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,nftlBuffer,sizeof(nftlBuffer),EDC)==flOK) { tffsset(nftlBuffer,0,sizeof(nftlBuffer)); vol.flash->write(vol.flash,addr,nftlBuffer,sizeof(nftlBuffer),0); #if (defined(VERIFY_WRITE) || defined (VERIFY_VOLUME) || defined(VERIFY_ERASED_SECTOR)) /* Set all ff's for verifySector routine */ tffsset(nftlBuffer,0xff,sizeof(nftlBuffer)); #endif /* VERIFY_WRITE || VERIFY_VOLUME || VERIFY_ERASED_SECTOR */ } #endif /* MAKE_SURE_IGNORE_HAS_BAD_EDC */ } #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(Anand vol, dword sectorCount) { FLStatus status; ANANDUnitNo virUnitNo; ANANDUnitNo unitNo; dword curRead; CardAddress unitOffset; CardAddress startSectorAddress; CardAddress sourceSectorAddress; CardAddress nextFreeSectorAddress; byte index,sectorFlags; 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.sectorsPerUnit); sectorCount = TFFSMIN(vol.virtualSectors - vol.verifiedSectorNo,sectorCount); /* Force remapping of internal catched sector */ vol.flash->socket->remapped = TRUE; tffsset(nftlBuffer,0xff,sizeof(nftlBuffer)); /* 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.sectorsPerUnit) << SECTOR_SIZE_BITS); curRead = TFFSMIN(sectorCount,((1UL<>SECTOR_SIZE_BITS); unitNo = vol.virtualUnits[virUnitNo]; if(unitNo == ANAND_NO_UNIT) /* Unit is empty */ { vol.verifiedSectorNo += ((1<>SECTOR_SIZE_BITS; continue; } /* Check all sector of unit or until required sectors */ startSectorAddress = unitBaseAddress(&vol,unitNo)+unitOffset; for (index = 0 ; index < curRead ; index++, vol.verifiedSectorNo++) { nextFreeSectorAddress = ANAND_UNASSIGNED_ADDRESS; /* Search end addr */ sourceSectorAddress = virtual2Physical(&vol, vol.verifiedSectorNo, &nextFreeSectorAddress); if(sourceSectorAddress == ANAND_UNASSIGNED_ADDRESS) /* No written sector */ sourceSectorAddress = startSectorAddress + ((CardAddress)index << SECTOR_SIZE_BITS); sectorFlags = getSectorFlags(&vol,sourceSectorAddress); if(sectorFlags == SECTOR_FREE) { checkStatus(vol.flash->read(vol.flash,sourceSectorAddress,buffer,SECTOR_SIZE,0)); if (tffscmp(nftlBuffer,buffer,SECTOR_SIZE)!=0) markAsIgnored(&vol,sourceSectorAddress); continue; } else /* Used sector */ { status = vol.flash->read(vol.flash,sourceSectorAddress,buffer,SECTOR_SIZE,EDC); switch (status) { case flDataError: markAsIgnored(&vol,sourceSectorAddress); createUnitCount(&vol,virUnitNo); break; /* Mark as ignored */ case flOK: break; /* Sector OK */ default: return status; /* Report error */ } } /* Check the next free sector to make sure it is erased */ if(nextFreeSectorAddress != ANAND_UNASSIGNED_ADDRESS) { checkStatus(vol.flash->read(vol.flash,nextFreeSectorAddress,buffer,SECTOR_SIZE,0)); if (tffscmp(nftlBuffer,buffer,SECTOR_SIZE)!=0) markAsIgnored(&vol,nextFreeSectorAddress); } } /* Loop over all sector of unit or until required sectors */ } /* 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(Anand vol) { return verifySectors(&vol, 0xffffffff); } #endif /* VERIFY_WRITE || VERIFY_VOLUME || VERIFY_ERASED_SECTOR */ /*----------------------------------------------------------------------*/ /* c h e c k F o l d i n g */ /* */ /* Check folding status and if needed fold again. */ /* */ /* Parameters: */ /* vol : Pointer identifying drive */ /* virtualUnitNo : Virtual unit number to re-fold */ /* */ /* Returns: */ /* FLStatus : 0 on success, failed otherwise */ /*----------------------------------------------------------------------*/ static FLStatus checkFolding(Anand vol, ANANDUnitNo virtualUnitNo) { ANANDUnitNo tmp; if(vol.freeUnits == 0) checkStatus(foldBestChain(&vol, &tmp)); return foldUnit(&vol,virtualUnitNo,TRUE); } #endif /* FL_READ_ONLY */