blob: 258c2a40b10983320cf7547cf53644f8e1fea0de [file] [log] [blame]
/*
* FreeRTOS+FAT build 191128 - Note: FreeRTOS+FAT is still in the lab!
* Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Authors include James Walmsley, Hein Tibosch and Richard Barry
*
* 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
*
*/
/**
* @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 <string.h>
#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
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
/**
* @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
/*-----------------------------------------------------------*/
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( 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
/*-----------------------------------------------------------*/
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( ( 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
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 */
/*-----------------------------------------------------------*/