2 * FreeRTOS+FAT build 191128 - Note: FreeRTOS+FAT is still in the lab!
\r
3 * Copyright (C) 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
\r
4 * Authors include James Walmsley, Hein Tibosch and Richard Barry
\r
6 * Permission is hereby granted, free of charge, to any person obtaining a copy of
\r
7 * this software and associated documentation files (the "Software"), to deal in
\r
8 * the Software without restriction, including without limitation the rights to
\r
9 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
\r
10 * the Software, and to permit persons to whom the Software is furnished to do so,
\r
11 * subject to the following conditions:
\r
13 * The above copyright notice and this permission notice shall be included in all
\r
14 * copies or substantial portions of the Software.
\r
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
\r
18 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
\r
19 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
\r
20 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
\r
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
23 * https://www.FreeRTOS.org
\r
27 /* Standard includes. */
\r
33 /* Scheduler include files. */
\r
34 #include "FreeRTOS.h"
\r
37 #include "portmacro.h"
\r
39 /* FreeRTOS+FAT includes. */
\r
40 #include "ff_headers.h"
\r
41 #include "ff_ramdisk.h"
\r
44 #define ramHIDDEN_SECTOR_COUNT 8
\r
45 #define ramPRIMARY_PARTITIONS 1
\r
46 #define ramHUNDRED_64_BIT 100ULL
\r
47 #define ramSECTOR_SIZE 512UL
\r
48 #define ramPARTITION_NUMBER 0 /* Only a single partition is used. */
\r
49 #define ramBYTES_PER_KB ( 1024ull )
\r
50 #define ramSECTORS_PER_KB ( ramBYTES_PER_KB / 512ull )
\r
52 /* Used as a magic number to indicate that an FF_Disk_t structure is a RAM
\r
54 #define ramSIGNATURE 0x41404342
\r
56 /*-----------------------------------------------------------*/
\r
59 * The function that writes to the media - as this is implementing a RAM disk
\r
60 * the media is just a RAM buffer.
\r
62 static int32_t prvWriteRAM( uint8_t *pucBuffer, uint32_t ulSectorNumber, uint32_t ulSectorCount, FF_Disk_t *pxDisk );
\r
65 * The function that reads from the media - as this is implementing a RAM disk
\r
66 * the media is just a RAM buffer.
\r
68 static int32_t prvReadRAM( uint8_t *pucBuffer, uint32_t ulSectorNumber, uint32_t ulSectorCount, FF_Disk_t *pxDisk );
\r
71 * This is the driver for a RAM disk. Unlike most media types, RAM disks are
\r
72 * volatile so are created anew each time the system is booted. As the disk is
\r
73 * new and just created, it must also be partitioned and formatted.
\r
75 static FF_Error_t prvPartitionAndFormatDisk( FF_Disk_t *pxDisk );
\r
77 /*-----------------------------------------------------------*/
\r
79 /* This is the prototype of the function used to initialise the RAM disk driver.
\r
80 Other media drivers do not have to have the same prototype.
\r
83 + pcName is the name to give the disk within FreeRTOS+FAT's virtual file system.
\r
84 + pucDataBuffer is the start of the RAM to use as the disk.
\r
85 + ulSectorCount is effectively the size of the disk, each sector is 512 bytes.
\r
86 + xIOManagerCacheSize is the size of the IO manager's cache, which must be a
\r
87 multiple of the sector size, and at least twice as big as the sector size.
\r
89 FF_Disk_t *FF_RAMDiskInit( char *pcName, uint8_t *pucDataBuffer, uint32_t ulSectorCount, size_t xIOManagerCacheSize )
\r
92 FF_Disk_t *pxDisk = NULL;
\r
93 FF_CreationParameters_t xParameters;
\r
95 /* Check the validity of the xIOManagerCacheSize parameter. */
\r
96 configASSERT( ( xIOManagerCacheSize % ramSECTOR_SIZE ) == 0 );
\r
97 configASSERT( ( xIOManagerCacheSize >= ( 2 * ramSECTOR_SIZE ) ) );
\r
99 /* Attempt to allocated the FF_Disk_t structure. */
\r
100 pxDisk = ( FF_Disk_t * ) pvPortMalloc( sizeof( FF_Disk_t ) );
\r
102 if( pxDisk != NULL )
\r
104 /* Start with every member of the structure set to zero. */
\r
105 memset( pxDisk, '\0', sizeof( FF_Disk_t ) );
\r
107 /* Clear the entire space. */
\r
108 memset( pucDataBuffer, '\0', ulSectorCount * ramSECTOR_SIZE );
\r
110 /* The pvTag member of the FF_Disk_t structure allows the structure to be
\r
111 extended to also include media specific parameters. The only media
\r
112 specific data that needs to be stored in the FF_Disk_t structure for a
\r
113 RAM disk is the location of the RAM buffer itself - so this is stored
\r
114 directly in the FF_Disk_t's pvTag member. */
\r
115 pxDisk->pvTag = ( void * ) pucDataBuffer;
\r
117 /* The signature is used by the disk read and disk write functions to
\r
118 ensure the disk being accessed is a RAM disk. */
\r
119 pxDisk->ulSignature = ramSIGNATURE;
\r
121 /* The number of sectors is recorded for bounds checking in the read and
\r
122 write functions. */
\r
123 pxDisk->ulNumberOfSectors = ulSectorCount;
\r
125 /* Create the IO manager that will be used to control the RAM disk. */
\r
126 memset( &xParameters, '\0', sizeof( xParameters ) );
\r
127 xParameters.pucCacheMemory = NULL;
\r
128 xParameters.ulMemorySize = xIOManagerCacheSize;
\r
129 xParameters.ulSectorSize = ramSECTOR_SIZE;
\r
130 xParameters.fnWriteBlocks = prvWriteRAM;
\r
131 xParameters.fnReadBlocks = prvReadRAM;
\r
132 xParameters.pxDisk = pxDisk;
\r
134 /* Driver is reentrant so xBlockDeviceIsReentrant can be set to pdTRUE.
\r
135 In this case the semaphore is only used to protect FAT data
\r
137 xParameters.pvSemaphore = ( void * ) xSemaphoreCreateRecursiveMutex();
\r
138 xParameters.xBlockDeviceIsReentrant = pdFALSE;
\r
140 pxDisk->pxIOManager = FF_CreateIOManger( &xParameters, &xError );
\r
142 if( ( pxDisk->pxIOManager != NULL ) && ( FF_isERR( xError ) == pdFALSE ) )
\r
144 /* Record that the RAM disk has been initialised. */
\r
145 pxDisk->xStatus.bIsInitialised = pdTRUE;
\r
147 /* Create a partition on the RAM disk. NOTE! The disk is only
\r
148 being partitioned here because it is a new RAM disk. It is
\r
149 known that the disk has not been used before, and cannot already
\r
150 contain any partitions. Most media drivers will not perform
\r
151 this step because the media will have already been partitioned. */
\r
152 xError = prvPartitionAndFormatDisk( pxDisk );
\r
154 if( FF_isERR( xError ) == pdFALSE )
\r
156 /* Record the partition number the FF_Disk_t structure is, then
\r
157 mount the partition. */
\r
158 pxDisk->xStatus.bPartitionNumber = ramPARTITION_NUMBER;
\r
160 /* Mount the partition. */
\r
161 xError = FF_Mount( pxDisk, ramPARTITION_NUMBER );
\r
162 FF_PRINTF( "FF_RAMDiskInit: FF_Mount: %s\n", ( const char * ) FF_GetErrMessage( xError ) );
\r
165 if( FF_isERR( xError ) == pdFALSE )
\r
167 /* The partition mounted successfully, add it to the virtual
\r
168 file system - where it will appear as a directory off the file
\r
169 system's root directory. */
\r
170 FF_FS_Add( pcName, pxDisk );
\r
175 FF_PRINTF( "FF_RAMDiskInit: FF_CreateIOManger: %s\n", ( const char * ) FF_GetErrMessage( xError ) );
\r
177 /* The disk structure was allocated, but the disk's IO manager could
\r
178 not be allocated, so free the disk again. */
\r
179 FF_RAMDiskDelete( pxDisk );
\r
185 FF_PRINTF( "FF_RAMDiskInit: Malloc failed\n" );
\r
190 /*-----------------------------------------------------------*/
\r
192 BaseType_t FF_RAMDiskDelete( FF_Disk_t *pxDisk )
\r
194 if( pxDisk != NULL )
\r
196 pxDisk->ulSignature = 0;
\r
197 pxDisk->xStatus.bIsInitialised = 0;
\r
198 if( pxDisk->pxIOManager != NULL )
\r
200 FF_DeleteIOManager( pxDisk->pxIOManager );
\r
203 vPortFree( pxDisk );
\r
208 /*-----------------------------------------------------------*/
\r
210 static int32_t prvReadRAM( uint8_t *pucDestination, uint32_t ulSectorNumber, uint32_t ulSectorCount, FF_Disk_t *pxDisk )
\r
213 uint8_t *pucSource;
\r
215 if( pxDisk != NULL )
\r
217 if( pxDisk->ulSignature != ramSIGNATURE )
\r
219 /* The disk structure is not valid because it doesn't contain a
\r
220 magic number written to the disk when it was created. */
\r
221 lReturn = FF_ERR_IOMAN_DRIVER_FATAL_ERROR | FF_ERRFLAG;
\r
223 else if( pxDisk->xStatus.bIsInitialised == pdFALSE )
\r
225 /* The disk has not been initialised. */
\r
226 lReturn = FF_ERR_IOMAN_OUT_OF_BOUNDS_WRITE | FF_ERRFLAG;
\r
228 else if( ulSectorNumber >= pxDisk->ulNumberOfSectors )
\r
230 /* The start sector is not within the bounds of the disk. */
\r
231 lReturn = ( FF_ERR_IOMAN_OUT_OF_BOUNDS_WRITE | FF_ERRFLAG );
\r
233 else if( ( pxDisk->ulNumberOfSectors - ulSectorNumber ) < ulSectorCount )
\r
235 /* The end sector is not within the bounds of the disk. */
\r
236 lReturn = ( FF_ERR_IOMAN_OUT_OF_BOUNDS_WRITE | FF_ERRFLAG );
\r
240 /* Obtain the pointer to the RAM buffer being used as the disk. */
\r
241 pucSource = ( uint8_t * ) pxDisk->pvTag;
\r
243 /* Move to the start of the sector being read. */
\r
244 pucSource += ( ramSECTOR_SIZE * ulSectorNumber );
\r
246 /* Copy the data from the disk. As this is a RAM disk this can be
\r
247 done using memcpy(). */
\r
248 memcpy( ( void * ) pucDestination,
\r
249 ( void * ) pucSource,
\r
250 ( size_t ) ( ulSectorCount * ramSECTOR_SIZE ) );
\r
252 lReturn = FF_ERR_NONE;
\r
257 lReturn = FF_ERR_NULL_POINTER | FF_ERRFLAG;
\r
262 /*-----------------------------------------------------------*/
\r
264 static int32_t prvWriteRAM( uint8_t *pucSource, uint32_t ulSectorNumber, uint32_t ulSectorCount, FF_Disk_t *pxDisk )
\r
266 int32_t lReturn = FF_ERR_NONE;
\r
267 uint8_t *pucDestination;
\r
269 if( pxDisk != NULL )
\r
271 if( pxDisk->ulSignature != ramSIGNATURE )
\r
273 /* The disk structure is not valid because it doesn't contain a
\r
274 magic number written to the disk when it was created. */
\r
275 lReturn = FF_ERR_IOMAN_DRIVER_FATAL_ERROR | FF_ERRFLAG;
\r
277 else if( pxDisk->xStatus.bIsInitialised == pdFALSE )
\r
279 /* The disk has not been initialised. */
\r
280 lReturn = FF_ERR_IOMAN_OUT_OF_BOUNDS_WRITE | FF_ERRFLAG;
\r
282 else if( ulSectorNumber >= pxDisk->ulNumberOfSectors )
\r
284 /* The start sector is not within the bounds of the disk. */
\r
285 lReturn = ( FF_ERR_IOMAN_OUT_OF_BOUNDS_WRITE | FF_ERRFLAG );
\r
287 else if( ( pxDisk->ulNumberOfSectors - ulSectorNumber ) < ulSectorCount )
\r
289 /* The end sector is not within the bounds of the disk. */
\r
290 lReturn = ( FF_ERR_IOMAN_OUT_OF_BOUNDS_WRITE | FF_ERRFLAG );
\r
294 /* Obtain the location of the RAM being used as the disk. */
\r
295 pucDestination = ( uint8_t * ) pxDisk->pvTag;
\r
297 /* Move to the sector being written to. */
\r
298 pucDestination += ( ramSECTOR_SIZE * ulSectorNumber );
\r
300 /* Write to the disk. As this is a RAM disk the write can use a
\r
302 memcpy( ( void * ) pucDestination,
\r
303 ( void * ) pucSource,
\r
304 ( size_t ) ulSectorCount * ( size_t ) ramSECTOR_SIZE );
\r
306 lReturn = FF_ERR_NONE;
\r
311 lReturn = FF_ERR_NULL_POINTER | FF_ERRFLAG;
\r
316 /*-----------------------------------------------------------*/
\r
318 static FF_Error_t prvPartitionAndFormatDisk( FF_Disk_t *pxDisk )
\r
320 FF_PartitionParameters_t xPartition;
\r
323 /* Create a single partition that fills all available space on the disk. */
\r
324 memset( &xPartition, '\0', sizeof( xPartition ) );
\r
325 xPartition.ulSectorCount = pxDisk->ulNumberOfSectors;
\r
326 xPartition.ulHiddenSectors = ramHIDDEN_SECTOR_COUNT;
\r
327 xPartition.xPrimaryCount = ramPRIMARY_PARTITIONS;
\r
328 xPartition.eSizeType = eSizeIsQuota;
\r
330 /* Partition the disk */
\r
331 xError = FF_Partition( pxDisk, &xPartition );
\r
332 FF_PRINTF( "FF_Partition: %s\n", ( const char * ) FF_GetErrMessage( xError ) );
\r
334 if( FF_isERR( xError ) == pdFALSE )
\r
336 /* Format the partition. */
\r
337 xError = FF_Format( pxDisk, ramPARTITION_NUMBER, pdTRUE, pdTRUE );
\r
338 FF_PRINTF( "FF_RAMDiskInit: FF_Format: %s\n", ( const char * ) FF_GetErrMessage( xError ) );
\r
343 /*-----------------------------------------------------------*/
\r
345 BaseType_t FF_RAMDiskShowPartition( FF_Disk_t *pxDisk )
\r
348 uint64_t ullFreeSectors;
\r
349 uint32_t ulTotalSizeKB, ulFreeSizeKB;
\r
350 int iPercentageFree;
\r
351 FF_IOManager_t *pxIOManager;
\r
352 const char *pcTypeName = "unknown type";
\r
353 BaseType_t xReturn = pdPASS;
\r
355 if( pxDisk == NULL )
\r
361 pxIOManager = pxDisk->pxIOManager;
\r
363 FF_PRINTF( "Reading FAT and calculating Free Space\n" );
\r
365 switch( pxIOManager->xPartition.ucType )
\r
368 pcTypeName = "FAT12";
\r
372 pcTypeName = "FAT16";
\r
376 pcTypeName = "FAT32";
\r
380 pcTypeName = "UNKOWN";
\r
384 FF_GetFreeSize( pxIOManager, &xError );
\r
386 ullFreeSectors = pxIOManager->xPartition.ulFreeClusterCount * pxIOManager->xPartition.ulSectorsPerCluster;
\r
387 if( pxIOManager->xPartition.ulDataSectors == ( uint32_t )0 )
\r
389 iPercentageFree = 0;
\r
393 iPercentageFree = ( int ) ( ( ramHUNDRED_64_BIT * ullFreeSectors + pxIOManager->xPartition.ulDataSectors / 2 ) /
\r
394 ( ( uint64_t )pxIOManager->xPartition.ulDataSectors ) );
\r
397 ulTotalSizeKB = pxIOManager->xPartition.ulDataSectors / ramSECTORS_PER_KB;
\r
398 ulFreeSizeKB = ( uint32_t ) ( ullFreeSectors / ramSECTORS_PER_KB );
\r
400 /* It is better not to use the 64-bit format such as %Lu because it
\r
401 might not be implemented. */
\r
402 FF_PRINTF( "Partition Nr %8u\n", pxDisk->xStatus.bPartitionNumber );
\r
403 FF_PRINTF( "Type %8u (%s)\n", pxIOManager->xPartition.ucType, pcTypeName );
\r
404 FF_PRINTF( "VolLabel '%8s' \n", pxIOManager->xPartition.pcVolumeLabel );
\r
405 FF_PRINTF( "TotalSectors %8lu\n", pxIOManager->xPartition.ulTotalSectors );
\r
406 FF_PRINTF( "SecsPerCluster %8lu\n", pxIOManager->xPartition.ulSectorsPerCluster );
\r
407 FF_PRINTF( "Size %8lu KB\n", ulTotalSizeKB );
\r
408 FF_PRINTF( "FreeSize %8lu KB ( %d perc free )\n", ulFreeSizeKB, iPercentageFree );
\r
413 /*-----------------------------------------------------------*/
\r
415 void FF_RAMDiskFlush( FF_Disk_t *pxDisk )
\r
417 if( ( pxDisk != NULL ) && ( pxDisk->xStatus.bIsInitialised != 0 ) && ( pxDisk->pxIOManager != NULL ) )
\r
419 FF_FlushCache( pxDisk->pxIOManager );
\r
422 /*-----------------------------------------------------------*/
\r