| /* | |
| * 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 */ | |
| /*-----------------------------------------------------------*/ | |