You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1547 lines
45 KiB
C
1547 lines
45 KiB
C
5 years ago
|
/*
|
||
|
* 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 */
|
||
|
/*-----------------------------------------------------------*/
|
||
|
|