/* * 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_ioman.c * @ingroup IOMAN * * @defgroup IOMAN I/O Manager * @brief Handles IO buffers for FreeRTOS+FAT safely. * * Provides a simple static interface to the rest of FreeRTOS+FAT to manage * buffers. It also defines the public interfaces for Creating and * Destroying a FreeRTOS+FAT IO object. **/ #include #include #include "ff_headers.h" #define FAT16_SECTOR_COUNT_4085 4085 #define FAT32_SECTOR_COUNT_65525 65525 /* 65536 clusters */ /* Some values and offsets describing the special sector FS INFO: */ #define FS_INFO_SIGNATURE1_0x41615252 0x41615252UL #define FS_INFO_SIGNATURE2_0x61417272 0x61417272UL #define FS_INFO_OFFSET_SIGNATURE1_000 0 #define FS_INFO_OFFSET_SIGNATURE2_484 484 #define FS_INFO_OFFSET_FREE_COUNT_488 488 #define FS_INFO_OFFSET_FREE_CLUSTER_492 492 /* Inspect the PBR (Partition Boot Record) to determine the type of FAT */ static FF_Error_t prvDetermineFatType( FF_IOManager_t *pxIOManager ); /* Check if a given ID introduces an extended partition. */ static BaseType_t prvIsExtendedPartition( uint8_t ucPartitionID ); /* Return pdTRUE if the media byte in an MBR is valid. */ static BaseType_t prvIsValidMedia( uint8_t media ); /* Read the MBR to see what extended partitions have been defined. Definitions of extended partitions may be chained. Walk down the chain to find all extended partitions. */ static FF_Error_t FF_ParseExtended( FF_IOManager_t *pxIOManager, uint32_t ulFirstSector, uint32_t ulFirstSize, FF_SPartFound_t *pPartsFound ); static FF_Error_t FF_GetEfiPartitionEntry( FF_IOManager_t *pxIOManager, uint32_t ulPartitionNumber ); static BaseType_t prvHasActiveHandles( FF_IOManager_t *pxIOManager ); /** * @public * @brief Creates an FF_IOManager_t object, to initialise FreeRTOS+FAT * * @param pucCacheMem Pointer to a buffer for the cache. (NULL if ok to Malloc). * @param ulCacheSize The size of the provided buffer, or size of the cache to be created. * (Must be at least 2 * ulSectorSize). Always a multiple of ulSectorSize. * @param usSectorSize The block size of devices to be attached. If in doubt use 512. * @param pError Pointer to a signed byte for error checking. Can be NULL if not required. * To be checked when a NULL pointer is returned. * * @Return Returns a pointer to an FF_IOManager_t type object. NULL on xError, check the contents of * @Return pError **/ FF_IOManager_t *FF_CreateIOManger( FF_CreationParameters_t *pxParameters, FF_Error_t *pError ) { FF_IOManager_t *pxIOManager = NULL; FF_Error_t xError; uint32_t ulCacheSize = pxParameters->ulMemorySize; uint32_t usSectorSize = pxParameters->ulSectorSize; /* Normally: ulSectorSize = 512 ulCacheSize = N x ulSectorSize. */ if( ( ( usSectorSize % 512 ) != 0 ) || ( usSectorSize == 0 ) ) { /* ulSectorSize Size not a multiple of 512 or it is zero*/ xError = FF_ERR_IOMAN_BAD_BLKSIZE | FF_CREATEIOMAN; } else if( ( ( ulCacheSize % ( uint32_t ) usSectorSize ) != 0 ) || ( ulCacheSize == 0 ) || ( ulCacheSize == ( uint32_t ) usSectorSize ) ) { /* The size of the caching memory (ulCacheSize) must now be atleast 2 * ulSectorSize (or a deadlock will occur). */ xError = FF_ERR_IOMAN_BAD_MEMSIZE | FF_CREATEIOMAN; } else { pxIOManager = ( FF_IOManager_t * ) ffconfigMALLOC( sizeof( FF_IOManager_t ) ); /* Ensure malloc() succeeded. */ if( pxIOManager != NULL ) { /* Use memset() to clear every single bit. */ memset( pxIOManager, '\0', sizeof( FF_IOManager_t ) ); if( FF_CreateEvents( pxIOManager ) != pdFALSE ) { xError = FF_ERR_NONE; } else { /* xEventGroupCreate() probably failed. */ xError = FF_ERR_NOT_ENOUGH_MEMORY | FF_CREATEIOMAN; } } else { /* ffconfigMALLOC() failed. */ xError = FF_ERR_NOT_ENOUGH_MEMORY | FF_CREATEIOMAN; } } if( FF_isERR( xError ) == pdFALSE ) { /* pxIOManager is created, FF_CreateEvents() succeeded. */ if( pxParameters->pucCacheMemory != NULL ) { /* The caller has provided a piece of memory, use it. */ pxIOManager->pucCacheMem = pxParameters->pucCacheMemory; } else { /* No cache buffer provided, call malloc(). */ pxIOManager->pucCacheMem = ( uint8_t * ) ffconfigMALLOC( ulCacheSize ); if( pxIOManager->pucCacheMem != NULL ) { /* Indicate that malloc() was used for pucCacheMem. */ pxIOManager->ucFlags |= FF_IOMAN_ALLOC_BUFFERS; } else { xError = FF_ERR_NOT_ENOUGH_MEMORY | FF_CREATEIOMAN; } } if( pxIOManager->pucCacheMem != NULL ) { memset( pxIOManager->pucCacheMem, '\0', ulCacheSize ); } } if( FF_isERR( xError ) == pdFALSE ) { pxIOManager->usSectorSize = usSectorSize; pxIOManager->usCacheSize = ( uint16_t ) ( ulCacheSize / ( uint32_t ) usSectorSize ); /* Malloc() memory for buffer objects. FreeRTOS+FAT never refers to a buffer directly but uses buffer objects instead. Allows for thread safety. */ pxIOManager->pxBuffers = ( FF_Buffer_t * ) ffconfigMALLOC( sizeof( FF_Buffer_t ) * pxIOManager->usCacheSize ); if( pxIOManager->pxBuffers != NULL ) { /* From now on a call to FF_IOMAN_InitBufferDescriptors will clear pxBuffers. */ pxIOManager->ucFlags |= FF_IOMAN_ALLOC_BUFDESCR; FF_IOMAN_InitBufferDescriptors( pxIOManager ); /* Finally store the semaphore for Buffer Description modifications. */ pxIOManager->pvSemaphore = pxParameters->pvSemaphore; if( pxParameters->xBlockDeviceIsReentrant != pdFALSE ) { pxIOManager->ucFlags |= FF_IOMAN_BLOCK_DEVICE_IS_REENTRANT; } pxIOManager->xBlkDevice.fnpReadBlocks = pxParameters->fnReadBlocks; pxIOManager->xBlkDevice.fnpWriteBlocks = pxParameters->fnWriteBlocks; pxIOManager->xBlkDevice.pxDisk = pxParameters->pxDisk; } else { xError = FF_ERR_NOT_ENOUGH_MEMORY | FF_CREATEIOMAN; } } if( FF_isERR( xError ) ) { if( pxIOManager != NULL ) { FF_DeleteIOManager( pxIOManager ); pxIOManager = NULL; } } if( pError != NULL ) { *pError = xError; } return pxIOManager; } /* FF_CreateIOManger() */ /*-----------------------------------------------------------*/ /** * @public * @brief Destroys an FF_IOManager_t object, and frees all assigned memory. * * @param pxIOManager Pointer to an FF_IOManager_t object, as returned from FF_CreateIOManger. * * @Return FF_ERR_NONE on sucess, or a documented error code on failure. (FF_ERR_NULL_POINTER) * **/ FF_Error_t FF_DeleteIOManager( FF_IOManager_t *pxIOManager ) { FF_Error_t xError; /* Ensure no NULL pointer was provided. */ if( pxIOManager == NULL ) { xError = FF_ERR_NULL_POINTER | FF_DESTROYIOMAN; } else { xError = FF_ERR_NONE; /* Ensure pxBuffers pointer was allocated. */ if( ( pxIOManager->ucFlags & FF_IOMAN_ALLOC_BUFDESCR ) != 0 ) { ffconfigFREE( pxIOManager->pxBuffers ); } /* Ensure pucCacheMem pointer was allocated. */ if( ( pxIOManager->ucFlags & FF_IOMAN_ALLOC_BUFFERS ) != 0 ) { ffconfigFREE( pxIOManager->pucCacheMem ); } /* Delete the event group object within the IO manager before deleting the manager. */ FF_DeleteEvents( pxIOManager ); /* Finally free the FF_IOManager_t object. */ ffconfigFREE( pxIOManager ); } return xError; } /* FF_DeleteIOManager() */ /*-----------------------------------------------------------*/ /** * @private * @brief Initialises Buffer Descriptions as part of the FF_IOManager_t object initialisation. * * @param pxIOManager IOMAN Object. * **/ void FF_IOMAN_InitBufferDescriptors( FF_IOManager_t *pxIOManager ) { uint8_t *pucBuffer = pxIOManager->pucCacheMem; FF_Buffer_t *pxBuffer = pxIOManager->pxBuffers; FF_Buffer_t *pxLastBuffer = pxBuffer + pxIOManager->usCacheSize; /* Clear the contents of the buffer descriptors. */ memset( ( void * ) pxBuffer, '\0', sizeof( FF_Buffer_t ) * pxIOManager->usCacheSize ); while( pxBuffer < pxLastBuffer ) { pxBuffer->pucBuffer = pucBuffer; pxBuffer++; pucBuffer += pxIOManager->usSectorSize; } } /* FF_IOMAN_InitBufferDescriptors() */ /*-----------------------------------------------------------*/ /** * @private * @brief Flushes all Write cache buffers with no active Handles. * * @param pxIOManager IOMAN Object. * * @Return FF_ERR_NONE on Success. **/ FF_Error_t FF_FlushCache( FF_IOManager_t *pxIOManager ) { BaseType_t xIndex, xIndex2; FF_Error_t xError; if( pxIOManager == NULL ) { xError = FF_ERR_NULL_POINTER | FF_FLUSHCACHE; } else { xError = FF_ERR_NONE; FF_PendSemaphore( pxIOManager->pvSemaphore ); { for( xIndex = 0; xIndex < pxIOManager->usCacheSize; xIndex++ ) { /* If a buffers has no users and if it has been modified... */ if( ( pxIOManager->pxBuffers[ xIndex ].usNumHandles == 0 ) && ( pxIOManager->pxBuffers[ xIndex ].bModified == pdTRUE ) ) { /* The buffer may be flushed to disk. */ FF_BlockWrite( pxIOManager, pxIOManager->pxBuffers[ xIndex ].ulSector, 1, pxIOManager->pxBuffers[ xIndex ].pucBuffer, pdTRUE ); /* Buffer has now been flushed, mark it as a read buffer and unmodified. */ pxIOManager->pxBuffers[ xIndex ].ucMode = FF_MODE_READ; pxIOManager->pxBuffers[ xIndex ].bModified = pdFALSE; /* Search for other buffers that used this sector, and mark them as modified So that further requests will result in the new sector being fetched. */ for( xIndex2 = 0; xIndex2 < pxIOManager->usCacheSize; xIndex2++ ) { if( ( xIndex != xIndex2 ) && ( pxIOManager->pxBuffers[ xIndex2 ].ulSector == pxIOManager->pxBuffers[ xIndex ].ulSector ) && ( pxIOManager->pxBuffers[ xIndex2 ].ucMode == FF_MODE_READ ) ) { pxIOManager->pxBuffers[ xIndex2 ].bModified = pdTRUE; } } } } } if( ( pxIOManager->xBlkDevice.pxDisk != NULL ) && ( pxIOManager->xBlkDevice.pxDisk->fnFlushApplicationHook != NULL ) ) { /* Let the low-level driver also flush data. See comments in ff_ioman.h. */ pxIOManager->xBlkDevice.pxDisk->fnFlushApplicationHook( pxIOManager->xBlkDevice.pxDisk ); } FF_ReleaseSemaphore( pxIOManager->pvSemaphore ); } return xError; } /* FF_FlushCache() */ /*-----------------------------------------------------------*/ /* A new version of FF_GetBuffer() with a simple mechanism for timeout */ #define FF_GETBUFFER_SLEEP_TIME_MS 10 #define FF_GETBUFFER_WAIT_TIME_MS ( 20000 / FF_GETBUFFER_SLEEP_TIME_MS ) FF_Buffer_t *FF_GetBuffer( FF_IOManager_t *pxIOManager, uint32_t ulSector, uint8_t ucMode ) { FF_Buffer_t *pxBuffer; /* Least Recently Used Buffer */ FF_Buffer_t *pxRLUBuffer; FF_Buffer_t *pxMatchingBuffer = NULL; int32_t lRetVal; BaseType_t xLoopCount = FF_GETBUFFER_WAIT_TIME_MS; const FF_Buffer_t *pxLastBuffer = &( pxIOManager->pxBuffers[ pxIOManager->usCacheSize ] ); /* 'pxIOManager->usCacheSize' is bigger than zero and it is a multiple of ulSectorSize. */ while( pxMatchingBuffer == NULL ) { xLoopCount--; if( xLoopCount == 0 ) { break; } FF_PendSemaphore( pxIOManager->pvSemaphore ); for( pxBuffer = pxIOManager->pxBuffers; pxBuffer < pxLastBuffer; pxBuffer++ ) { if( ( pxBuffer->ulSector == ulSector ) && ( pxBuffer->bValid ) ) { pxMatchingBuffer = pxBuffer; /* Don't look further if you found a perfect match. */ break; } } if( pxMatchingBuffer != NULL ) { /* A Match was found process! */ if( ( ucMode == FF_MODE_READ ) && ( pxMatchingBuffer->ucMode == FF_MODE_READ ) ) { pxMatchingBuffer->usNumHandles += 1; pxMatchingBuffer->usPersistance += 1; break; } if( pxMatchingBuffer->usNumHandles == 0 ) { pxMatchingBuffer->ucMode = ( ucMode & FF_MODE_RD_WR ); if( ( ucMode & FF_MODE_WRITE ) != 0 ) { /* This buffer has no attached handles. */ pxMatchingBuffer->bModified = pdTRUE; } pxMatchingBuffer->usNumHandles = 1; pxMatchingBuffer->usPersistance += 1; break; } pxMatchingBuffer = NULL; /* Sector is already in use, keep yielding until its available! */ } else { /* There is no valid buffer now for the desired sector. Find a free buffer and use it for that sector. */ pxRLUBuffer = NULL; for( pxBuffer = pxIOManager->pxBuffers; pxBuffer < pxLastBuffer; pxBuffer++ ) { if( pxBuffer->usNumHandles != 0 ) { continue; /* Occupied */ } pxBuffer->ulLRU += 1; if( ( pxRLUBuffer == NULL ) || ( pxBuffer->ulLRU > pxRLUBuffer->ulLRU ) || ( ( pxBuffer->ulLRU == pxRLUBuffer->ulLRU ) && ( pxBuffer->usPersistance > pxRLUBuffer->usPersistance ) ) ) { pxRLUBuffer = pxBuffer; } } /* A free buffer with the highest value of 'ulLRU' was found: */ if( pxRLUBuffer != NULL ) { /* Process the suitable candidate. */ if( pxRLUBuffer->bModified == pdTRUE ) { /* Along with the pdTRUE parameter to indicate semaphore has been claimed already. */ lRetVal = FF_BlockWrite( pxIOManager, pxRLUBuffer->ulSector, 1, pxRLUBuffer->pucBuffer, pdTRUE ); if( lRetVal < 0 ) { /* NULL will be returned because 'pxMatchingBuffer' is still NULL. */ break; } } if( ucMode == FF_MODE_WR_ONLY ) { memset( pxRLUBuffer->pucBuffer, '\0', pxIOManager->usSectorSize ); } else { lRetVal = FF_BlockRead( pxIOManager, ulSector, 1, pxRLUBuffer->pucBuffer, pdTRUE ); if( lRetVal < 0 ) { /* 'pxMatchingBuffer' is NULL. */ break; } } pxRLUBuffer->ucMode = ( ucMode & FF_MODE_RD_WR ); pxRLUBuffer->usPersistance = 1; pxRLUBuffer->ulLRU = 0; pxRLUBuffer->usNumHandles = 1; pxRLUBuffer->ulSector = ulSector; pxRLUBuffer->bModified = ( ucMode & FF_MODE_WRITE ) != 0; pxRLUBuffer->bValid = pdTRUE; pxMatchingBuffer = pxRLUBuffer; break; } /* if( pxRLUBuffer != NULL ) */ } /* else ( pxMatchingBuffer == NULL ) */ FF_ReleaseSemaphore( pxIOManager->pvSemaphore ); /* Better to go asleep to give low-priority task a chance to release buffer(s). */ FF_BufferWait( pxIOManager, FF_GETBUFFER_SLEEP_TIME_MS ); } /* while( pxMatchingBuffer == NULL ) */ if( xLoopCount > 0 ) { /* If xLoopCount is 0 here, the semaphore was not taken. */ FF_ReleaseSemaphore( pxIOManager->pvSemaphore ); } if( pxMatchingBuffer == NULL ) { FF_PRINTF( "FF_GetBuffer[0x%X]: failed mode 0x%X\n", ( unsigned )ulSector, ( unsigned )ucMode ); } return pxMatchingBuffer; /* Return the Matched Buffer! */ } /* FF_GetBuffer() */ /*-----------------------------------------------------------*/ /** * @private * @brief Releases a buffer resource. * * @param pxIOManager Pointer to an FF_IOManager_t object. * @param pxBuffer Pointer to an FF_Buffer_t object. * **/ FF_Error_t FF_ReleaseBuffer( FF_IOManager_t *pxIOManager, FF_Buffer_t *pxBuffer ) { FF_Error_t xError = FF_ERR_NONE; /* Protect description changes with a semaphore. */ FF_PendSemaphore( pxIOManager->pvSemaphore ); { #if( ffconfigCACHE_WRITE_THROUGH != 0 ) if( pxBuffer->bModified == pdTRUE ) { xError = FF_BlockWrite( pxIOManager, pxBuffer->ulSector, 1, pxBuffer->pucBuffer, pdTRUE ); if( FF_isERR( xError ) == pdFALSE ) { /* Ensure if an error occurs its still possible to write the block again. */ pxBuffer->bModified = pdFALSE; } } #endif configASSERT( pxBuffer->usNumHandles != 0 ); if( pxBuffer->usNumHandles != 0 ) { pxBuffer->usNumHandles--; } else { /*printf ("FF_ReleaseBuffer: buffer not claimed\n"); */ } } FF_ReleaseSemaphore( pxIOManager->pvSemaphore ); /* Notify tasks which may be waiting in FF_GetBuffer() */ FF_BufferProceed( pxIOManager ); return xError; } /* FF_ReleaseBuffer() */ /*-----------------------------------------------------------*/ /* New Interface for FreeRTOS+FAT to read blocks. */ int32_t FF_BlockRead( FF_IOManager_t *pxIOManager, uint32_t ulSectorLBA, uint32_t ulNumSectors, void *pxBuffer, BaseType_t xSemLocked ) { int32_t slRetVal = 0; if( pxIOManager->xPartition.ulTotalSectors != 0ul ) { /* At some point while formatting a partition, ulTotalSectors might be unknown. In that case this test will be skipped. */ if( ( ulSectorLBA + ulNumSectors ) > ( pxIOManager->xPartition.ulTotalSectors + pxIOManager->xPartition.ulBeginLBA ) ) { slRetVal = FF_ERR_IOMAN_OUT_OF_BOUNDS_READ | FF_BLOCKREAD; } } if( ( slRetVal == 0ul ) && ( pxIOManager->xBlkDevice.fnpReadBlocks != NULL ) ) { do { /* Make sure we don't execute a NULL. */ if( ( xSemLocked == pdFALSE ) && ( ( pxIOManager->ucFlags & FF_IOMAN_BLOCK_DEVICE_IS_REENTRANT ) == pdFALSE ) ) { FF_PendSemaphore( pxIOManager->pvSemaphore ); } slRetVal = pxIOManager->xBlkDevice.fnpReadBlocks( pxBuffer, ulSectorLBA, ulNumSectors, pxIOManager->xBlkDevice.pxDisk ); if( ( xSemLocked == pdFALSE ) && ( ( pxIOManager->ucFlags & FF_IOMAN_BLOCK_DEVICE_IS_REENTRANT ) == pdFALSE ) ) { FF_ReleaseSemaphore( pxIOManager->pvSemaphore ); } if( FF_GETERROR( slRetVal ) != FF_ERR_DRIVER_BUSY ) { break; } FF_Sleep( ffconfigDRIVER_BUSY_SLEEP_MS ); } while( pdTRUE ); } return slRetVal; } /* FF_BlockRead() */ /*-----------------------------------------------------------*/ int32_t FF_BlockWrite( FF_IOManager_t *pxIOManager, uint32_t ulSectorLBA, uint32_t ulNumSectors, void *pxBuffer, BaseType_t xSemLocked ) { int32_t slRetVal = 0; if( pxIOManager->xPartition.ulTotalSectors != 0 ) { /* At some point while formatting a partition, ulTotalSectors might be unknown. In that case this test will be skipped. */ if( ( ulSectorLBA + ulNumSectors ) > ( pxIOManager->xPartition.ulTotalSectors + pxIOManager->xPartition.ulBeginLBA ) ) { slRetVal = FF_ERR_IOMAN_OUT_OF_BOUNDS_WRITE | FF_BLOCKWRITE; } } if( ( slRetVal == 0ul ) && ( pxIOManager->xBlkDevice.fnpWriteBlocks != NULL ) ) { do { /* Make sure we don't execute a NULL. */ if( ( xSemLocked == pdFALSE ) && ( ( pxIOManager->ucFlags & FF_IOMAN_BLOCK_DEVICE_IS_REENTRANT ) == pdFALSE ) ) { FF_PendSemaphore( pxIOManager->pvSemaphore ); } slRetVal = pxIOManager->xBlkDevice.fnpWriteBlocks( pxBuffer, ulSectorLBA, ulNumSectors, pxIOManager->xBlkDevice.pxDisk ); if( ( xSemLocked == pdFALSE ) && ( ( pxIOManager->ucFlags & FF_IOMAN_BLOCK_DEVICE_IS_REENTRANT ) == pdFALSE ) ) { FF_ReleaseSemaphore( pxIOManager->pvSemaphore ); } if( FF_GETERROR( slRetVal ) != FF_ERR_DRIVER_BUSY ) { break; } FF_Sleep( ffconfigDRIVER_BUSY_SLEEP_MS ); } while( pdTRUE ); } return slRetVal; } /* FF_BlockWrite() */ /*-----------------------------------------------------------*/ /* * This global variable is a kind of expert option: * It may be set to one of these values: FF_T_FAT[12,16,32] * just to force the driver to assume a certain FAT type. */ uint8_t ucAssumeFATType; /* The history of FAT types: * The Microsoft documents says that the actual type: FAT-12, FAT-16 and FAT-32 * of a partition can be found by looking at the total number of data clusters: * * if( clusters < 4085 ) * Assume FAT-12 * else if( clusters < 65525 ) * Assume FAT-16 * else * Assume FAT-32 * * In practice however, this does not always seem to be a correct assumption. * * The first 12 or 16 bits in the FAT table may also help to determine the * correct FAT-type: * * FAT-12: ( firstWord & 0x3FF ) == 0x3F8 ) * FAT-16: ( firstWord == 0xFFF8 ) */ static FF_Error_t prvDetermineFatType( FF_IOManager_t *pxIOManager ) { FF_Partition_t *pxPartition; FF_Buffer_t *pxBuffer; uint32_t ulFirstWord = 0ul; FF_Error_t xError = FF_ERR_NONE; pxPartition = &( pxIOManager->xPartition ); if( ucAssumeFATType != 0 ) { switch( ucAssumeFATType ) { case FF_T_FAT12: case FF_T_FAT16: case FF_T_FAT32: pxPartition->ucType = ucAssumeFATType; break; default: /* An invalid value will be ignored, and the FAT type is determined dynamically. */ ucAssumeFATType = 0; break; } xError = FF_ERR_NONE; } /* Test again, the value may have become zero now: */ if( ucAssumeFATType == 0 ) { pxBuffer = FF_GetBuffer( pxIOManager, pxIOManager->xPartition.ulFATBeginLBA, FF_MODE_READ ); if( pxBuffer == NULL ) { xError = FF_ERR_DEVICE_DRIVER_FAILED | FF_DETERMINEFATTYPE; } else { ulFirstWord = ( uint32_t ) FF_getShort( pxBuffer->pucBuffer, 0x0000 ); xError = FF_ReleaseBuffer( pxIOManager, pxBuffer ); } } if( ( ucAssumeFATType == 0 ) && ( FF_isERR( xError ) == pdFALSE ) ) { #if( ffconfigFAT12_SUPPORT != 0 ) if( pxPartition->ulNumClusters < FAT16_SECTOR_COUNT_4085 ) { /* FAT12 */ pxPartition->ucType = FF_T_FAT12; #if( ffconfigFAT_CHECK != 0 ) if( ( ulFirstWord & 0x3FF ) != 0x3F8 ) { xError = FF_ERR_IOMAN_NOT_FAT_FORMATTED | FF_DETERMINEFATTYPE; } else #endif /* ffconfigFAT_CHECK */ { xError = FF_ERR_NONE; } } else #endif /* ffconfigFAT12_SUPPORT */ if( pxPartition->ulNumClusters < FAT32_SECTOR_COUNT_65525 ) { /* FAT 16 */ pxPartition->ucType = FF_T_FAT16; #if( ffconfigFAT_CHECK != 0 ) { if( ulFirstWord == 0xFFF8 ) { xError = FF_ERR_NONE; } else { if( ( ulFirstWord & 0x3FF ) != 0x3F8 ) { FF_PRINTF( "Part at %lu is probably a FAT12\n", pxIOManager->xPartition.ulFATBeginLBA ); } else { FF_PRINTF( "Partition at %lu has strange FAT data %08lX\n", pxIOManager->xPartition.ulFATBeginLBA, ulFirstWord ); } xError = FF_ERR_IOMAN_INVALID_FORMAT | FF_DETERMINEFATTYPE; } } #endif /* ffconfigFAT_CHECK */ } else { /* FAT 32! */ pxPartition->ucType = FF_T_FAT32; #if( ffconfigFAT_CHECK != 0 ) if( ( ( ulFirstWord & 0x0FFFFFF8 ) != 0x0FFFFFF8 ) && ( ( ulFirstWord & 0x0FFFFFF8 ) != 0x0FFFFFF0 ) ) { /* _HT_ I had an SD-card which worked well in Linux/W32 but FreeRTOS+FAT returned me this error So for me I left out this check (just issue a warning for now) */ FF_PRINTF( "prvDetermineFatType: firstWord %08lX\n", ulFirstWord ); xError = FF_ERR_NONE; /* FF_ERR_IOMAN_NOT_FAT_FORMATTED; */ } #endif /* ffconfigFAT_CHECK */ xError = FF_ERR_NONE; } } return xError; } /* prvDetermineFatType() */ /*-----------------------------------------------------------*/ /* Check if ucPartitionID introduces an extended partition. */ static BaseType_t prvIsExtendedPartition( uint8_t ucPartitionID ) { BaseType_t xResult; if( ( ucPartitionID == FF_DOS_EXT_PART ) || ( ucPartitionID == FF_WIN98_EXT_PART ) || ( ucPartitionID == FF_LINUX_EXT_PART ) ) { xResult = pdTRUE; } else { xResult = pdFALSE; } return xResult; } /* prvIsExtendedPartition() */ /*-----------------------------------------------------------*/ /* Check if the media byte in an MBR is valid */ static BaseType_t prvIsValidMedia( uint8_t media ) { BaseType_t xResult; /* * 0xF8 is the standard value for “fixed” (non-removable) media. For * removable media, 0xF0 is frequently used. The legal values for this * field are 0xF0, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, and * 0xFF. The only other important point is that whatever value is put * in here must also be put in the low byte of the FAT[0] entry. This * dates back to the old MS-DOS 1.x media determination noted * earlier and is no longer usually used for anything. */ if( ( 0xf8 <= media ) || ( media == 0xf0 ) ) { xResult = pdTRUE; } else { xResult = pdFALSE; } return xResult; } /* prvIsValidMedia() */ /*-----------------------------------------------------------*/ void FF_ReadParts( uint8_t *pucBuffer, FF_Part_t *pxParts ) { BaseType_t xPartNr; UBaseType_t uxOffset = FF_FAT_PTBL; /* pxParts is expected to be declared as an array of 4 elements: FF_Part_t pxParts[4]; FF_ReadParts( pxBuffer->pucBuffer, pxParts ); */ for( xPartNr = 0; xPartNr < 4; xPartNr++, uxOffset += 16, pxParts++ ) { pxParts->ucActive = FF_getChar( pucBuffer, uxOffset + FF_FAT_PTBL_ACTIVE ); pxParts->ucPartitionID = FF_getChar( pucBuffer, uxOffset + FF_FAT_PTBL_ID ); pxParts->ulSectorCount = FF_getLong( pucBuffer, uxOffset + FF_FAT_PTBL_SECT_COUNT ); pxParts->ulStartLBA = FF_getLong( pucBuffer, uxOffset + FF_FAT_PTBL_LBA ); } } /*-----------------------------------------------------------*/ /* This function will traverse through a chain of extended partitions. */ /* It is protected against rubbish data by a counter. */ static FF_Error_t FF_ParseExtended( FF_IOManager_t *pxIOManager, uint32_t ulFirstSector, uint32_t ulFirstSize, FF_SPartFound_t *pPartsFound ) { uint32_t ulThisSector, ulThisSize; uint32_t ulSectorSize = pxIOManager->usSectorSize / 512; uint32_t prevTotalSectors = pxIOManager->xPartition.ulTotalSectors; FF_Buffer_t *pxBuffer = NULL; BaseType_t xTryCount = 100; BaseType_t xPartNr; BaseType_t xExtendedPartNr; FF_Error_t xError = FF_ERR_NONE; FF_Part_t pxPartitions[ 4 ]; ulThisSector = ulFirstSector; ulThisSize = ulFirstSize; /* Disable sector checking in FF_BlockRead, because the exact disk (partition) parameters are not yet known. Let user driver return an error is appropriate. */ pxIOManager->xPartition.ulTotalSectors = 0; while( xTryCount-- ) { if( ( pxBuffer == NULL ) || ( pxBuffer->ulSector != ulThisSector ) ) { /* Moving to a different sector. Release the previous one and allocate a new buffer. */ if( pxBuffer != NULL ) { xError = FF_ReleaseBuffer( pxIOManager, pxBuffer ); pxBuffer = NULL; if( FF_isERR( xError ) ) { break; } } FF_PRINTF( "FF_ParseExtended: Read sector %u\n", ( unsigned) ulThisSector ); pxBuffer = FF_GetBuffer( pxIOManager, ulThisSector, FF_MODE_READ ); if( pxBuffer == NULL ) { xError = FF_PARSEEXTENDED | FF_ERR_DEVICE_DRIVER_FAILED; /* | FUNCTION...; */ break; } } { uint8_t a = FF_getChar( pxBuffer->pucBuffer, FF_FAT_MBR_SIGNATURE + 0 ); uint8_t b = FF_getChar( pxBuffer->pucBuffer, FF_FAT_MBR_SIGNATURE + 1 ); if( ( a != 0x55 ) || ( b != 0xAA ) ) { FF_PRINTF( "FF_ParseExtended: No signature %02X,%02X\n", a, b ); break; } } /* Check for data partition(s), and remember if there is an extended partition */ FF_ReadParts( pxBuffer->pucBuffer, pxPartitions ); /* Assume there is no next ext partition. */ xExtendedPartNr = -1; for( xPartNr = 0; xPartNr < 4; xPartNr++ ) { uint32_t ulOffset, ulSize, ulNext; if( pxPartitions[ xPartNr ].ulSectorCount == 0 ) { /* Partition is empty */ continue; } if( prvIsExtendedPartition( pxPartitions[ xPartNr ].ucPartitionID ) ) { if( xExtendedPartNr < 0 ) { xExtendedPartNr = xPartNr; } continue; /* We'll examine this ext partition later */ } /* Some sanity checks */ ulOffset = pxPartitions[ xPartNr ].ulStartLBA * ulSectorSize; ulSize = pxPartitions[ xPartNr ].ulSectorCount * ulSectorSize; ulNext = ulThisSector + ulOffset; if( /* Is it oversized? */ ( ulOffset + ulSize > ulThisSize ) || /* or going backward? */ ( ulNext < ulFirstSector ) || /* Or outsize the logical partition? */ ( ulNext > ulFirstSector + ulFirstSize ) ) { FF_PRINTF( "Part %d looks insane: ulThisSector %u ulOffset %u ulNext %u\n", ( int ) xPartNr, ( unsigned )ulThisSector, ( unsigned )ulOffset, ( unsigned )ulNext ); continue; } { /* Store this partition for the caller */ FF_Part_t *p = &pPartsFound->pxPartitions[ pPartsFound->iCount++ ]; /* Copy the whole structure */ memcpy( p, pxPartitions + xPartNr, sizeof( *p ) ); /* and make LBA absolute to sector-0. */ p->ulStartLBA += ulThisSector; p->bIsExtended = pdTRUE; } if( pPartsFound->iCount >= ffconfigMAX_PARTITIONS ) { break; } xTryCount = 100; } /* for( xPartNr = 0; xPartNr < 4; xPartNr++ ) */ if( xExtendedPartNr < 0 ) { FF_PRINTF( "No more extended partitions\n" ); break; /* nothing left to do */ } /* Examine the ulNext extended partition */ ulThisSector = ulFirstSector + pxPartitions[ xExtendedPartNr ].ulStartLBA * ulSectorSize; ulThisSize = pxPartitions[ xExtendedPartNr ].ulSectorCount * ulSectorSize; } if( pxBuffer != NULL ) { FF_Error_t xTempError = FF_ReleaseBuffer( pxIOManager, pxBuffer ); if( FF_isERR( xError ) == pdFALSE ) { xError = xTempError; } } pxIOManager->xPartition.ulTotalSectors = prevTotalSectors; return xError; } /* FF_ParseExtended() */ /*-----------------------------------------------------------*/ /** * @public * @brief Searches a disk for all primary and extended/logical partitions * @brief Previously called FF_PartitionCount * * @param pxIOManager FF_IOManager_t object. * @param pPartsFound Contains an array of ffconfigMAX_PARTITIONS partitions * * @Return >=0 Number of partitions found * @Return <0 error **/ FF_Error_t FF_PartitionSearch( FF_IOManager_t *pxIOManager, FF_SPartFound_t *pPartsFound ) { BaseType_t xPartNr; FF_Buffer_t *pxBuffer; uint8_t *ucDataBuffer; BaseType_t isPBR = pdFALSE; FF_Error_t xError = FF_ERR_NONE; uint32_t prevTotalSectors = pxIOManager->xPartition.ulTotalSectors; FF_Part_t pxPartitions[ 4 ]; memset( pPartsFound, '\0', sizeof( *pPartsFound ) ); do { pxBuffer = FF_GetBuffer( pxIOManager, 0, FF_MODE_READ ); if( pxBuffer == NULL ) { xError = FF_ERR_DEVICE_DRIVER_FAILED | FF_PARTITIONSEARCH; break; } /* Disable sector checking in FF_BlockRead Let user driver return an error is appropriate. */ pxIOManager->xPartition.ulTotalSectors = 0; ucDataBuffer = pxBuffer->pucBuffer; /* Check MBR (Master Boot Record) or PBR (Partition Boot Record) signature. */ if( ( FF_getChar( ucDataBuffer, FF_FAT_MBR_SIGNATURE ) != 0x55 ) && ( FF_getChar( ucDataBuffer, FF_FAT_MBR_SIGNATURE ) != 0xAA ) ) { /* No MBR, but is it a PBR ? Partition Boot Record */ if( ( FF_getChar( ucDataBuffer, 0 ) == 0xEB ) && /* PBR Byte 0 */ ( FF_getChar( ucDataBuffer, 2 ) == 0x90 ) ) { /* PBR Byte 2 No MBR but PBR exist then there is only one partition Handle this later. */ isPBR = pdTRUE; } else { FF_PRINTF( "FF_PartitionSearch: [%02X,%02X] No signature (%02X %02X), no PBR neither\n", FF_getChar( ucDataBuffer, 0 ), FF_getChar( ucDataBuffer, 2 ), FF_getChar( ucDataBuffer, FF_FAT_MBR_SIGNATURE ), FF_getChar( ucDataBuffer, FF_FAT_MBR_SIGNATURE + 1 ) ); /* No MBR and no PBR then no partition found. */ xError = FF_ERR_IOMAN_INVALID_FORMAT | FF_PARTITIONSEARCH; break; } } /* Copy the 4 partition records into 'pxPartitions': */ FF_ReadParts( ucDataBuffer, pxPartitions ); for( xPartNr = 0; ( xPartNr < 4 ) && ( isPBR == pdFALSE ); xPartNr++ ) { /* FF_PRINTF ("FF_Part[%d]: id %02X act %02X Start %6lu Len %6lu (sectors)\n", */ /* xPartNr, pxPartitions[ xPartNr ].ucPartitionID, */ /* pxPartitions[ xPartNr ].ucActive, */ /* pxPartitions[ xPartNr ].ulStartLBA, */ /* pxPartitions[ xPartNr ].ulSectorCount); */ if( prvIsExtendedPartition( pxPartitions[ xPartNr ].ucPartitionID ) != pdFALSE ) { continue; /* Do this later */ } /* The first sector must be a MBR, then check the partition entry in the MBR */ if( ( pxPartitions[ xPartNr ].ucActive != 0x80 ) && ( pxPartitions[ xPartNr ].ucActive != 0x00 ) ) { if( ( xPartNr == 0 ) && ( FF_getShort( ucDataBuffer, FF_FAT_RESERVED_SECTORS ) != 0 ) && ( FF_getChar( ucDataBuffer, FF_FAT_NUMBER_OF_FATS ) != 0 ) ) { isPBR = pdTRUE; } else { xError = FF_ERR_IOMAN_INVALID_FORMAT | FF_PARTITIONSEARCH; break; } } else if( pxPartitions[ xPartNr ].ulSectorCount ) { FF_Part_t *p = &pPartsFound->pxPartitions[ pPartsFound->iCount++ ]; *p = pxPartitions[ xPartNr ]; p->bIsExtended = 0; if( pPartsFound->iCount >= ffconfigMAX_PARTITIONS ) { break; } } } if( FF_isERR( xError ) || ( pPartsFound->iCount >= ffconfigMAX_PARTITIONS ) ) { break; } for( xPartNr = 0; xPartNr < 4; xPartNr++ ) { if( prvIsExtendedPartition( pxPartitions[ xPartNr ].ucPartitionID ) ) { xError = FF_ParseExtended( pxIOManager, pxPartitions[ xPartNr ].ulStartLBA, pxPartitions[ xPartNr ].ulSectorCount, pPartsFound ); if( ( FF_isERR( xError ) != pdFALSE ) || ( pPartsFound->iCount >= ffconfigMAX_PARTITIONS ) ) { goto done; } } } if( pPartsFound->iCount == 0 ) { FF_PRINTF( "FF_Part: no partitions, try as PBR\n" ); isPBR = pdTRUE; } if( isPBR ) { uint8_t media = FF_getChar( ucDataBuffer, FF_FAT_MEDIA_TYPE ); FF_Part_t *p; if( !prvIsValidMedia( media ) ) { FF_PRINTF( "FF_Part: Looks like PBR but media %02X\n", media ); xError = FF_ERR_IOMAN_NO_MOUNTABLE_PARTITION | FF_PARTITIONSEARCH; goto done; } /* This looks like a PBR because it has a valid media type */ p = pPartsFound->pxPartitions; p->ulStartLBA = 0; /* FF_FAT_PTBL_LBA */ p->ulSectorCount = ( uint32_t ) FF_getShort( pxBuffer->pucBuffer, FF_FAT_16_TOTAL_SECTORS ); if( p->ulSectorCount == 0ul ) { p->ulSectorCount = FF_getLong( pxBuffer->pucBuffer, FF_FAT_32_TOTAL_SECTORS ); } p->ucActive = 0x80; /* FF_FAT_PTBL_ACTIVE */ p->ucPartitionID = 0x0B; /* FF_FAT_PTBL_ID MSDOS data partition */ p->bIsExtended = 0; pPartsFound->iCount = 1; } } while( pdFALSE ); done: if( pxBuffer ) { FF_Error_t xTempError = FF_ReleaseBuffer( pxIOManager, pxBuffer ); if( FF_isERR( xError ) == pdFALSE ) { xError = xTempError; } } pxIOManager->xPartition.ulTotalSectors = prevTotalSectors; return FF_isERR( xError ) ? xError : pPartsFound->iCount; } /* FF_PartitionSearch() */ /*-----------------------------------------------------------*/ /* Mount GPT Partition Tables */ #define FF_GPT_HEAD_ENTRY_SIZE 0x54 #define FF_GPT_HEAD_TOTAL_ENTRIES 0x50 #define FF_GPT_HEAD_PART_ENTRY_LBA 0x48 #define FF_GPT_ENTRY_FIRST_SECTOR_LBA 0x20 #define FF_GPT_HEAD_CRC 0x10 #define FF_GPT_HEAD_LENGTH 0x0C static FF_Error_t FF_GetEfiPartitionEntry( FF_IOManager_t *pxIOManager, uint32_t ulPartitionNumber ) { /* Continuing on from FF_Mount() pPartition->ulBeginLBA should be the sector of the GPT Header */ FF_Buffer_t *pxBuffer; FF_Partition_t *pxPartition = &( pxIOManager->xPartition ); uint32_t ulBeginGPT; uint32_t ulEntrySector; uint32_t ulSectorOffset; uint32_t ulPartitionEntrySize; uint32_t ulGPTHeadCRC, ulGPTCrcCheck, ulGPTHeadLength; FF_Error_t xError; do { if( ulPartitionNumber >= 128 ) { xError = FF_ERR_IOMAN_INVALID_PARTITION_NUM | FF_GETEFIPARTITIONENTRY; break; } pxBuffer = FF_GetBuffer( pxIOManager, pxPartition->ulBeginLBA, FF_MODE_READ ); if( pxBuffer == NULL ) { xError = FF_ERR_DEVICE_DRIVER_FAILED | FF_GETEFIPARTITIONENTRY; break; } /* Verify this is an EFI header with the text "EFI PART": */ if( memcmp( pxBuffer->pucBuffer, "EFI PART", 8 ) != 0 ) { /* Already returning an error, but this error would override the current one. */ xError = FF_ReleaseBuffer( pxIOManager, pxBuffer ); if( FF_isERR( xError ) == pdFALSE ) { xError = FF_ERR_IOMAN_INVALID_FORMAT | FF_GETEFIPARTITIONENTRY; } break; } ulBeginGPT = FF_getLong( pxBuffer->pucBuffer, FF_GPT_HEAD_PART_ENTRY_LBA ); ulPartitionEntrySize = FF_getLong( pxBuffer->pucBuffer, FF_GPT_HEAD_ENTRY_SIZE ); ulGPTHeadCRC = FF_getLong( pxBuffer->pucBuffer, FF_GPT_HEAD_CRC ); ulGPTHeadLength = FF_getLong( pxBuffer->pucBuffer, FF_GPT_HEAD_LENGTH ); /* Calculate Head CRC */ /* Blank CRC field */ FF_putLong( pxBuffer->pucBuffer, FF_GPT_HEAD_CRC, 0x00000000 ); /* Calculate CRC */ ulGPTCrcCheck = FF_GetCRC32( pxBuffer->pucBuffer, ulGPTHeadLength ); /* Restore The CRC field */ FF_putLong( pxBuffer->pucBuffer, FF_GPT_HEAD_CRC, ulGPTHeadCRC ); xError = FF_ReleaseBuffer( pxIOManager, pxBuffer ); if( FF_isERR( xError ) ) { break; } /* Check CRC */ if( ulGPTHeadCRC != ulGPTCrcCheck ) { xError = FF_ERR_IOMAN_GPT_HEADER_CORRUPT | FF_GETEFIPARTITIONENTRY; break; } /* Calculate Sector Containing the Partition Entry we want to use. */ ulEntrySector = ( ( ulPartitionNumber * ulPartitionEntrySize ) / pxIOManager->usSectorSize ) + ulBeginGPT; ulSectorOffset = ( ulPartitionNumber % ( pxIOManager->usSectorSize / ulPartitionEntrySize ) ) * ulPartitionEntrySize; pxBuffer = FF_GetBuffer( pxIOManager, ulEntrySector, FF_MODE_READ ); { if( pxBuffer == NULL ) { xError = FF_ERR_DEVICE_DRIVER_FAILED | FF_GETEFIPARTITIONENTRY; break; } pxPartition->ulBeginLBA = FF_getLong( pxBuffer->pucBuffer, ulSectorOffset + FF_GPT_ENTRY_FIRST_SECTOR_LBA ); } xError = FF_ReleaseBuffer( pxIOManager, pxBuffer ); if( FF_isERR( xError ) == pdFALSE ) { if( pxPartition->ulBeginLBA == 0ul ) { xError = FF_ERR_IOMAN_INVALID_PARTITION_NUM | FF_GETEFIPARTITIONENTRY; } } } while( pdFALSE ); return xError; } /* FF_GetEfiPartitionEntry() */ /*-----------------------------------------------------------*/ /** * @public * @brief Mounts the Specified partition, the volume specified by the FF_IOManager_t object provided. * * The device drivers must adhere to the specification provided by * FF_WriteBlocks_t and FF_ReadBlocks_t. * * @param pxIOManager FF_IOManager_t object. * @param PartitionNumber The primary or logical partition number to be mounted, * ranging between 0 and ffconfigMAX_PARTITIONS-1 (normally 0) * Note that FF_PartitionSearch can be called in advance to * enumerate all available partitions * * @Return 0 on success. * @Return FF_ERR_NULL_POINTER if a pxIOManager object wasn't provided. * @Return FF_ERR_IOMAN_INVALID_PARTITION_NUM if the partition number is out of range. * @Return FF_ERR_IOMAN_NO_MOUNTABLE_PARTITION if no partition was found. * @Return FF_ERR_IOMAN_INVALID_FORMAT if the master boot record or partition boot block didn't provide sensible data. * @Return FF_ERR_IOMAN_NOT_FAT_FORMATTED if the volume or partition couldn't be determined to be FAT. (@see FreeRTOSFATConfig.h) * **/ FF_Error_t FF_Mount( FF_Disk_t *pxDisk, BaseType_t xPartitionNumber ) { FF_Partition_t *pxPartition; FF_Buffer_t *pxBuffer = 0; FF_Error_t xError = FF_ERR_NONE; int16_t rootEntryCount; FF_IOManager_t *pxIOManager = pxDisk->pxIOManager; /* HT TODO: find a method to safely determine the FAT type: 32/16/12 */ /* other than only counting Clusters */ /* UBaseType_t fat32Indicator = 0; */ FF_Part_t *pxMyPartition; #if( ffconfigHASH_CACHE != 0 ) BaseType_t i; #endif FF_Error_t xPartitionCount = 0; FF_SPartFound_t partsFound; partsFound.iCount = 0; do { if( pxIOManager == NULL ) { xError = FF_ERR_NULL_POINTER | FF_MOUNT; break; } pxPartition = &( pxIOManager->xPartition ); #if( ffconfigREMOVABLE_MEDIA != 0 ) { pxIOManager->ucFlags &= ( uint8_t ) ( ~ ( FF_IOMAN_DEVICE_IS_EXTRACTED ) ); } #endif /* ffconfigREMOVABLE_MEDIA */ /* FF_IOMAN_InitBufferDescriptors will clear 'pxBuffers' */ memset( pxIOManager->pucCacheMem, '\0', ( size_t ) pxIOManager->usSectorSize * pxIOManager->usCacheSize ); #if( ffconfigHASH_CACHE != 0 ) { memset( pxIOManager->xHashCache, '\0', sizeof( pxIOManager->xHashCache ) ); for( i = 0; i < ffconfigHASH_CACHE_DEPTH; i++ ) { /* _HT_ Check why did JW put it to 100? */ pxIOManager->xHashCache[ i ].ulMisses = 100; } } #endif #if( ffconfigPATH_CACHE != 0 ) { memset( pxPartition->pxPathCache, '\0', sizeof( pxPartition->pxPathCache ) ); } #endif FF_IOMAN_InitBufferDescriptors( pxIOManager ); pxIOManager->FirstFile = 0; xPartitionCount = FF_PartitionSearch( pxIOManager, &partsFound ); if( FF_isERR( xPartitionCount ) ) { xError = xPartitionCount; break; } if( xPartitionCount == 0 ) { xError = FF_ERR_IOMAN_NO_MOUNTABLE_PARTITION | FF_MOUNT; break; } if( xPartitionNumber >= xPartitionCount ) { xError = FF_ERR_IOMAN_INVALID_PARTITION_NUM | FF_MOUNT; break; } pxMyPartition = &( partsFound.pxPartitions[ xPartitionNumber ] ); pxPartition->ulBeginLBA = pxMyPartition->ulStartLBA; if( pxMyPartition->ucPartitionID == 0xEE ) { xError = FF_GetEfiPartitionEntry( pxIOManager, xPartitionNumber ); if( FF_isERR( xError ) ) { break; } } /* Now we get the Partition sector. */ pxBuffer = FF_GetBuffer( pxIOManager, pxPartition->ulBeginLBA, FF_MODE_READ ); if( pxBuffer == NULL ) { xError = FF_ERR_DEVICE_DRIVER_FAILED | FF_MOUNT; break; } pxPartition->usBlkSize = FF_getShort( pxBuffer->pucBuffer, FF_FAT_BYTES_PER_SECTOR ); if( ( ( pxPartition->usBlkSize % 512 ) != 0 ) || ( pxPartition->usBlkSize == 0 ) ) { /* An error here should override the current error, as its likely fatal. */ xError = FF_ReleaseBuffer( pxIOManager, pxBuffer ); if( FF_isERR( xError ) == pdFALSE ) { xError = FF_ERR_IOMAN_INVALID_FORMAT | FF_MOUNT; } break; } /* Assume FAT16, then we'll adjust if its FAT32 */ pxPartition->usReservedSectors = FF_getShort( pxBuffer->pucBuffer, FF_FAT_RESERVED_SECTORS ); pxPartition->ulFATBeginLBA = pxPartition->ulBeginLBA + pxPartition->usReservedSectors; pxPartition->ucNumFATS = ( uint8_t ) FF_getShort( pxBuffer->pucBuffer, FF_FAT_NUMBER_OF_FATS ); pxPartition->ulSectorsPerFAT = ( uint32_t ) FF_getShort( pxBuffer->pucBuffer, FF_FAT_16_SECTORS_PER_FAT ); pxPartition->ulSectorsPerCluster = FF_getChar( pxBuffer->pucBuffer, FF_FAT_SECTORS_PER_CLUS ); /* Set the BlockFactor (How many real-blocks in a fake block!). */ pxPartition->ucBlkFactor = ( uint8_t ) ( pxPartition->usBlkSize / pxIOManager->usSectorSize ); pxPartition->ulTotalSectors = ( uint32_t ) FF_getShort( pxBuffer->pucBuffer, FF_FAT_16_TOTAL_SECTORS ); if( pxPartition->ulTotalSectors == 0 ) { pxPartition->ulTotalSectors = FF_getLong( pxBuffer->pucBuffer, FF_FAT_32_TOTAL_SECTORS ); } if( pxPartition->ulSectorsPerFAT == 0 ) { /* FAT32 */ pxPartition->ulSectorsPerFAT = FF_getLong( pxBuffer->pucBuffer, FF_FAT_32_SECTORS_PER_FAT ); pxPartition->ulRootDirCluster = FF_getLong( pxBuffer->pucBuffer, FF_FAT_ROOT_DIR_CLUSTER ); memcpy( pxPartition->pcVolumeLabel, pxBuffer->pucBuffer + FF_FAT_32_VOL_LABEL, sizeof( pxPartition->pcVolumeLabel ) - 1 ); } else { /* FAT16 */ pxPartition->ulRootDirCluster = 1; /* 1st Cluster is RootDir! */ memcpy( pxPartition->pcVolumeLabel, pxBuffer->pucBuffer + FF_FAT_16_VOL_LABEL, sizeof( pxPartition->pcVolumeLabel ) - 1); } pxPartition->ulClusterBeginLBA = pxPartition->ulFATBeginLBA + ( pxPartition->ucNumFATS * pxPartition->ulSectorsPerFAT ); #if( ffconfigWRITE_FREE_COUNT != 0 ) { pxPartition->ulFSInfoLBA = pxPartition->ulBeginLBA + FF_getShort( pxBuffer->pucBuffer, 48 ); } #endif FF_ReleaseBuffer( pxIOManager, pxBuffer ); /* Release the buffer finally! */ if( pxPartition->usBlkSize == 0 ) { xError = FF_ERR_IOMAN_INVALID_FORMAT | FF_MOUNT; break; } rootEntryCount = FF_getShort( pxBuffer->pucBuffer, FF_FAT_ROOT_ENTRY_COUNT ); pxPartition->ulRootDirSectors = ( ( rootEntryCount * 32 ) + pxPartition->usBlkSize - 1 ) / pxPartition->usBlkSize; pxPartition->ulFirstDataSector = pxPartition->ulClusterBeginLBA + pxPartition->ulRootDirSectors; pxPartition->ulDataSectors = pxPartition->ulTotalSectors - ( pxPartition->usReservedSectors + ( pxPartition->ucNumFATS * pxPartition->ulSectorsPerFAT ) + pxPartition->ulRootDirSectors ); /* * HT: fat32Indicator not yet used * As there is so much confusion about the FAT types * I was thinking of collecting indications for either FAT12, 16 or 32 */ /* if( FF_getShort( pxBuffer->pucBuffer, FF_FAT_EXT_BOOT_SIGNATURE ) == 0x29 ) fat32Indicator++; if( rootEntryCount == 0 ) fat32Indicator++; */ if( pxPartition->ulSectorsPerCluster == 0 ) { xError = FF_ERR_IOMAN_INVALID_FORMAT | FF_MOUNT; break; } pxPartition->ulNumClusters = pxPartition->ulDataSectors / pxPartition->ulSectorsPerCluster; xError = prvDetermineFatType( pxIOManager ); if( FF_isERR( xError ) ) { break; } if( !rootEntryCount && pxPartition->ucType != FF_T_FAT32 ) { FF_PRINTF( "No root dir, must be a FAT32\n" ); pxPartition->ucType = FF_T_FAT32; } pxPartition->ucPartitionMounted = pdTRUE; pxPartition->ulLastFreeCluster = 0; #if( ffconfigMOUNT_FIND_FREE != 0 ) { FF_LockFAT( pxIOManager ); { /* The parameter 'pdFALSE' means: do not claim the free cluster found. */ pxPartition->ulLastFreeCluster = FF_FindFreeCluster( pxIOManager, &xError, pdFALSE ); } FF_UnlockFAT( pxIOManager ); if( FF_isERR( xError ) ) { if( FF_GETERROR( xError ) == FF_ERR_IOMAN_NOT_ENOUGH_FREE_SPACE ) { pxPartition->ulLastFreeCluster = 0; } else { break; } } pxPartition->ulFreeClusterCount = FF_CountFreeClusters( pxIOManager, &xError ); if( FF_isERR( xError ) ) { break; } } #else { pxPartition->ulFreeClusterCount = 0; } #endif /* ffconfigMOUNT_FIND_FREE */ } while( pdFALSE ); if( FF_isERR( xError ) == pdFALSE ) { xError = 0; } return xError; } /* FF_Mount() */ /*-----------------------------------------------------------*/ /** * @private * @brief Checks the cache for Active Handles * * @param pxIOManager FF_IOManager_t Object. * * @Return pdTRUE if an active handle is found, else pdFALSE. * * @pre This function must be wrapped with the cache handling semaphore. **/ static BaseType_t prvHasActiveHandles( FF_IOManager_t *pxIOManager ) { BaseType_t xResult; FF_Buffer_t *pxBuffer = pxIOManager->pxBuffers; FF_Buffer_t *pxLastBuffer = pxBuffer + pxIOManager->usCacheSize; for( ; ; ) { if( pxBuffer->usNumHandles ) { xResult = pdTRUE; break; } pxBuffer++; if( pxBuffer == pxLastBuffer ) { xResult = pdFALSE; break; } } return xResult; } /* prvHasActiveHandles() */ /*-----------------------------------------------------------*/ /** * @public * @brief Unmounts the active partition. * * @param pxIOManager FF_IOManager_t Object. * * @Return FF_ERR_NONE on success. **/ FF_Error_t FF_Unmount( FF_Disk_t *pxDisk ) { FF_Error_t xError = FF_ERR_NONE; FF_IOManager_t *pxIOManager; #if( ffconfigMIRROR_FATS_UMOUNT != 0 ) UBaseType_t uxIndex, y; FF_Buffer_t *pxBuffer; #endif if( pxDisk->pxIOManager == NULL ) { xError = FF_ERR_NULL_POINTER | FF_UNMOUNT; } else if( pxDisk->pxIOManager->xPartition.ucPartitionMounted == 0 ) { xError = FF_ERR_NONE; } else { pxIOManager = pxDisk->pxIOManager; FF_PendSemaphore( pxIOManager->pvSemaphore ); /* Ensure that there are no File Handles */ { if( prvHasActiveHandles( pxIOManager ) != 0 ) { /* Active handles found on the cache. */ xError = FF_ERR_IOMAN_ACTIVE_HANDLES | FF_UNMOUNT; } else if( pxIOManager->FirstFile != NULL ) { /* Open files in this partition. */ xError = FF_ERR_IOMAN_ACTIVE_HANDLES | FF_UNMOUNT; } else { /* Release Semaphore to call this function! */ FF_ReleaseSemaphore( pxIOManager->pvSemaphore ); /* Flush any unwritten sectors to disk. */ xError = FF_FlushCache( pxIOManager ); /* Reclaim Semaphore */ FF_PendSemaphore( pxIOManager->pvSemaphore ); if( FF_isERR( xError ) == pdFALSE ) { pxIOManager->xPartition.ucPartitionMounted = pdFALSE; #if( ffconfigMIRROR_FATS_UMOUNT != 0 ) { FF_ReleaseSemaphore( pxIOManager->pvSemaphore ); for( uxIndex = 0; uxIndex < pxIOManager->xPartition.ulSectorsPerFAT; uxIndex++ ) { pxBuffer = FF_GetBuffer( pxIOManager, pxIOManager->xPartition.ulFATBeginLBA + uxIndex, FF_MODE_READ ); if( !pxBuffer ) { xError = FF_ERR_DEVICE_DRIVER_FAILED | FF_UNMOUNT; break; } for( y = 0; y < pxIOManager->xPartition.ucNumFATS; y++ ) { FF_BlockWrite( pxIOManager, pxIOManager->xPartition.ulFATBeginLBA + ( y * pxIOManager->xPartition.ulSectorsPerFAT ) + uxIndex, 1, pxBuffer->pucBuffer, pdFALSE ); } } FF_PendSemaphore( pxIOManager->pvSemaphore ); } #endif } } } FF_ReleaseSemaphore( pxIOManager->pvSemaphore ); } return xError; } /* FF_Unmount() */ /*-----------------------------------------------------------*/ FF_Error_t FF_IncreaseFreeClusters( FF_IOManager_t *pxIOManager, uint32_t Count ) { FF_Error_t xError; #if( ffconfigWRITE_FREE_COUNT != 0 ) FF_Buffer_t *pxBuffer; #endif do { /* Open a do {} while( pdFALSE ) loop to allow the use of break statements. */ if( pxIOManager->xPartition.ulFreeClusterCount == 0ul ) { /* Apparently the number of free clusters has not been calculated yet, or no free cluster was available. Now check it. */ pxIOManager->xPartition.ulFreeClusterCount = FF_CountFreeClusters( pxIOManager, &xError ); if( FF_isERR( xError ) ) { break; } } else { xError = FF_ERR_NONE; taskENTER_CRITICAL(); { pxIOManager->xPartition.ulFreeClusterCount += Count; } taskEXIT_CRITICAL(); } if( pxIOManager->xPartition.ulLastFreeCluster == 0 ) { BaseType_t xTakeLock = FF_Has_Lock( pxIOManager, FF_FAT_LOCK ) == pdFALSE; if( xTakeLock ) { FF_LockFAT( pxIOManager ); } /* Find the an available cluster. */ pxIOManager->xPartition.ulLastFreeCluster = FF_FindFreeCluster( pxIOManager, &xError, pdFALSE ); if( xTakeLock ) { FF_UnlockFAT( pxIOManager ); } if( FF_isERR( xError ) ) { break; } } #if( ffconfigWRITE_FREE_COUNT != 0 ) { /* FAT32 updates the FSINFO sector. */ if( pxIOManager->xPartition.ucType == FF_T_FAT32 ) { /* Find the FSINFO sector. */ pxBuffer = FF_GetBuffer( pxIOManager, pxIOManager->xPartition.ulFSInfoLBA, FF_MODE_WRITE ); if( pxBuffer == NULL ) { xError = FF_ERR_DEVICE_DRIVER_FAILED | FF_INCREASEFREECLUSTERS; } else { uint32_t ulSignature1; uint32_t ulSignature2; ulSignature1 = FF_getLong( pxBuffer->pucBuffer, FS_INFO_OFFSET_SIGNATURE1_000 ); ulSignature2 = FF_getLong( pxBuffer->pucBuffer, FS_INFO_OFFSET_SIGNATURE2_484 ); if( ( ulSignature1 == FS_INFO_SIGNATURE1_0x41615252 ) && ( ulSignature2 == FS_INFO_SIGNATURE2_0x61417272 ) ) { /* FSINFO sector magic numbers we're verified. Safe to write. */ FF_putLong( pxBuffer->pucBuffer, FS_INFO_OFFSET_FREE_COUNT_488, pxIOManager->xPartition.ulFreeClusterCount ); FF_putLong( pxBuffer->pucBuffer, FS_INFO_OFFSET_FREE_CLUSTER_492, pxIOManager->xPartition.ulLastFreeCluster ); } xError = FF_ReleaseBuffer( pxIOManager, pxBuffer ); } } } #endif } while( pdFALSE ); return xError; } /* FF_IncreaseFreeClusters() */ /*-----------------------------------------------------------*/ FF_Error_t FF_DecreaseFreeClusters( FF_IOManager_t *pxIOManager, uint32_t Count ) { FF_Error_t xError = FF_ERR_NONE; #if( ffconfigWRITE_FREE_COUNT != 0 ) FF_Buffer_t *pxBuffer; #endif if( pxIOManager->xPartition.ulFreeClusterCount == 0ul ) { pxIOManager->xPartition.ulFreeClusterCount = FF_CountFreeClusters( pxIOManager, &xError ); } else { taskENTER_CRITICAL(); pxIOManager->xPartition.ulFreeClusterCount -= Count; taskEXIT_CRITICAL(); } if( FF_isERR( xError ) == pdFALSE ) { if( pxIOManager->xPartition.ulLastFreeCluster == 0 ) { FF_LockFAT( pxIOManager ); { pxIOManager->xPartition.ulLastFreeCluster = FF_FindFreeCluster( pxIOManager, &xError, pdFALSE ); } FF_UnlockFAT( pxIOManager ); } } if( FF_isERR( xError ) == pdFALSE ) { #if( ffconfigWRITE_FREE_COUNT != 0 ) { /* FAT32 update the FSINFO sector. */ if( pxIOManager->xPartition.ucType == FF_T_FAT32 ) { /* Find the FSINFO sector. */ pxBuffer = FF_GetBuffer( pxIOManager, pxIOManager->xPartition.ulFSInfoLBA, FF_MODE_WRITE ); if( pxBuffer == NULL ) { xError = FF_ERR_DEVICE_DRIVER_FAILED | FF_DECREASEFREECLUSTERS; } else { if( ( FF_getLong( pxBuffer->pucBuffer, FS_INFO_OFFSET_SIGNATURE1_000 ) == FS_INFO_SIGNATURE1_0x41615252 ) && ( FF_getLong( pxBuffer->pucBuffer, FS_INFO_OFFSET_SIGNATURE2_484 ) == FS_INFO_SIGNATURE2_0x61417272 ) ) { /* FSINFO sector magic nums we're verified. Safe to write. */ FF_putLong( pxBuffer->pucBuffer, FS_INFO_OFFSET_FREE_COUNT_488, pxIOManager->xPartition.ulFreeClusterCount ); FF_putLong( pxBuffer->pucBuffer, FS_INFO_OFFSET_FREE_CLUSTER_492, pxIOManager->xPartition.ulLastFreeCluster ); } xError = FF_ReleaseBuffer( pxIOManager, pxBuffer ); } } } #endif } return xError; } /* FF_DecreaseFreeClusters() */ /*-----------------------------------------------------------*/ /** * @brief Returns the Block-size of a mounted Partition * * The purpose of this function is to provide API access to information * that might be useful in special cases. Like USB sticks that require a sector * knocking sequence for security. After the sector knock, some secure USB * sticks then present a different BlockSize. * * @param pxIOManager FF_IOManager_t Object returned from FF_CreateIOManger() * * @Return The blocksize of the partition. A value less than 0 when an error occurs. * @Return Any negative value can be cast to the FF_Error_t type. **/ int32_t FF_GetPartitionBlockSize( FF_IOManager_t *pxIOManager ) { int32_t lReturn; if( pxIOManager ) { lReturn = ( int32_t ) pxIOManager->xPartition.usBlkSize; } else { lReturn = FF_ERR_NULL_POINTER | FF_GETPARTITIONBLOCKSIZE; } return lReturn; } /* FF_GetPartitionBlockSize() */ /*-----------------------------------------------------------*/ #if( ffconfig64_NUM_SUPPORT != 0 ) /** * @brief Returns the number of bytes contained within the mounted partition or volume. * * @param pxIOManager FF_IOManager_t Object returned from FF_CreateIOManger() * * @Return The total number of bytes that the mounted partition or volume contains. * **/ uint64_t FF_GetVolumeSize( FF_IOManager_t *pxIOManager ) { uint64_t ullResult; if( pxIOManager ) { uint32_t TotalClusters = ( pxIOManager->xPartition.ulDataSectors / pxIOManager->xPartition.ulSectorsPerCluster ); ullResult = ( uint64_t ) ( ( uint64_t ) TotalClusters * ( uint64_t ) ( ( uint64_t ) pxIOManager->xPartition.ulSectorsPerCluster * ( uint64_t ) pxIOManager->xPartition.usBlkSize ) ); } else { ullResult = 0ULL; } return ullResult; } /* FF_GetVolumeSize() */ #else uint32_t FF_GetVolumeSize( FF_IOManager_t *pxIOManager ) { uint32_t ulResult; if( pxIOManager ) { uint32_t TotalClusters = pxIOManager->xPartition.ulDataSectors / pxIOManager->xPartition.ulSectorsPerCluster; ulResult = ( uint32_t ) ( TotalClusters * ( pxIOManager->xPartition.ulSectorsPerCluster * pxIOManager->xPartition.usBlkSize ) ); } else { ulResult = 0UL; } return ulResult; } /* FF_GetVolumeSize() */ #endif /*-----------------------------------------------------------*/