/* * FreeRTOS+FAT V2.3.3 * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * https://www.FreeRTOS.org * https://github.com/FreeRTOS * */ /* Standard includes. */ #include #include #include #include /* Scheduler include files. */ #include "FreeRTOS.h" #include "task.h" #include "semphr.h" #include "portmacro.h" /* FreeRTOS+FAT includes. */ #include "ff_headers.h" #include "ff_ramdisk.h" #include "ff_sys.h" #define ramHIDDEN_SECTOR_COUNT 8 #define ramPRIMARY_PARTITIONS 1 #define ramHUNDRED_64_BIT 100ULL #define ramSECTOR_SIZE 512UL #define ramPARTITION_NUMBER 0 /* Only a single partition is used. */ #define ramBYTES_PER_KB ( 1024ull ) #define ramSECTORS_PER_KB ( ramBYTES_PER_KB / 512ull ) /* Used as a magic number to indicate that an FF_Disk_t structure is a RAM * disk. */ #define ramSIGNATURE 0x41404342 /*-----------------------------------------------------------*/ /* * The function that writes to the media - as this is implementing a RAM disk * the media is just a RAM buffer. */ static int32_t prvWriteRAM( uint8_t * pucBuffer, uint32_t ulSectorNumber, uint32_t ulSectorCount, FF_Disk_t * pxDisk ); /* * The function that reads from the media - as this is implementing a RAM disk * the media is just a RAM buffer. */ static int32_t prvReadRAM( uint8_t * pucBuffer, uint32_t ulSectorNumber, uint32_t ulSectorCount, FF_Disk_t * pxDisk ); /* * This is the driver for a RAM disk. Unlike most media types, RAM disks are * volatile so are created anew each time the system is booted. As the disk is * new and just created, it must also be partitioned and formatted. */ static FF_Error_t prvPartitionAndFormatDisk( FF_Disk_t * pxDisk ); /*-----------------------------------------------------------*/ /* This is the prototype of the function used to initialise the RAM disk driver. * Other media drivers do not have to have the same prototype. * * In this example: + pcName is the name to give the disk within FreeRTOS+FAT's virtual file system. + pucDataBuffer is the start of the RAM to use as the disk. + ulSectorCount is effectively the size of the disk, each sector is 512 bytes. + xIOManagerCacheSize is the size of the IO manager's cache, which must be a + multiple of the sector size, and at least twice as big as the sector size. */ FF_Disk_t * FF_RAMDiskInit( char * pcName, uint8_t * pucDataBuffer, uint32_t ulSectorCount, size_t xIOManagerCacheSize ) { FF_Error_t xError; FF_Disk_t * pxDisk = NULL; FF_CreationParameters_t xParameters; /* Check the validity of the xIOManagerCacheSize parameter. */ configASSERT( ( xIOManagerCacheSize % ramSECTOR_SIZE ) == 0 ); configASSERT( ( xIOManagerCacheSize >= ( 2 * ramSECTOR_SIZE ) ) ); /* Attempt to allocated the FF_Disk_t structure. */ pxDisk = ( FF_Disk_t * ) pvPortMalloc( sizeof( FF_Disk_t ) ); if( pxDisk != NULL ) { /* Start with every member of the structure set to zero. */ memset( pxDisk, '\0', sizeof( FF_Disk_t ) ); /* Clear the entire space. */ memset( pucDataBuffer, '\0', ulSectorCount * ramSECTOR_SIZE ); /* The pvTag member of the FF_Disk_t structure allows the structure to be * extended to also include media specific parameters. The only media * specific data that needs to be stored in the FF_Disk_t structure for a * RAM disk is the location of the RAM buffer itself - so this is stored * directly in the FF_Disk_t's pvTag member. */ pxDisk->pvTag = ( void * ) pucDataBuffer; /* The signature is used by the disk read and disk write functions to * ensure the disk being accessed is a RAM disk. */ pxDisk->ulSignature = ramSIGNATURE; /* The number of sectors is recorded for bounds checking in the read and * write functions. */ pxDisk->ulNumberOfSectors = ulSectorCount; /* Create the IO manager that will be used to control the RAM disk. */ memset( &xParameters, '\0', sizeof( xParameters ) ); xParameters.pucCacheMemory = NULL; xParameters.ulMemorySize = xIOManagerCacheSize; xParameters.ulSectorSize = ramSECTOR_SIZE; xParameters.fnWriteBlocks = prvWriteRAM; xParameters.fnReadBlocks = prvReadRAM; xParameters.pxDisk = pxDisk; /* Driver is reentrant so xBlockDeviceIsReentrant can be set to pdTRUE. * In this case the semaphore is only used to protect FAT data * structures. */ xParameters.pvSemaphore = ( void * ) xSemaphoreCreateRecursiveMutex(); xParameters.xBlockDeviceIsReentrant = pdFALSE; pxDisk->pxIOManager = FF_CreateIOManager( &xParameters, &xError ); if( ( pxDisk->pxIOManager != NULL ) && ( FF_isERR( xError ) == pdFALSE ) ) { /* Record that the RAM disk has been initialised. */ pxDisk->xStatus.bIsInitialised = pdTRUE; /* Create a partition on the RAM disk. NOTE! The disk is only * being partitioned here because it is a new RAM disk. It is * known that the disk has not been used before, and cannot already * contain any partitions. Most media drivers will not perform * this step because the media will have already been partitioned. */ xError = prvPartitionAndFormatDisk( pxDisk ); if( FF_isERR( xError ) == pdFALSE ) { /* Record the partition number the FF_Disk_t structure is, then * mount the partition. */ pxDisk->xStatus.bPartitionNumber = ramPARTITION_NUMBER; /* Mount the partition. */ xError = FF_Mount( pxDisk, ramPARTITION_NUMBER ); FF_PRINTF( "FF_RAMDiskInit: FF_Mount: %s\n", ( const char * ) FF_GetErrMessage( xError ) ); } if( FF_isERR( xError ) == pdFALSE ) { /* The partition mounted successfully, add it to the virtual * file system - where it will appear as a directory off the file * system's root directory. */ FF_FS_Add( pcName, pxDisk ); } } else { FF_PRINTF( "FF_RAMDiskInit: FF_CreateIOManager: %s\n", ( const char * ) FF_GetErrMessage( xError ) ); /* The disk structure was allocated, but the disk's IO manager could * not be allocated, so free the disk again. */ FF_RAMDiskDelete( pxDisk ); pxDisk = NULL; } } else { FF_PRINTF( "FF_RAMDiskInit: Malloc failed\n" ); } return pxDisk; } /*-----------------------------------------------------------*/ BaseType_t FF_RAMDiskDelete( FF_Disk_t * pxDisk ) { if( pxDisk != NULL ) { pxDisk->ulSignature = 0; pxDisk->xStatus.bIsInitialised = 0; if( pxDisk->pxIOManager != NULL ) { FF_DeleteIOManager( pxDisk->pxIOManager ); } vPortFree( pxDisk ); } return pdPASS; } /*-----------------------------------------------------------*/ static int32_t prvReadRAM( uint8_t * pucDestination, uint32_t ulSectorNumber, uint32_t ulSectorCount, FF_Disk_t * pxDisk ) { int32_t lReturn; uint8_t * pucSource; if( pxDisk != NULL ) { if( pxDisk->ulSignature != ramSIGNATURE ) { /* The disk structure is not valid because it doesn't contain a * magic number written to the disk when it was created. */ lReturn = FF_ERR_IOMAN_DRIVER_FATAL_ERROR | FF_ERRFLAG; } else if( pxDisk->xStatus.bIsInitialised == pdFALSE ) { /* The disk has not been initialised. */ lReturn = FF_ERR_IOMAN_OUT_OF_BOUNDS_WRITE | FF_ERRFLAG; } else if( ulSectorNumber >= pxDisk->ulNumberOfSectors ) { /* The start sector is not within the bounds of the disk. */ lReturn = ( FF_ERR_IOMAN_OUT_OF_BOUNDS_WRITE | FF_ERRFLAG ); } else if( ( pxDisk->ulNumberOfSectors - ulSectorNumber ) < ulSectorCount ) { /* The end sector is not within the bounds of the disk. */ lReturn = ( FF_ERR_IOMAN_OUT_OF_BOUNDS_WRITE | FF_ERRFLAG ); } else { /* Obtain the pointer to the RAM buffer being used as the disk. */ pucSource = ( uint8_t * ) pxDisk->pvTag; /* Move to the start of the sector being read. */ pucSource += ( ramSECTOR_SIZE * ulSectorNumber ); /* Copy the data from the disk. As this is a RAM disk this can be * done using memcpy(). */ memcpy( ( void * ) pucDestination, ( void * ) pucSource, ( size_t ) ( ulSectorCount * ramSECTOR_SIZE ) ); lReturn = FF_ERR_NONE; } } else { lReturn = FF_ERR_NULL_POINTER | FF_ERRFLAG; } return lReturn; } /*-----------------------------------------------------------*/ static int32_t prvWriteRAM( uint8_t * pucSource, uint32_t ulSectorNumber, uint32_t ulSectorCount, FF_Disk_t * pxDisk ) { int32_t lReturn = FF_ERR_NONE; uint8_t * pucDestination; if( pxDisk != NULL ) { if( pxDisk->ulSignature != ramSIGNATURE ) { /* The disk structure is not valid because it doesn't contain a * magic number written to the disk when it was created. */ lReturn = FF_ERR_IOMAN_DRIVER_FATAL_ERROR | FF_ERRFLAG; } else if( pxDisk->xStatus.bIsInitialised == pdFALSE ) { /* The disk has not been initialised. */ lReturn = FF_ERR_IOMAN_OUT_OF_BOUNDS_WRITE | FF_ERRFLAG; } else if( ulSectorNumber >= pxDisk->ulNumberOfSectors ) { /* The start sector is not within the bounds of the disk. */ lReturn = ( FF_ERR_IOMAN_OUT_OF_BOUNDS_WRITE | FF_ERRFLAG ); } else if( ( pxDisk->ulNumberOfSectors - ulSectorNumber ) < ulSectorCount ) { /* The end sector is not within the bounds of the disk. */ lReturn = ( FF_ERR_IOMAN_OUT_OF_BOUNDS_WRITE | FF_ERRFLAG ); } else { /* Obtain the location of the RAM being used as the disk. */ pucDestination = ( uint8_t * ) pxDisk->pvTag; /* Move to the sector being written to. */ pucDestination += ( ramSECTOR_SIZE * ulSectorNumber ); /* Write to the disk. As this is a RAM disk the write can use a * memcpy(). */ memcpy( ( void * ) pucDestination, ( void * ) pucSource, ( size_t ) ulSectorCount * ( size_t ) ramSECTOR_SIZE ); lReturn = FF_ERR_NONE; } } else { lReturn = FF_ERR_NULL_POINTER | FF_ERRFLAG; } return lReturn; } /*-----------------------------------------------------------*/ static FF_Error_t prvPartitionAndFormatDisk( FF_Disk_t * pxDisk ) { FF_PartitionParameters_t xPartition; FF_Error_t xError; /* Create a single partition that fills all available space on the disk. */ memset( &xPartition, '\0', sizeof( xPartition ) ); xPartition.ulSectorCount = pxDisk->ulNumberOfSectors; xPartition.ulHiddenSectors = ramHIDDEN_SECTOR_COUNT; xPartition.xPrimaryCount = ramPRIMARY_PARTITIONS; xPartition.eSizeType = eSizeIsQuota; /* Partition the disk */ xError = FF_Partition( pxDisk, &xPartition ); FF_PRINTF( "FF_Partition: %s\n", ( const char * ) FF_GetErrMessage( xError ) ); if( FF_isERR( xError ) == pdFALSE ) { /* Format the partition. */ xError = FF_Format( pxDisk, ramPARTITION_NUMBER, pdTRUE, pdTRUE ); FF_PRINTF( "FF_RAMDiskInit: FF_Format: %s\n", ( const char * ) FF_GetErrMessage( xError ) ); } return xError; } /*-----------------------------------------------------------*/ BaseType_t FF_RAMDiskShowPartition( FF_Disk_t * pxDisk ) { FF_Error_t xError; uint64_t ullFreeSectors; uint32_t ulTotalSizeKB, ulFreeSizeKB; int iPercentageFree; FF_IOManager_t * pxIOManager; const char * pcTypeName = "unknown type"; BaseType_t xReturn = pdPASS; if( pxDisk == NULL ) { xReturn = pdFAIL; } else { pxIOManager = pxDisk->pxIOManager; FF_PRINTF( "Reading FAT and calculating Free Space\n" ); switch( pxIOManager->xPartition.ucType ) { case FF_T_FAT12: pcTypeName = "FAT12"; break; case FF_T_FAT16: pcTypeName = "FAT16"; break; case FF_T_FAT32: pcTypeName = "FAT32"; break; default: pcTypeName = "UNKOWN"; break; } FF_GetFreeSize( pxIOManager, &xError ); ullFreeSectors = pxIOManager->xPartition.ulFreeClusterCount * pxIOManager->xPartition.ulSectorsPerCluster; if( pxIOManager->xPartition.ulDataSectors == ( uint32_t ) 0 ) { iPercentageFree = 0; } else { iPercentageFree = ( int ) ( ( ramHUNDRED_64_BIT * ullFreeSectors + pxIOManager->xPartition.ulDataSectors / 2 ) / ( ( uint64_t ) pxIOManager->xPartition.ulDataSectors ) ); } ulTotalSizeKB = pxIOManager->xPartition.ulDataSectors / ramSECTORS_PER_KB; ulFreeSizeKB = ( uint32_t ) ( ullFreeSectors / ramSECTORS_PER_KB ); /* It is better not to use the 64-bit format such as %Lu because it * might not be implemented. */ FF_PRINTF( "Partition Nr %8u\n", pxDisk->xStatus.bPartitionNumber ); FF_PRINTF( "Type %8u (%s)\n", pxIOManager->xPartition.ucType, pcTypeName ); FF_PRINTF( "VolLabel '%8s' \n", pxIOManager->xPartition.pcVolumeLabel ); FF_PRINTF( "TotalSectors %8u\n", ( unsigned ) pxIOManager->xPartition.ulTotalSectors ); FF_PRINTF( "SecsPerCluster %8u\n", ( unsigned ) pxIOManager->xPartition.ulSectorsPerCluster ); FF_PRINTF( "Size %8u KB\n", ( unsigned ) ulTotalSizeKB ); FF_PRINTF( "FreeSize %8u KB ( %d perc free )\n", ( unsigned ) ulFreeSizeKB, iPercentageFree ); } return xReturn; } /*-----------------------------------------------------------*/ void FF_RAMDiskFlush( FF_Disk_t * pxDisk ) { if( ( pxDisk != NULL ) && ( pxDisk->xStatus.bIsInitialised != 0 ) && ( pxDisk->pxIOManager != NULL ) ) { FF_FlushCache( pxDisk->pxIOManager ); } } /*-----------------------------------------------------------*/