blob: 19231393af795d24c9c3c3489e45634c67c4402b [file] [log] [blame]
/*
* 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
*
*/
/* Standard includes. */
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
/* 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_CreateIOManger( &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_CreateIOManger: %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 %8lu\n", pxIOManager->xPartition.ulTotalSectors );
FF_PRINTF( "SecsPerCluster %8lu\n", pxIOManager->xPartition.ulSectorsPerCluster );
FF_PRINTF( "Size %8lu KB\n", ulTotalSizeKB );
FF_PRINTF( "FreeSize %8lu KB ( %d perc free )\n", 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 );
}
}
/*-----------------------------------------------------------*/