/* * FreeRTOS+FAT V2.3.3 * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * https://www.FreeRTOS.org * https://github.com/FreeRTOS * */ /** * @file ff_fat.c * @ingroup FAT * * @defgroup FAT Fat File-System * @brief Handles FAT access and traversal. * * Provides file-system interfaces for the FAT file-system. **/ #include "ff_headers.h" #include #if ffconfigFAT_USES_STAT /* This module make use of a buffer caching called 'FF_FATBuffers_t'. * The struct below may gather statistics about its usage: hits/misses. */ struct SFatStat fatStat; #endif /* ffconfigFAT_USES_STAT */ /* prvGetFromFATBuffers() will see if the FF_Buffer_t pointed to by ppxBuffer contains the * buffer that is needed, i.e. opened for the same sector and with the correct R/W mode. * If ppxBuffer is NULL or if it can not be used, a new buffer will be created. * The buffer pointed to by ppxBuffer will either be released or its pointer will be returned. */ FF_Buffer_t * prvGetFromFATBuffers( FF_IOManager_t * pxIOManager, FF_FATBuffers_t * pxFATBuffers, BaseType_t xBufferIndex, uint32_t ulFATSector, FF_Error_t * pxError, uint8_t ucMode ); #if ( ffconfigFAT12_SUPPORT != 0 ) /* A very special case for FAT12: an entry is stored in two sectors. * Read the two sectors and merge the two values found. */ static uint32_t prvGetFAT12Entry( FF_IOManager_t * pxIOManager, FF_Error_t * pxError, FF_FATBuffers_t * pxFATBuffers, uint32_t ulFATSector ); #endif #if ( ffconfigFAT12_SUPPORT != 0 ) /* Same as above: put a FAT12 entry that is spread-out over two sectors. * Read the current value first to preserve and merge the earlier 4 bits * of an adjacent FAT12 entry. */ static FF_Error_t prvPutFAT12Entry( FF_IOManager_t * pxIOManager, uint32_t ulCluster, uint32_t ulValue, FF_FATBuffers_t * pxFATBuffers, uint32_t ulFATSector ); #endif #if ( ffconfigFAT12_SUPPORT != 0 ) /* A generic less-optimised way of finding the first free cluster. * Used for FAT12 only. */ static uint32_t prvFindFreeClusterSimple( FF_IOManager_t * pxIOManager, FF_Error_t * pxError ); #endif /* ffconfigFAT12_SUPPORT */ #if ( ffconfigFAT12_SUPPORT != 0 ) /* A generic less-optimised way of counting free clusters. * Used for FAT12 only. */ static uint32_t prvCountFreeClustersSimple( FF_IOManager_t * pxIOManager, FF_Error_t * pxError ); #endif /* ffconfigFAT12_SUPPORT */ /* Have a cluster number and translate it to an LBA (Logical Block Address). * 'ulSectorsPerCluster' should be seen as 'blocks per cluster', where the length of one * block is defined in the PBR (Partition Boot Record) at FF_FAT_BYTES_PER_SECTOR (offset 0x0B). */ uint32_t FF_Cluster2LBA( FF_IOManager_t * pxIOManager, uint32_t ulCluster ) { uint32_t ulLBA = 0; FF_Partition_t * pxPartition; if( pxIOManager != NULL ) { pxPartition = &( pxIOManager->xPartition ); if( ulCluster >= 2 ) { ulLBA = ( ( ulCluster - 2 ) * pxPartition->ulSectorsPerCluster ) + pxPartition->ulFirstDataSector; } else { ulLBA = pxPartition->ulClusterBeginLBA; } } return ulLBA; } /*-----------------------------------------------------------*/ /* * Major and Minor sectors/blocks: * * A cluster is defined as N "sectors". Those sectors in fact are "major blocks" * whose size is defined in a field called 'FF_FAT_BYTES_PER_SECTOR' in the PBR. * * I/O to the disk takes place in "minor block" of usually 512 byte and the addressing * is also based on "minor block" sector numbers. * * In most cases, Major == Minor == 512 bytes. * * Here below some translations are done for 'entries', which can be 1-byte entries * as well as the 32-byte directory entries. * */ /* Translate an 'entry number' (ulEntry) to a relative cluster number, * where e.g. 'ulEntry' may be a sequence number of a directory entry for * which ulEntrySize = 32 bytes. */ uint32_t FF_getClusterChainNumber( FF_IOManager_t * pxIOManager, uint32_t ulEntry, uint32_t ulEntrySize ) { uint32_t ulBytesPerCluster = pxIOManager->xPartition.usBlkSize * pxIOManager->xPartition.ulSectorsPerCluster; uint32_t ulEntriesPerCluster = ( ulBytesPerCluster / ulEntrySize ); /* E.g. ulBytesPerCluster = 16384, ulEntrySize = 32: 16384 / 32 = 512 entries per cluster. */ return ulEntry / ulEntriesPerCluster; } /*-----------------------------------------------------------*/ /* If the above function returns a cluster number, this function * returns a BYTE position within that cluster. */ uint32_t FF_getClusterPosition( FF_IOManager_t * pxIOManager, uint32_t ulEntry, uint32_t ulEntrySize ) { uint32_t ulBytesPerCluster = pxIOManager->xPartition.usBlkSize * pxIOManager->xPartition.ulSectorsPerCluster; uint32_t ulEntriesPerCluster = ( ulBytesPerCluster / ulEntrySize ); /* Return the block offset within the current cluster: */ return ( ulEntry % ulEntriesPerCluster ) * ulEntrySize; } /*-----------------------------------------------------------*/ /* Return the block offset (= number of major blocks) within the current cluster: */ uint32_t FF_getMajorBlockNumber( FF_IOManager_t * pxIOManager, uint32_t ulEntry, uint32_t ulEntrySize ) { uint32_t ulBytesPerCluster = pxIOManager->xPartition.usBlkSize * pxIOManager->xPartition.ulSectorsPerCluster; uint32_t ulEntriesPerCluster = ( ulBytesPerCluster / ulEntrySize ); uint32_t ulRelClusterEntry; /* Calculate the entry number within a cluster: */ ulRelClusterEntry = ulEntry % ulEntriesPerCluster; /* Return the block offset within the current cluster: */ return ulRelClusterEntry / ( pxIOManager->xPartition.usBlkSize / ulEntrySize ); } /*-----------------------------------------------------------*/ /* Return the minor block number within the current major block */ uint32_t FF_getMinorBlockNumber( FF_IOManager_t * pxIOManager, uint32_t ulEntry, uint32_t ulEntrySize ) { uint32_t ulBytesPerCluster = pxIOManager->xPartition.usBlkSize * pxIOManager->xPartition.ulSectorsPerCluster; uint32_t ulEntriesPerCluster = ( ulBytesPerCluster / ulEntrySize ); uint32_t ulRelClusterEntry; uint32_t ulRelMajorBlockEntry; /* Calculate the entry number within a cluster: */ ulRelClusterEntry = ulEntry % ulEntriesPerCluster; ulRelMajorBlockEntry = ulRelClusterEntry % ( pxIOManager->xPartition.usBlkSize / ulEntrySize ); return ulRelMajorBlockEntry / ( pxIOManager->usSectorSize / ulEntrySize ); } /*-----------------------------------------------------------*/ /* Get the entry number within the minor block */ uint32_t FF_getMinorBlockEntry( FF_IOManager_t * pxIOManager, uint32_t ulEntry, uint32_t ulEntrySize ) { uint32_t ulBytesPerCluster = pxIOManager->xPartition.usBlkSize * pxIOManager->xPartition.ulSectorsPerCluster; uint32_t ulEntriesPerCluster = ( ulBytesPerCluster / ulEntrySize ); uint32_t ulRelClusterEntry; uint32_t ulRelMajorBlockEntry; /* Calculate the entry number within a cluster: */ ulRelClusterEntry = ulEntry % ulEntriesPerCluster; ulRelMajorBlockEntry = ulRelClusterEntry % ( pxIOManager->xPartition.usBlkSize / ulEntrySize ); return ulRelMajorBlockEntry % ( pxIOManager->usSectorSize / ulEntrySize ); } /*-----------------------------------------------------------*/ FF_Error_t FF_ReleaseFATBuffers( FF_IOManager_t * pxIOManager, FF_FATBuffers_t * pxFATBuffers ) { BaseType_t xIndex; FF_Error_t xError = FF_ERR_NONE; FF_Buffer_t * pxBuffer; #if ffconfigBUF_STORE_COUNT != 2 #warning Only maintaining one FAT table #endif /* 'ffconfigBUF_STORE_COUNT' equals to the number of FAT tables. */ for( xIndex = 0; xIndex < ffconfigBUF_STORE_COUNT; xIndex++ ) { pxBuffer = pxFATBuffers->pxBuffers[ xIndex ]; if( pxBuffer != NULL ) { FF_Error_t xTempError = FF_ERR_NONE; pxFATBuffers->pxBuffers[ xIndex ] = NULL; xTempError = FF_ReleaseBuffer( pxIOManager, pxBuffer ); if( FF_isERR( xError ) == pdFALSE ) { /* as everywhere, this function will return * the first error that occurred, if any. */ xError = xTempError; } } } #if ffconfigFAT_USES_STAT { fatStat.clearCount++; } #endif /* ffconfigFAT_USES_STAT */ return xError; } /*-----------------------------------------------------------*/ FF_Buffer_t * prvGetFromFATBuffers( FF_IOManager_t * pxIOManager, FF_FATBuffers_t * pxFATBuffers, BaseType_t xBufferIndex, uint32_t ulFATSector, FF_Error_t * pxError, uint8_t ucMode ) { FF_Error_t xError = FF_ERR_NONE; FF_Buffer_t * pxBuffer = NULL; if( pxFATBuffers != NULL ) { /* See if the same buffer can be reused. */ pxBuffer = pxFATBuffers->pxBuffers[ xBufferIndex ]; if( pxBuffer != NULL ) { /* Now the buffer is either owned by pxBuffer, * or it has been released, so put it to NULL. */ pxFATBuffers->pxBuffers[ xBufferIndex ] = NULL; if( ( pxBuffer->ulSector == ulFATSector ) && ( ( ( ucMode & FF_MODE_WRITE ) == 0 ) || ( ( pxBuffer->ucMode & FF_MODE_WRITE ) != 0 ) ) ) { /* Same sector, AND * write-permission is not required OR the buffer has write permission: * it can be reused. */ #if ffconfigFAT_USES_STAT { fatStat.reuseCount[ ( ucMode & FF_MODE_WRITE ) ? 1 : 0 ]++; } #endif /* ffconfigFAT_USES_STAT */ } else { xError = FF_ReleaseBuffer( pxIOManager, pxBuffer ); pxBuffer = NULL; #if ffconfigFAT_USES_STAT { fatStat.missCount[ ( ucMode & FF_MODE_WRITE ) ? 1 : 0 ]++; } #endif /* ffconfigFAT_USES_STAT */ } } else { #if ffconfigFAT_USES_STAT { fatStat.getCount[ ( ucMode & FF_MODE_WRITE ) ? 1 : 0 ]++; } #endif /* ffconfigFAT_USES_STAT */ } } if( ( pxBuffer == NULL ) && ( FF_isERR( xError ) == pdFALSE ) ) { pxBuffer = FF_GetBuffer( pxIOManager, ulFATSector, ucMode ); if( pxBuffer == NULL ) { /* Setting an error code without the Module/Function, * will be filled-in by the caller. */ xError = ( FF_Error_t ) ( FF_ERR_DEVICE_DRIVER_FAILED | FF_ERRFLAG ); } } *pxError = xError; return pxBuffer; } #if ( ffconfigFAT12_SUPPORT != 0 ) /* A very special case for FAT12: an entry is stored in two sectors. * Read the two sectors and merge the two values found. */ static uint32_t prvGetFAT12Entry( FF_IOManager_t * pxIOManager, FF_Error_t * pxError, FF_FATBuffers_t * pxFATBuffers, uint32_t ulFATSector ) { FF_Error_t xError = FF_ERR_NONE; FF_Buffer_t * pxBuffer = NULL; /* preferred buffer access mode, user might want to update this entry * and set it to FF_MODE_WRITE. */ uint8_t ucMode = pxFATBuffers ? pxFATBuffers->ucMode : FF_MODE_READ; /* Collect the two bytes in an array. */ uint8_t ucBytes[ 2 ]; /* The function return value. */ uint32_t ulFATEntry = 0UL; pxBuffer = prvGetFromFATBuffers( pxIOManager, pxFATBuffers, 0, ulFATSector, &xError, ucMode ); if( FF_isERR( xError ) ) { xError = FF_GETERROR( xError ) | FF_GETFATENTRY; } else { /* Fetch the very last byte of this segment. */ ucBytes[ 0 ] = FF_getChar( pxBuffer->pucBuffer, ( uint16_t ) ( pxIOManager->usSectorSize - 1 ) ); xError = FF_ReleaseBuffer( pxIOManager, pxBuffer ); /* release the other buffer as well. */ if( ( FF_isERR( xError ) == pdFALSE ) && ( pxFATBuffers != NULL ) ) { xError = FF_ReleaseFATBuffers( pxIOManager, pxFATBuffers ); } if( FF_isERR( xError ) == pdFALSE ) { /* Second Buffer get the first Byte in buffer (second byte of out address)! */ pxBuffer = FF_GetBuffer( pxIOManager, ulFATSector + 1, ucMode ); if( pxBuffer == NULL ) { xError = ( FF_Error_t ) ( FF_ERR_DEVICE_DRIVER_FAILED | FF_GETFATENTRY ); } else { /* Read the first byte from the subsequent sector. */ ucBytes[ 1 ] = FF_getChar( pxBuffer->pucBuffer, 0 ); /* And release that buffer. */ xError = FF_ReleaseBuffer( pxIOManager, pxBuffer ); if( FF_isERR( xError ) == pdFALSE ) { /* Join the two bytes: */ ulFATEntry = ( uint32_t ) FF_getShort( ( uint8_t * ) ucBytes, 0 ); } } } } *pxError = xError; return ( int32_t ) ulFATEntry; } #endif /* ffconfigFAT12_SUPPORT */ /*-----------------------------------------------------------*/ /* Get a FAT entry, which is nothing more than a number referring to a sector. */ uint32_t FF_getFATEntry( FF_IOManager_t * pxIOManager, uint32_t ulCluster, FF_Error_t * pxError, FF_FATBuffers_t * pxFATBuffers ) { FF_Buffer_t * pxBuffer = NULL; uint32_t ulFATOffset; uint32_t ulFATSector = 0; uint32_t ulFATSectorEntry; /* The function result. */ uint32_t ulFATEntry = 0; uint32_t ulLBAAdjust; uint32_t ulRelClusterEntry = 0; FF_Error_t xError = FF_ERR_NONE; /* preferred mode, user might want to update this entry. */ uint8_t ucMode = pxFATBuffers ? pxFATBuffers->ucMode : FF_MODE_READ; FF_Assert_Lock( pxIOManager, FF_FAT_LOCK ); if( ulCluster >= pxIOManager->xPartition.ulNumClusters ) { /* _HT_ find a more specific error code. * Probably not really important as this is a function internal to the library. */ xError = ( FF_Error_t ) ( FF_ERR_IOMAN_NOT_ENOUGH_FREE_SPACE | FF_GETFATENTRY ); } else { if( pxIOManager->xPartition.ucType == FF_T_FAT32 ) { ulFATOffset = ulCluster * 4; } else if( pxIOManager->xPartition.ucType == FF_T_FAT16 ) { ulFATOffset = ulCluster * 2; } else /* pxIOManager->xPartition.ucType == FF_T_FAT12 */ { ulFATOffset = ulCluster + ( ulCluster / 2 ); } ulFATSector = pxIOManager->xPartition.ulFATBeginLBA + ( ulFATOffset / pxIOManager->xPartition.usBlkSize ); ulFATSectorEntry = ulFATOffset % pxIOManager->xPartition.usBlkSize; ulLBAAdjust = ulFATSectorEntry / ( ( uint32_t ) pxIOManager->usSectorSize ); ulRelClusterEntry = ulFATSectorEntry % pxIOManager->usSectorSize; ulFATSector = FF_getRealLBA( pxIOManager, ulFATSector ); ulFATSector += ulLBAAdjust; } #if ( ffconfigFAT12_SUPPORT != 0 ) if( ( pxIOManager->xPartition.ucType == FF_T_FAT12 ) && ( FF_isERR( xError ) == pdFALSE ) && ( ulRelClusterEntry == ( uint32_t ) ( ( pxIOManager->usSectorSize - 1 ) ) ) ) { /* Fat Entry SPANS a Sector! * It has 4 bits on one sector and 8 bits on the other sector. * Handle this in a separate function prvGetFAT12Entry(). */ ulFATEntry = prvGetFAT12Entry( pxIOManager, &xError, pxFATBuffers, ulFATSector ); if( ( ulCluster & 0x0001 ) != 0 ) { /* For odd clusters, shift the address 4 bits to the right: */ ulFATEntry = ( ulFATEntry & 0xfff0 ) >> 4; } else { /* For even clusters, take the lower 12 bits: */ ulFATEntry = ( ulFATEntry & 0x0fff ); } /* Return ulFATEntry, unless xError contains an error. */ } else #endif /* ffconfigFAT12_SUPPORT */ if( FF_isERR( xError ) == pdFALSE ) { /* Handle FAT16, FAT32, and FAT12 (in case the entry lies on a single sector). */ pxBuffer = prvGetFromFATBuffers( pxIOManager, pxFATBuffers, 0, ulFATSector, &xError, ucMode ); if( FF_isERR( xError ) ) { xError = FF_GETERROR( xError ) | FF_GETFATENTRY; } else { switch( pxIOManager->xPartition.ucType ) { case FF_T_FAT32: ulFATEntry = FF_getLong( pxBuffer->pucBuffer, ulRelClusterEntry ); /* Clear the top 4 bits. */ ulFATEntry &= 0x0fffffff; break; case FF_T_FAT16: ulFATEntry = ( uint32_t ) FF_getShort( pxBuffer->pucBuffer, ulRelClusterEntry ); break; #if ( ffconfigFAT12_SUPPORT != 0 ) case FF_T_FAT12: ulFATEntry = ( uint32_t ) FF_getShort( pxBuffer->pucBuffer, ulRelClusterEntry ); /* Entries are either stored as 4 + 8 bits or as 8 + 4 bits, * depending on the cluster being odd or even. */ if( ( ulCluster & 0x0001 ) != 0 ) { /* For odd clusters, shift the address 4 bits to the right: */ ulFATEntry = ( ulFATEntry & 0xfff0 ) >> 4; } else { /* For even clusters, take the lower 12 bits: */ ulFATEntry = ( ulFATEntry & 0x0fff ); } break; #endif /* if ( ffconfigFAT12_SUPPORT != 0 ) */ default: ulFATEntry = 0; break; } if( pxFATBuffers != NULL ) { /* Store the buffer. */ pxFATBuffers->pxBuffers[ 0 ] = pxBuffer; } else { /* Or release it. */ xError = FF_ReleaseBuffer( pxIOManager, pxBuffer ); } } /* if( FF_isERR( xError ) == pdFALSE ) */ } /* else Handle FAT16, FAT32, and FAT12 (in case the entry lies on a single sector). */ if( FF_isERR( xError ) ) { /* The sector address 0 is not meaningful and here it is used as the 'error value'. */ ulFATEntry = 0UL; } if( pxError != NULL ) { *pxError = xError; } return ( int32_t ) ulFATEntry; } /* FF_getFATEntry() */ /*-----------------------------------------------------------*/ /* Write all zero's to all sectors of a given cluster. */ FF_Error_t FF_ClearCluster( FF_IOManager_t * pxIOManager, uint32_t ulCluster ) { FF_Error_t xError = FF_ERR_NONE; FF_Buffer_t * pxBuffer = NULL; BaseType_t xIndex; uint32_t ulBaseLBA; /* Calculate from cluster number to a real block address. */ ulBaseLBA = FF_Cluster2LBA( pxIOManager, ulCluster ); ulBaseLBA = FF_getRealLBA( pxIOManager, ulBaseLBA ); for( xIndex = 0; xIndex < ( BaseType_t ) pxIOManager->xPartition.ulSectorsPerCluster; xIndex++ ) { if( xIndex == 0 ) { /* When using the FF_MODE_WR_ONLY flag, the data will not be read from disk. * Only in the first round a buffer will be claimed. */ pxBuffer = FF_GetBuffer( pxIOManager, ulBaseLBA, FF_MODE_WR_ONLY ); if( pxBuffer == NULL ) { xError = ( FF_Error_t ) ( FF_ERR_DEVICE_DRIVER_FAILED | FF_CLEARCLUSTER ); break; } memset( pxBuffer->pucBuffer, 0x00, pxIOManager->usSectorSize ); } xError = FF_BlockWrite( pxIOManager, ulBaseLBA + xIndex, 1, pxBuffer->pucBuffer, pdFALSE ); if( FF_isERR( xError ) ) { break; } } if( pxBuffer != NULL ) { FF_Error_t xTempError; /* The contents of the buffer (all zero's) has been written explicitly to disk * by calling FF_BlockWrite(). Therefore, the bModified should be cleared. */ pxBuffer->bModified = pdFALSE; /* Releasing the handle will not write anything */ xTempError = FF_ReleaseBuffer( pxIOManager, pxBuffer ); if( FF_isERR( xError ) == pdFALSE ) { xError = xTempError; } } return xError; } /*-----------------------------------------------------------*/ /** * @private * @brief Returns the Cluster address of the Cluster number from the beginning of a chain. * * @param pxIOManager FF_IOManager_t Object * @param ulStart Cluster address of the first cluster in the chain. * @param ulCount Number of Cluster in the chain, * * * **/ uint32_t FF_TraverseFAT( FF_IOManager_t * pxIOManager, uint32_t ulStart, uint32_t ulCount, FF_Error_t * pxError ) { FF_Error_t xError = FF_ERR_NONE; uint32_t ulIndex; uint32_t ulFatEntry = ulStart; uint32_t ulCurrentCluster = ulStart; FF_FATBuffers_t xFATBuffers; BaseType_t xTakeLock = FF_Has_Lock( pxIOManager, FF_FAT_LOCK ) == pdFALSE; /* xFATBuffers is nothing more than an array of FF_Buffer_t's. * One buffer for each FAT copy on disk. */ FF_InitFATBuffers( &xFATBuffers, FF_MODE_READ ); if( xTakeLock ) { FF_LockFAT( pxIOManager ); } for( ulIndex = 0; ulIndex < ulCount; ulIndex++ ) { ulFatEntry = FF_getFATEntry( pxIOManager, ulCurrentCluster, &xError, &xFATBuffers ); if( FF_isERR( xError ) ) { ulFatEntry = 0; break; } if( FF_isEndOfChain( pxIOManager, ulFatEntry ) ) { ulFatEntry = ulCurrentCluster; break; } ulCurrentCluster = ulFatEntry; } if( xTakeLock ) { FF_UnlockFAT( pxIOManager ); } { FF_Error_t xTempError; xTempError = FF_ReleaseFATBuffers( pxIOManager, &xFATBuffers ); if( FF_isERR( xError ) == pdFALSE ) { xError = xTempError; } } *pxError = xError; return ulFatEntry; } /*-----------------------------------------------------------*/ uint32_t FF_FindEndOfChain( FF_IOManager_t * pxIOManager, uint32_t ulStart, FF_Error_t * pxError ) { uint32_t ulFatEntry = ulStart; FF_Error_t xError; if( FF_isEndOfChain( pxIOManager, ulStart ) == pdFALSE ) { /* Traverse FAT for (2^32-1) items/clusters, * or until end-of-chain is encountered. */ ulFatEntry = FF_TraverseFAT( pxIOManager, ulStart, ~0UL, &xError ); } else { xError = FF_ERR_NONE; } *pxError = xError; return ulFatEntry; } /*-----------------------------------------------------------*/ /** * @private * @brief Tests if the ulFATEntry is an End of Chain Marker. * * @param pxIOManager FF_IOManager_t Object * @param ulFATEntry The fat entry from the FAT table to be checked. * * @return pdTRUE if it is an end of chain, otherwise pdFALSE. * **/ BaseType_t FF_isEndOfChain( FF_IOManager_t * pxIOManager, uint32_t ulFATEntry ) { BaseType_t xResult = pdFALSE; if( pxIOManager->xPartition.ucType == FF_T_FAT32 ) { if( ( ulFATEntry & 0x0fffffff ) >= 0x0ffffff8 ) { xResult = pdTRUE; } } else if( pxIOManager->xPartition.ucType == FF_T_FAT16 ) { if( ulFATEntry >= 0x0000fff8 ) { xResult = pdTRUE; } } else { if( ulFATEntry >= 0x00000ff8 ) { xResult = pdTRUE; } } if( ulFATEntry == 0x00000000 ) { xResult = pdTRUE; /* Perhaps trying to read a deleted file! */ } return xResult; } /*-----------------------------------------------------------*/ #if ( ffconfigFAT12_SUPPORT != 0 ) static FF_Error_t prvPutFAT12Entry( FF_IOManager_t * pxIOManager, uint32_t ulCluster, uint32_t ulValue, FF_FATBuffers_t * pxFATBuffers, uint32_t ulFATSector ) { FF_Buffer_t * pxBuffer = NULL; /* For FAT12 FAT Table Across sector boundary traversal. */ uint8_t ucBytes[ 2 ]; /* The function result value. */ uint32_t ulFATEntry; FF_Error_t xError = FF_ERR_NONE; BaseType_t xIndex; #if ( ffconfigWRITE_BOTH_FATS != 0 ) const BaseType_t xNumFATs = pxIOManager->xPartition.ucNumFATS; #else const BaseType_t xNumFATs = 1; #endif /* This routine will only change 12 out of 16 bits. * Get the current 16-bit value, 4 bits shall be preserved. */ ulFATEntry = prvGetFAT12Entry( pxIOManager, &xError, pxFATBuffers, ulFATSector ); if( FF_isERR( xError ) == pdFALSE ) { if( ( ulCluster & 0x0001 ) != 0 ) { ulFATEntry &= 0x000F; ulValue = ( ulValue << 4 ); ulValue &= 0xFFF0; } else { ulFATEntry &= 0xF000; ulValue &= 0x0FFF; } ulFATEntry |= ulValue; /* Write at offset 0 in the array ucBytes. */ FF_putShort( ucBytes, 0, ( uint16_t ) ulFATEntry ); for( xIndex = 0; xIndex < xNumFATs; xIndex++, ulFATSector += pxIOManager->xPartition.ulSectorsPerFAT ) { /* Write the last byte in the first sector. */ pxBuffer = FF_GetBuffer( pxIOManager, ulFATSector, FF_MODE_WRITE ); { if( pxBuffer == NULL ) { xError = ( FF_Error_t ) ( FF_ERR_DEVICE_DRIVER_FAILED | FF_PUTFATENTRY ); break; } FF_putChar( pxBuffer->pucBuffer, ( uint16_t ) ( pxIOManager->usSectorSize - 1 ), ucBytes[ 0 ] ); } xError = FF_ReleaseBuffer( pxIOManager, pxBuffer ); if( FF_isERR( xError ) ) { break; } /* Write the first byte in the subsequent sector. */ pxBuffer = FF_GetBuffer( pxIOManager, ulFATSector + 1, FF_MODE_WRITE ); { if( pxBuffer == NULL ) { xError = ( FF_Error_t ) ( FF_ERR_DEVICE_DRIVER_FAILED | FF_PUTFATENTRY ); break; } FF_putChar( pxBuffer->pucBuffer, 0x0000, ucBytes[ 1 ] ); } xError = FF_ReleaseBuffer( pxIOManager, pxBuffer ); if( FF_isERR( xError ) ) { break; } } /* for ( xIndex = 0; xIndex < xNumFATs; xIndex++ ) */ } return xError; } #endif /* if ( ffconfigFAT12_SUPPORT != 0 ) */ /** * @private * @brief Writes a new Entry to the FAT Tables. * * @param pxIOManager IOMAN object. * @param ulCluster Cluster Number to be modified. * @param ulValue The value to store. **/ FF_Error_t FF_putFATEntry( FF_IOManager_t * pxIOManager, uint32_t ulCluster, uint32_t ulValue, FF_FATBuffers_t * pxFATBuffers ) { FF_Buffer_t * pxBuffer; uint32_t ulFATOffset; uint32_t ulFATSector = 0; uint32_t ulFATSectorEntry; uint32_t ulFATEntry; uint32_t ulLBAAdjust; uint32_t ulRelClusterEntry = 0; BaseType_t xIndex; FF_Error_t xError = FF_ERR_NONE; #if ( ffconfigWRITE_BOTH_FATS != 0 ) const BaseType_t xNumFATs = pxIOManager->xPartition.ucNumFATS; #else const BaseType_t xNumFATs = 1; #endif FF_Assert_Lock( pxIOManager, FF_FAT_LOCK ); /* Avoid corrupting the disk. */ if( ( ulCluster == 0ul ) || ( ulCluster >= pxIOManager->xPartition.ulNumClusters ) ) { /* find a more specific error code. */ xError = ( FF_Error_t ) ( FF_ERR_IOMAN_NOT_ENOUGH_FREE_SPACE | FF_PUTFATENTRY ); } else { if( pxIOManager->xPartition.ucType == FF_T_FAT32 ) { ulFATOffset = ulCluster * 4; } else if( pxIOManager->xPartition.ucType == FF_T_FAT16 ) { ulFATOffset = ulCluster * 2; } else /* pxIOManager->xPartition.ucType == FF_T_FAT12 */ { ulFATOffset = ulCluster + ( ulCluster / 2 ); } ulFATSector = pxIOManager->xPartition.ulFATBeginLBA + ( ulFATOffset / pxIOManager->xPartition.usBlkSize ); ulFATSectorEntry = ulFATOffset % pxIOManager->xPartition.usBlkSize; ulLBAAdjust = ulFATSectorEntry / ( ( uint32_t ) pxIOManager->usSectorSize ); ulRelClusterEntry = ulFATSectorEntry % pxIOManager->usSectorSize; ulFATSector = FF_getRealLBA( pxIOManager, ulFATSector ); ulFATSector += ulLBAAdjust; } #if ( ffconfigFAT12_SUPPORT != 0 ) if( ( pxIOManager->xPartition.ucType == FF_T_FAT12 ) && ( FF_isERR( xError ) == pdFALSE ) && ( ulRelClusterEntry == ( uint32_t ) ( ( pxIOManager->usSectorSize - 1 ) ) ) ) { /* The special case in which one FAT12 entries is divided over 2 sectors. * Treat this in a separate function. */ xError = prvPutFAT12Entry( pxIOManager, ulCluster, ulValue, pxFATBuffers, ulFATSector ); /* Return xError. */ } else #endif /* ffconfigFAT12_SUPPORT */ if( FF_isERR( xError ) == pdFALSE ) { /* Handle FAT16, FAT32, and FAT12 (in case the entry lies on a single sector). */ for( xIndex = 0; xIndex < xNumFATs; xIndex++, ulFATSector += pxIOManager->xPartition.ulSectorsPerFAT ) { pxBuffer = prvGetFromFATBuffers( pxIOManager, pxFATBuffers, xIndex, ulFATSector, &xError, FF_MODE_WRITE ); if( FF_isERR( xError ) ) { xError = FF_GETERROR( xError ) | FF_PUTFATENTRY; break; } if( pxIOManager->xPartition.ucType == FF_T_FAT32 ) { /* Clear the top 4 bits. */ ulValue &= 0x0fffffff; FF_putLong( pxBuffer->pucBuffer, ulRelClusterEntry, ulValue ); } else if( pxIOManager->xPartition.ucType == FF_T_FAT16 ) { FF_putShort( pxBuffer->pucBuffer, ulRelClusterEntry, ( uint16_t ) ulValue ); } else { ulFATEntry = ( uint32_t ) FF_getShort( pxBuffer->pucBuffer, ulRelClusterEntry ); if( ( ulCluster & 0x0001 ) != 0 ) { ulFATEntry &= 0x000F; ulValue = ( ulValue << 4 ); ulValue &= 0xFFF0; } else { ulFATEntry &= 0xF000; ulValue &= 0x0FFF; } FF_putShort( pxBuffer->pucBuffer, ulRelClusterEntry, ( uint16_t ) ( ulFATEntry | ulValue ) ); } if( ( xIndex < ffconfigBUF_STORE_COUNT ) && ( pxFATBuffers != NULL ) ) { /* Store it for later use. */ pxFATBuffers->pxBuffers[ xIndex ] = pxBuffer; pxFATBuffers->ucMode = FF_MODE_WRITE; } else { xError = FF_ReleaseBuffer( pxIOManager, pxBuffer ); if( FF_isERR( xError ) ) { break; } } } } /* FF_putFATEntry() returns just an error code, not an address. */ return xError; } /* FF_putFATEntry() */ /*-----------------------------------------------------------*/ /** * @private * @brief Finds a Free Cluster and returns its number. * * @param pxIOManager IOMAN Object. * * @return The number of the cluster found to be free. * @return 0 on error. **/ #if ( ffconfigFAT12_SUPPORT != 0 ) static uint32_t prvFindFreeClusterSimple( FF_IOManager_t * pxIOManager, FF_Error_t * pxError ) { FF_Error_t xError = FF_ERR_NONE; uint32_t ulCluster = 0; uint32_t ulFATEntry; FF_FATBuffers_t xFATBuffers; FF_InitFATBuffers( &xFATBuffers, FF_MODE_READ ); for( ulCluster = pxIOManager->xPartition.ulLastFreeCluster; ulCluster < pxIOManager->xPartition.ulNumClusters; ulCluster++ ) { ulFATEntry = FF_getFATEntry( pxIOManager, ulCluster, &xError, &xFATBuffers ); if( FF_isERR( xError ) ) { break; } if( ulFATEntry == 0 ) { pxIOManager->xPartition.ulLastFreeCluster = ulCluster; break; } } { FF_Error_t xTempError; xTempError = FF_ReleaseFATBuffers( pxIOManager, &xFATBuffers ); if( FF_isERR( xError ) == pdFALSE ) { xError = xTempError; } } if( ( FF_isERR( xError ) == pdFALSE ) && ( ulCluster == pxIOManager->xPartition.ulNumClusters ) ) { /* There is no free cluster any more. */ ulCluster = 0; xError = FF_FINDFREECLUSTER | FF_ERR_IOMAN_NOT_ENOUGH_FREE_SPACE; } *pxError = xError; return ulCluster; } #endif /* if ( ffconfigFAT12_SUPPORT != 0 ) */ /*-----------------------------------------------------------*/ uint32_t FF_FindFreeCluster( FF_IOManager_t * pxIOManager, FF_Error_t * pxError, BaseType_t xDoClaim ) { FF_Error_t xError = FF_ERR_NONE; FF_Buffer_t * pxBuffer = NULL; uint32_t x, ulCluster; uint32_t ulFATSectorEntry; uint32_t ulEntriesPerSector; uint32_t ulFATEntry = 1; const BaseType_t xEntrySize = ( pxIOManager->xPartition.ucType == FF_T_FAT32 ) ? 4 : 2; const uint32_t uNumClusters = pxIOManager->xPartition.ulNumClusters; BaseType_t xTakeLock = FF_Has_Lock( pxIOManager, FF_FAT_LOCK ) == pdFALSE; if( xTakeLock ) { FF_LockFAT( pxIOManager ); } ulCluster = pxIOManager->xPartition.ulLastFreeCluster; #if ( ffconfigFAT12_SUPPORT != 0 ) /* FAT12 tables are too small to optimise, and would make it very complicated! */ if( pxIOManager->xPartition.ucType == FF_T_FAT12 ) { ulCluster = prvFindFreeClusterSimple( pxIOManager, &xError ); } else #endif { #if ( ffconfigFSINFO_TRUSTED != 0 ) { /* If 'ffconfigFSINFO_TRUSTED', the contents of the field 'ulLastFreeCluster' is trusted. * Only ready it in case of FAT32 and only during the very first time, i.e. when * ulLastFreeCluster is still zero. */ if( ( pxIOManager->xPartition.ucType == FF_T_FAT32 ) && ( pxIOManager->xPartition.ulLastFreeCluster == 0ul ) ) { pxBuffer = FF_GetBuffer( pxIOManager, pxIOManager->xPartition.ulFSInfoLBA, FF_MODE_READ ); if( pxBuffer == NULL ) { xError = ( FF_Error_t ) ( FF_ERR_DEVICE_DRIVER_FAILED | FF_FINDFREECLUSTER ); } else { if( ( FF_getLong( pxBuffer->pucBuffer, 0 ) == 0x41615252 ) && ( FF_getLong( pxBuffer->pucBuffer, 484 ) == 0x61417272 ) ) { ulCluster = FF_getLong( pxBuffer->pucBuffer, 492 ); } xError = FF_ReleaseBuffer( pxIOManager, pxBuffer ); pxBuffer = NULL; } } } #endif /* if ( ffconfigFSINFO_TRUSTED != 0 ) */ if( FF_isERR( xError ) == pdFALSE ) { uint32_t ulFATSector; uint32_t ulFATOffset; ulEntriesPerSector = pxIOManager->usSectorSize / xEntrySize; ulFATOffset = ulCluster * xEntrySize; /* Start from a sector where the first free entry is expected, * and iterate through every FAT sector. */ for( ulFATSector = ( ulFATOffset / pxIOManager->xPartition.usBlkSize ); ulFATSector < pxIOManager->xPartition.ulSectorsPerFAT; ulFATSector++ ) { pxBuffer = FF_GetBuffer( pxIOManager, pxIOManager->xPartition.ulFATBeginLBA + ulFATSector, FF_MODE_READ ); if( pxBuffer == NULL ) { xError = ( FF_Error_t ) ( FF_ERR_DEVICE_DRIVER_FAILED | FF_FINDFREECLUSTER ); break; } for( x = ( ulCluster % ulEntriesPerSector ); x < ulEntriesPerSector; x++ ) { /* Double-check: don't use non-existing clusters */ if( ulCluster >= uNumClusters ) { xError = ( FF_Error_t ) ( FF_ERR_IOMAN_NOT_ENOUGH_FREE_SPACE | FF_FINDFREECLUSTER ); break; } ulFATSectorEntry = ulFATOffset % pxIOManager->xPartition.usBlkSize; if( pxIOManager->xPartition.ucType == FF_T_FAT32 ) { ulFATEntry = FF_getLong( pxBuffer->pucBuffer, ulFATSectorEntry ); /* Clear the top 4 bits. */ ulFATEntry &= 0x0fffffff; } else { ulFATEntry = ( uint32_t ) FF_getShort( pxBuffer->pucBuffer, ulFATSectorEntry ); } if( ulFATEntry == 0x00000000 ) { /* Break and return 'ulCluster' */ break; } ulFATOffset += xEntrySize; ulCluster++; } xError = FF_ReleaseBuffer( pxIOManager, pxBuffer ); pxBuffer = NULL; if( FF_isERR( xError ) ) { break; } if( ulFATEntry == 0x00000000 ) { /* And break from the outer loop. */ break; } } if( ( FF_isERR( xError ) == pdFALSE ) && ( ulFATSector == pxIOManager->xPartition.ulSectorsPerFAT ) ) { xError = ( FF_Error_t ) ( FF_ERR_IOMAN_NOT_ENOUGH_FREE_SPACE | FF_FINDFREECLUSTER ); } } /* if( FF_isERR( xError ) == pdFALSE ) */ } /* if( pxIOManager->xPartition.ucType != FF_T_FAT12 ) */ if( FF_isERR( xError ) ) { ulCluster = 0UL; } if( ( ulCluster != 0UL ) && ( xDoClaim != pdFALSE ) ) { FF_Error_t xTempError; /* Found a free cluster! */ pxIOManager->xPartition.ulLastFreeCluster = ulCluster + 1; xTempError = FF_putFATEntry( pxIOManager, ulCluster, 0xFFFFFFFF, NULL ); if( FF_isERR( xError ) == pdFALSE ) { xError = xTempError; } if( FF_isERR( xError ) ) { ulCluster = 0UL; } } if( xTakeLock ) { FF_UnlockFAT( pxIOManager ); } *pxError = xError; return ulCluster; } /* FF_FindFreeCluster */ /*-----------------------------------------------------------*/ /** * @private * @brief Creates a Cluster Chain * @return > 0 New created cluster * @return = 0 See pxError **/ uint32_t FF_CreateClusterChain( FF_IOManager_t * pxIOManager, FF_Error_t * pxError ) { uint32_t ulStartCluster; FF_Error_t xError = FF_ERR_NONE; FF_LockFAT( pxIOManager ); { ulStartCluster = FF_FindFreeCluster( pxIOManager, &xError, pdTRUE ); } FF_UnlockFAT( pxIOManager ); if( ulStartCluster != 0L ) { xError = FF_DecreaseFreeClusters( pxIOManager, 1 ); } *pxError = xError; return ulStartCluster; } /*-----------------------------------------------------------*/ uint32_t FF_GetChainLength( FF_IOManager_t * pxIOManager, uint32_t ulStartCluster, uint32_t * pulEndOfChain, FF_Error_t * pxError ) { uint32_t ulLength = 0; FF_FATBuffers_t xFATBuffers; FF_Error_t xError = FF_ERR_NONE; FF_InitFATBuffers( &xFATBuffers, FF_MODE_READ ); FF_LockFAT( pxIOManager ); { while( FF_isEndOfChain( pxIOManager, ulStartCluster ) == pdFALSE ) { ulStartCluster = FF_getFATEntry( pxIOManager, ulStartCluster, &xError, &xFATBuffers ); if( FF_isERR( xError ) ) { ulLength = 0; break; } ulLength++; } if( pulEndOfChain != NULL ) { /* _HT_ * ulStartCluster has just been tested as an end-of-chain token. * Not sure if the caller expects this. */ *pulEndOfChain = ulStartCluster; } xError = FF_ReleaseFATBuffers( pxIOManager, &xFATBuffers ); } FF_UnlockFAT( pxIOManager ); *pxError = xError; return ulLength; } /*-----------------------------------------------------------*/ /** * @private * @brief Free's Disk space by freeing unused links on Cluster Chains * * @param pxIOManager, IOMAN object. * @param ulStartCluster Cluster Number that starts the chain. * @param ulCount Number of Clusters from the end of the chain to unlink. * @param ulCount 0 Means Free the entire chain (delete file). * @param ulCount 1 Means mark the start cluster with EOF. * * @return 0 On Success. * @return -1 If the device driver failed to provide access. * **/ FF_Error_t FF_UnlinkClusterChain( FF_IOManager_t * pxIOManager, uint32_t ulStartCluster, BaseType_t xDoTruncate ) { uint32_t ulFATEntry; uint32_t ulCurrentCluster; uint32_t ulLength = 0; uint32_t ulLastFree = ulStartCluster; FF_Error_t xTempError; FF_Error_t xError = FF_ERR_NONE; FF_FATBuffers_t xFATBuffers; BaseType_t xTakeLock = FF_Has_Lock( pxIOManager, FF_FAT_LOCK ) == pdFALSE; if( xTakeLock ) { FF_LockFAT( pxIOManager ); } FF_InitFATBuffers( &xFATBuffers, FF_MODE_WRITE ); ulFATEntry = ulStartCluster; /* Free all clusters in the chain! */ ulCurrentCluster = ulStartCluster; ulFATEntry = ulCurrentCluster; do { /* Sector will now be fetched in write-mode. */ ulFATEntry = FF_getFATEntry( pxIOManager, ulFATEntry, &xError, &xFATBuffers ); if( FF_isERR( xError ) ) { break; } if( ( xDoTruncate != pdFALSE ) && ( ulCurrentCluster == ulStartCluster ) ) { xError = FF_putFATEntry( pxIOManager, ulCurrentCluster, 0xFFFFFFFF, &xFATBuffers ); } else { xError = FF_putFATEntry( pxIOManager, ulCurrentCluster, 0x00000000, &xFATBuffers ); ulLength++; } if( FF_isERR( xError ) ) { break; } if( ulLastFree > ulCurrentCluster ) { ulLastFree = ulCurrentCluster; } ulCurrentCluster = ulFATEntry; } while( FF_isEndOfChain( pxIOManager, ulFATEntry ) == pdFALSE ); if( FF_isERR( xError ) == pdFALSE ) { if( pxIOManager->xPartition.ulLastFreeCluster > ulLastFree ) { pxIOManager->xPartition.ulLastFreeCluster = ulLastFree; } } xTempError = FF_ReleaseFATBuffers( pxIOManager, &xFATBuffers ); if( FF_isERR( xError ) == pdFALSE ) { xError = xTempError; } if( xTakeLock ) { FF_UnlockFAT( pxIOManager ); } if( ulLength != 0 ) { xTempError = FF_IncreaseFreeClusters( pxIOManager, ulLength ); if( FF_isERR( xError ) == pdFALSE ) { xError = xTempError; } } return xError; } /*-----------------------------------------------------------*/ #if ( ffconfigFAT12_SUPPORT != 0 ) static uint32_t prvCountFreeClustersSimple( FF_IOManager_t * pxIOManager, FF_Error_t * pxError ) { FF_Error_t xError = FF_ERR_NONE; uint32_t ulIndex; uint32_t ulFATEntry; uint32_t ulFreeClusters = 0; const uint32_t xTotalClusters = pxIOManager->xPartition.ulDataSectors / pxIOManager->xPartition.ulSectorsPerCluster; for( ulIndex = 0; ulIndex < xTotalClusters; ulIndex++ ) { ulFATEntry = FF_getFATEntry( pxIOManager, ulIndex, &xError, NULL ); if( FF_isERR( xError ) ) { break; } if( ulFATEntry == 0UL ) { ulFreeClusters++; } } *pxError = xError; return ulFreeClusters; } #endif /* if ( ffconfigFAT12_SUPPORT != 0 ) */ /*-----------------------------------------------------------*/ uint32_t FF_CountFreeClusters( FF_IOManager_t * pxIOManager, FF_Error_t * pxError ) { FF_Error_t xError = FF_ERR_NONE; FF_Buffer_t * pxBuffer; uint32_t ulIndex, x; uint32_t ulFATEntry; uint32_t ulEntriesPerSector; uint32_t ulFreeClusters = 0; uint32_t ClusterNum = 0; BaseType_t xInfoKnown = pdFALSE; BaseType_t xTakeLock = FF_Has_Lock( pxIOManager, FF_FAT_LOCK ) == pdFALSE; if( xTakeLock ) { FF_LockFAT( pxIOManager ); } #if ( ffconfigFAT12_SUPPORT != 0 ) /* FAT12 tables are too small to optimise, and would make it very complicated! */ if( pxIOManager->xPartition.ucType == FF_T_FAT12 ) { ulFreeClusters = prvCountFreeClustersSimple( pxIOManager, &xError ); } else #endif { /* For FAT16 and FAT32 */ #if ( ffconfigFSINFO_TRUSTED != 0 ) { /* If 'ffconfigFSINFO_TRUSTED', the contents of the field 'ulFreeClusterCount' is trusted. */ if( pxIOManager->xPartition.ucType == FF_T_FAT32 ) { pxBuffer = FF_GetBuffer( pxIOManager, pxIOManager->xPartition.ulFSInfoLBA, FF_MODE_READ ); if( pxBuffer == NULL ) { xError = ( FF_Error_t ) ( FF_ERR_DEVICE_DRIVER_FAILED | FF_COUNTFREECLUSTERS ); } else { if( ( FF_getLong( pxBuffer->pucBuffer, 0 ) == 0x41615252 ) && ( FF_getLong( pxBuffer->pucBuffer, 484 ) == 0x61417272 ) ) { ulFreeClusters = FF_getLong( pxBuffer->pucBuffer, 488 ); if( ulFreeClusters != ~0ul ) { xInfoKnown = pdTRUE; } else { ulFreeClusters = 0ul; } } xError = FF_ReleaseBuffer( pxIOManager, pxBuffer ); pxBuffer = NULL; if( xInfoKnown != pdFALSE ) { pxIOManager->xPartition.ulFreeClusterCount = ulFreeClusters; } } } } #endif /* if ( ffconfigFSINFO_TRUSTED != 0 ) */ if( ( xInfoKnown == pdFALSE ) && ( pxIOManager->xPartition.usBlkSize != 0 ) ) { if( pxIOManager->xPartition.ucType == FF_T_FAT32 ) { ulEntriesPerSector = pxIOManager->usSectorSize / 4; } else { ulEntriesPerSector = pxIOManager->usSectorSize / 2; } for( ulIndex = 0; ulIndex < pxIOManager->xPartition.ulSectorsPerFAT; ulIndex++ ) { pxBuffer = FF_GetBuffer( pxIOManager, pxIOManager->xPartition.ulFATBeginLBA + ulIndex, FF_MODE_READ ); if( pxBuffer == NULL ) { xError = ( FF_Error_t ) ( FF_ERR_DEVICE_DRIVER_FAILED | FF_COUNTFREECLUSTERS ); break; } #if USE_SOFT_WDT { /* _HT_ : FF_CountFreeClusters was a little too busy, have it call the WDT and sleep */ clearWDT(); if( ( ( ulIndex + 1 ) % 32 ) == 0 ) { FF_Sleep( 1 ); } } #endif for( x = 0; x < ulEntriesPerSector; x++ ) { if( pxIOManager->xPartition.ucType == FF_T_FAT32 ) { /* Clearing the top 4 bits. */ ulFATEntry = FF_getLong( pxBuffer->pucBuffer, x * 4 ) & 0x0fffffff; } else { ulFATEntry = ( uint32_t ) FF_getShort( pxBuffer->pucBuffer, x * 2 ); } if( ulFATEntry == 0ul ) { ulFreeClusters++; } /* FAT table might not be cluster aligned. */ if( ClusterNum > pxIOManager->xPartition.ulNumClusters ) { /* Stop counting if that's the case. */ break; } ClusterNum++; } xError = FF_ReleaseBuffer( pxIOManager, pxBuffer ); pxBuffer = NULL; if( FF_isERR( xError ) ) { break; } if( ClusterNum > pxIOManager->xPartition.ulNumClusters ) { /* Break out of 2nd loop too ^^ */ break; } /* ulFreeClusters is -2 because the first 2 fat entries in the table are reserved. */ if( ulFreeClusters > pxIOManager->xPartition.ulNumClusters ) { ulFreeClusters = pxIOManager->xPartition.ulNumClusters; } } /* for( ulIndex = 0; ulIndex < pxIOManager->xPartition.ulSectorsPerFAT; ulIndex++ ) */ } } if( xTakeLock ) { FF_UnlockFAT( pxIOManager ); } if( FF_isERR( xError ) ) { ulFreeClusters = 0; } *pxError = xError; return ulFreeClusters; } /*-----------------------------------------------------------*/ #if ( ffconfig64_NUM_SUPPORT != 0 ) uint64_t FF_GetFreeSize( FF_IOManager_t * pxIOManager, FF_Error_t * pxError ) { FF_Error_t xError = FF_ERR_NONE; uint32_t ulFreeClusters; uint64_t ulFreeSize = 0; if( pxIOManager != NULL ) { if( pxIOManager->xPartition.ulFreeClusterCount == 0ul ) { FF_LockFAT( pxIOManager ); { pxIOManager->xPartition.ulFreeClusterCount = FF_CountFreeClusters( pxIOManager, &xError ); } FF_UnlockFAT( pxIOManager ); } ulFreeClusters = pxIOManager->xPartition.ulFreeClusterCount; ulFreeSize = ( uint64_t ) ( ( uint64_t ) ulFreeClusters * ( uint64_t ) ( ( uint64_t ) pxIOManager->xPartition.ulSectorsPerCluster * ( uint64_t ) pxIOManager->xPartition.usBlkSize ) ); } if( pxError != NULL ) { *pxError = xError; } return ulFreeSize; } #else /* if ( ffconfig64_NUM_SUPPORT != 0 ) */ uint32_t FF_GetFreeSize( FF_IOManager_t * pxIOManager, FF_Error_t * pxError ) { FF_Error_t xError = FF_ERR_NONE; uint32_t ulFreeClusters; uint32_t ulFreeSize = 0; if( pxIOManager != NULL ) { if( pxIOManager->xPartition.ulFreeClusterCount == 0ul ) { FF_LockFAT( pxIOManager ); { pxIOManager->xPartition.ulFreeClusterCount = FF_CountFreeClusters( pxIOManager, &xError ); } FF_UnlockFAT( pxIOManager ); } ulFreeClusters = pxIOManager->xPartition.ulFreeClusterCount; ulFreeSize = ( uint32_t ) ( ( uint32_t ) ulFreeClusters * ( uint32_t ) ( ( uint32_t ) pxIOManager->xPartition.ulSectorsPerCluster * ( uint32_t ) pxIOManager->xPartition.usBlkSize ) ); } if( pxError != NULL ) { *pxError = xError; } return ulFreeSize; } #endif /* ffconfig64_NUM_SUPPORT */ /*-----------------------------------------------------------*/