5537 lines
183 KiB
C
5537 lines
183 KiB
C
// DOM-IGNORE-BEGIN
|
|
/*******************************************************************************
|
|
Copyright 2015 Microchip Technology Inc. (www.microchip.com)
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
|
|
To request to license the code under the MLA license (www.microchip.com/mla_license),
|
|
please contact mla_licensing@microchip.com
|
|
*******************************************************************************/
|
|
//DOM-IGNORE-END
|
|
|
|
#include "FileSystem_config.h"
|
|
//#include "system.h"
|
|
#include "fileio_lfn.h"
|
|
#include "fileio_private_lfn.h"
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include "../SDCardMgr.h"
|
|
#include "fileio_driver.h"
|
|
#include "sd_spi.h"
|
|
#include "fileio.h"
|
|
|
|
uint32_t ReadRam32bit(uint8_t * pBuffer, uint16_t index);
|
|
uint16_t ReadRam16bit (uint8_t * pBuffer, uint16_t index);
|
|
|
|
/*****************************************************************************/
|
|
/* Global Variables */
|
|
/*****************************************************************************/
|
|
|
|
FILEIO_DRIVE gDriveArray[FILEIO_CONFIG_MAX_DRIVES];
|
|
uint8_t gDriveSlotOpen[FILEIO_CONFIG_MAX_DRIVES];
|
|
|
|
//FILEIO_TimestampGet timestampGet;
|
|
|
|
uint16_t lfnBuffer[256];
|
|
|
|
#if defined (__XC16__) || defined (__XC32__)
|
|
#if defined (FILEIO_CONFIG_MULTIPLE_BUFFER_MODE_DISABLE)
|
|
uint8_t __attribute__ ((aligned(4))) gDataBuffer[FILEIO_CONFIG_MEDIA_SECTOR_SIZE]; // The global data sector buffer
|
|
uint8_t __attribute__ ((aligned(4))) gFATBuffer[FILEIO_CONFIG_MEDIA_SECTOR_SIZE]; // The global FAT sector buffer
|
|
FILEIO_BUFFER_STATUS bufferStatus; // Status of the buffer contents (and buffer ownership)
|
|
#else
|
|
uint8_t __attribute__ ((aligned(4))) gDataBuffer[FILEIO_CONFIG_MAX_DRIVES][FILEIO_CONFIG_MEDIA_SECTOR_SIZE]; // The global data sector buffer
|
|
uint8_t __attribute__ ((aligned(4))) gFATBuffer[FILEIO_CONFIG_MAX_DRIVES][FILEIO_CONFIG_MEDIA_SECTOR_SIZE]; // The global FAT sector buffer
|
|
FILEIO_BUFFER_STATUS bufferStatus[FILEIO_CONFIG_MAX_DRIVES];
|
|
#endif
|
|
#else
|
|
#if defined (FILEIO_CONFIG_MULTIPLE_BUFFER_MODE_DISABLE)
|
|
uint8_t gDataBuffer[FILEIO_CONFIG_MEDIA_SECTOR_SIZE]; // The global data sector buffer
|
|
uint8_t gFATBuffer[FILEIO_CONFIG_MEDIA_SECTOR_SIZE]; // The global FAT sector buffer
|
|
FILEIO_BUFFER_STATUS bufferStatus; // Status of the buffer contents (and buffer ownership)
|
|
#else
|
|
uint8_t gDataBuffer[FILEIO_CONFIG_MAX_DRIVES][FILEIO_CONFIG_MEDIA_SECTOR_SIZE]; // The global data sector buffer
|
|
uint8_t gFATBuffer[FILEIO_CONFIG_MAX_DRIVES][FILEIO_CONFIG_MEDIA_SECTOR_SIZE]; // The global FAT sector buffer
|
|
FILEIO_BUFFER_STATUS bufferStatus[FILEIO_CONFIG_MAX_DRIVES];
|
|
#endif
|
|
#endif
|
|
|
|
struct
|
|
{
|
|
FILEIO_DIRECTORY currentWorkingDirectory;
|
|
} globalParameters;
|
|
|
|
/************************************************************************************/
|
|
/* Prototypes */
|
|
/************************************************************************************/
|
|
|
|
//void FILEIO_RegisterTimestampGet (FILEIO_TimestampGet timestampFunction)
|
|
//{
|
|
// timestampGet = timestampFunction;
|
|
//}
|
|
|
|
int FILEIO_Initialize (void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < FILEIO_CONFIG_MAX_DRIVES; i++)
|
|
{
|
|
gDriveSlotOpen[i] = true;
|
|
#if defined (FILEIO_CONFIG_MULTIPLE_BUFFER_MODE_DISABLE)
|
|
gDriveArray[i].dataBuffer = &gDataBuffer[0];
|
|
gDriveArray[i].fatBuffer = &gFATBuffer[0];
|
|
gDriveArray[i].bufferStatusPtr = &bufferStatus;
|
|
#else
|
|
gDriveArray[i].dataBuffer = &gDataBuffer[i][0];
|
|
gDriveArray[i].fatBuffer = &gFATBuffer[i][0];
|
|
gDriveArray[i].bufferStatusPtr = &bufferStatus[i];
|
|
bufferStatus[i].flags.dataBufferNeedsWrite = false;
|
|
bufferStatus[i].flags.fatBufferNeedsWrite = false;
|
|
bufferStatus[i].dataBufferCachedSector = 0xFFFFFFFF;
|
|
bufferStatus[i].fatBufferCachedSector = 0xFFFFFFFF;
|
|
#endif
|
|
}
|
|
|
|
#if defined (FILEIO_CONFIG_MULTIPLE_BUFFER_MODE_DISABLE)
|
|
bufferStatus.driveOwner = NULL;
|
|
bufferStatus.flags.dataBufferNeedsWrite = false;
|
|
bufferStatus.flags.fatBufferNeedsWrite = false;
|
|
bufferStatus.dataBufferCachedSector = 0xFFFFFFFF;
|
|
bufferStatus.fatBufferCachedSector = 0xFFFFFFFF;
|
|
#endif
|
|
|
|
globalParameters.currentWorkingDirectory.drive = 0;
|
|
globalParameters.currentWorkingDirectory.cluster = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
int FILEIO_Reinitialize (void)
|
|
{
|
|
return FILEIO_Initialize();
|
|
}
|
|
|
|
bool FILEIO_MediaDetect (const FILEIO_DRIVE_CONFIG * driveConfig, void * mediaParameters)
|
|
{
|
|
return FILEIO_DRIVER_MediaDetect(0);
|
|
// return (*driveConfig->funcMediaDetect)(mediaParameters);
|
|
}
|
|
|
|
FILEIO_DRIVE * FILEIO_CharToDrive (uint16_t driveId)
|
|
{
|
|
uint8_t i;
|
|
|
|
for (i = 0; i < FILEIO_CONFIG_MAX_DRIVES; i++)
|
|
{
|
|
if ((gDriveSlotOpen[i] == false) && (gDriveArray[i].driveId == driveId))
|
|
{
|
|
return &gDriveArray[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
FILEIO_FILE_SYSTEM_TYPE FILEIO_FileSystemTypeGet (uint16_t driveId)
|
|
{
|
|
FILEIO_DRIVE * drive = FILEIO_CharToDrive (driveId);
|
|
|
|
if (drive == NULL)
|
|
{
|
|
return FILEIO_FILE_SYSTEM_TYPE_NONE;
|
|
}
|
|
|
|
return drive->type;
|
|
}
|
|
|
|
FILEIO_ERROR_TYPE FILEIO_DriveMount (uint16_t driveId, const FILEIO_DRIVE_CONFIG * driveConfig, void * mediaParameters)
|
|
{
|
|
FILEIO_ERROR_TYPE error = FILEIO_ERROR_NONE;
|
|
FILEIO_DRIVE * drive;
|
|
uint8_t i;
|
|
FILEIO_MEDIA_INFORMATION * mediaInformation;
|
|
|
|
drive = FILEIO_CharToDrive(driveId);
|
|
|
|
if (drive == NULL)
|
|
{
|
|
for (i = 0; i < FILEIO_CONFIG_MAX_DRIVES; i++)
|
|
{
|
|
if (gDriveSlotOpen[i])
|
|
{
|
|
gDriveSlotOpen[i] = false;
|
|
gDriveArray[i].driveId = driveId;
|
|
gDriveArray[i].driveConfig = driveConfig;
|
|
drive = &gDriveArray[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (drive == NULL)
|
|
{
|
|
return FILEIO_ERROR_TOO_MANY_DRIVES_OPEN;
|
|
}
|
|
|
|
// Reinitialize the drive cache information
|
|
// This will force the library to recache sectors if a drive is unmounted and re-mounted
|
|
#if defined (FILEIO_CONFIG_MULTIPLE_BUFFER_MODE_DISABLE)
|
|
if (drive->bufferStatusPtr->driveOwner == drive)
|
|
{
|
|
drive->bufferStatusPtr->driveOwner = NULL;
|
|
}
|
|
#else
|
|
bufferStatus[i].flags.dataBufferNeedsWrite = false;
|
|
bufferStatus[i].flags.fatBufferNeedsWrite = false;
|
|
bufferStatus[i].dataBufferCachedSector = 0xFFFFFFFF;
|
|
bufferStatus[i].fatBufferCachedSector = 0xFFFFFFFF;
|
|
#endif
|
|
|
|
#if defined (FILEIO_CONFIG_MULTIPLE_BUFFER_MODE_DISABLE)
|
|
if (FILEIO_GetSingleBuffer (drive) != FILEIO_RESULT_SUCCESS)
|
|
{
|
|
return FILEIO_ERROR_WRITE;
|
|
}
|
|
#endif
|
|
|
|
drive->mediaParameters = mediaParameters;
|
|
|
|
// if(driveConfig->funcIOInit != NULL)
|
|
// {
|
|
// (*driveConfig->funcIOInit)(mediaParameters);
|
|
// }
|
|
FILEIO_DRIVER_IOInitialize(drive->driveId);
|
|
|
|
|
|
mediaInformation = FILEIO_DRIVER_MediaInitialize(drive->driveId);//(*driveConfig->funcMediaInit)(mediaParameters);
|
|
if (mediaInformation->errorCode != MEDIA_NO_ERROR)
|
|
{
|
|
error = FILEIO_ERROR_INIT_ERROR;
|
|
}
|
|
else
|
|
{
|
|
if (mediaInformation->validityFlags.bits.sectorSize)
|
|
{
|
|
drive->sectorSize = mediaInformation->sectorSize;
|
|
if (mediaInformation->sectorSize > FILEIO_CONFIG_MEDIA_SECTOR_SIZE)
|
|
{
|
|
error = FILEIO_ERROR_UNSUPPORTED_SECTOR_SIZE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Load the Master Boot Record (partition)
|
|
if (error == FILEIO_ERROR_NONE)
|
|
{
|
|
if ((error = FILEIO_LoadMBR (drive)) == FILEIO_ERROR_NONE)
|
|
{
|
|
// Load the boot sector
|
|
error = FILEIO_LoadBootSector(drive);
|
|
}
|
|
}
|
|
|
|
if (error == FILEIO_ERROR_NONE)
|
|
{
|
|
// If this is the first drive we're mounting, set its root as the current working directory
|
|
if (globalParameters.currentWorkingDirectory.drive == 0)
|
|
{
|
|
globalParameters.currentWorkingDirectory.drive = drive;
|
|
globalParameters.currentWorkingDirectory.cluster = drive->firstRootCluster;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gDriveArray[i].driveId = 0;
|
|
gDriveSlotOpen[i] = true;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
FILEIO_ERROR_TYPE FILEIO_LoadMBR (FILEIO_DRIVE * drive)
|
|
{
|
|
FILEIO_MASTER_BOOT_RECORD * ptrMbr;
|
|
uint8_t type;
|
|
bool hasMbr = true;
|
|
FILEIO_ERROR_TYPE error = FILEIO_ERROR_NONE;
|
|
FILEIO_BOOT_SECTOR * ptrBootSector;
|
|
|
|
// Get the partition table from the MBR
|
|
//if ( (*drive->driveConfig->funcSectorRead) (drive->mediaParameters, FILEIO_MEDIA_SECTOR_MBR, drive->dataBuffer) != true)
|
|
if(FILEIO_DRIVER_SectorRead(drive->driveId, FILEIO_MEDIA_SECTOR_MBR, drive->dataBuffer) != true)
|
|
{
|
|
error = FILEIO_ERROR_BAD_SECTOR_READ;
|
|
}
|
|
else
|
|
{
|
|
drive->bufferStatusPtr->dataBufferCachedSector = FILEIO_MEDIA_SECTOR_MBR;
|
|
// Check if the card has no MBR
|
|
ptrBootSector = (FILEIO_BOOT_SECTOR *)drive->dataBuffer;
|
|
|
|
if ((ptrBootSector->signature0 == FILEIO_FAT_GOOD_SIGN_0) && (ptrBootSector->signature1 == FILEIO_FAT_GOOD_SIGN_1))
|
|
{
|
|
// Technically, the OEM name is not for indication
|
|
// The alternative is to read the CIS from attribute
|
|
// memory. See the PCMCIA metaformat for more details
|
|
/* #if defined (__XC16__) || defined (__XC32__)
|
|
if ((*(drive->dataBuffer + BSI_FSTYPE) == 'F') && \
|
|
(*(drive->dataBuffer + BSI_FSTYPE + 1) == 'A') && \
|
|
(*(drive->dataBuffer + BSI_FSTYPE + 2) == 'T') && \
|
|
(*(drive->dataBuffer + BSI_FSTYPE + 3) == '1') && \
|
|
(*(drive->dataBuffer + BSI_BOOTSIG) == 0x29))
|
|
#else*/
|
|
if ((ptrBootSector->biosParameterBlock.fat16.fileSystemType[0] == 'F') && \
|
|
(ptrBootSector->biosParameterBlock.fat16.fileSystemType[1] == 'A') && \
|
|
(ptrBootSector->biosParameterBlock.fat16.fileSystemType[2] == 'T') && \
|
|
(ptrBootSector->biosParameterBlock.fat16.fileSystemType[3] == '1') && \
|
|
(ptrBootSector->biosParameterBlock.fat16.bootSignature == 0x29))
|
|
// #endif
|
|
{
|
|
drive->firstPartitionSector = 0;
|
|
drive->type = FILEIO_FILE_SYSTEM_TYPE_FAT16;
|
|
hasMbr = false;
|
|
}
|
|
else
|
|
{
|
|
#if defined (__XC16__) || defined (__XC32__)
|
|
if ((*(drive->dataBuffer + BSI_FAT32_FSTYPE ) == 'F') && \
|
|
(*(drive->dataBuffer + BSI_FAT32_FSTYPE + 1 ) == 'A') && \
|
|
(*(drive->dataBuffer + BSI_FAT32_FSTYPE + 2 ) == 'T') && \
|
|
(*(drive->dataBuffer + BSI_FAT32_FSTYPE + 3 ) == '3') && \
|
|
(*(drive->dataBuffer + BSI_FAT32_BOOTSIG) == 0x29))
|
|
#else
|
|
if ((ptrBootSector->biosParameterBlock.fat32.fileSystemType[0] == 'F') && \
|
|
(ptrBootSector->biosParameterBlock.fat32.fileSystemType[1] == 'A') && \
|
|
(ptrBootSector->biosParameterBlock.fat32.fileSystemType[2] == 'T') && \
|
|
(ptrBootSector->biosParameterBlock.fat32.fileSystemType[3] == '3') && \
|
|
(ptrBootSector->biosParameterBlock.fat32.bootSignature == 0x29))
|
|
#endif
|
|
{
|
|
drive->firstPartitionSector = 0;
|
|
drive->type = FILEIO_FILE_SYSTEM_TYPE_FAT32;
|
|
hasMbr = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the first sector isn't a boot sector, parse the actual MBR
|
|
if (hasMbr)
|
|
{
|
|
// assign it the partition table structure
|
|
ptrMbr = (FILEIO_MASTER_BOOT_RECORD *)drive->dataBuffer;
|
|
|
|
// Ensure its good
|
|
if((ptrMbr->signature0 != FILEIO_FAT_GOOD_SIGN_0) || (ptrMbr->signature1 != FILEIO_FAT_GOOD_SIGN_1))
|
|
{
|
|
error = FILEIO_ERROR_BAD_PARTITION;
|
|
}
|
|
else
|
|
{
|
|
uint8_t j;
|
|
FILEIO_MBR_PARTITION_TABLE_ENTRY * ptrEntry = &ptrMbr->partition0;
|
|
|
|
drive->type = FILEIO_FILE_SYSTEM_TYPE_NONE;
|
|
for(j = 0; j < 4; j++)
|
|
{
|
|
/* Valid Master Boot Record Loaded */
|
|
|
|
// Get the 32 bit offset to the first partition
|
|
drive->firstPartitionSector = ptrEntry->lbaFirstSector;
|
|
|
|
// check if the partition type is acceptable
|
|
type = ptrEntry->fileSystemDescriptor;
|
|
|
|
switch (type)
|
|
{
|
|
case 0x01:
|
|
drive->type = FILEIO_FILE_SYSTEM_TYPE_FAT12;
|
|
break;
|
|
case 0x04:
|
|
case 0x06:
|
|
case 0x0E:
|
|
drive->type = FILEIO_FILE_SYSTEM_TYPE_FAT16;
|
|
break;
|
|
case 0x0B:
|
|
case 0x0C:
|
|
drive->type = FILEIO_FILE_SYSTEM_TYPE_FAT32; // FAT32 is supported too
|
|
break;
|
|
} // switch
|
|
|
|
if (drive->type != FILEIO_FILE_SYSTEM_TYPE_NONE)
|
|
{
|
|
break;
|
|
}
|
|
|
|
/* If we are here, we didn't find a matching partition. We
|
|
should increment to the next partition table entry */
|
|
ptrEntry++;
|
|
}
|
|
|
|
if (drive->type == FILEIO_FILE_SYSTEM_TYPE_NONE)
|
|
{
|
|
error = FILEIO_ERROR_UNSUPPORTED_FS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
FILEIO_ERROR_TYPE FILEIO_LoadBootSector (FILEIO_DRIVE * drive)
|
|
{
|
|
FILEIO_BOOT_SECTOR * ptrBootSector;
|
|
FILEIO_ERROR_TYPE error = FILEIO_ERROR_NONE;
|
|
uint32_t rootDirectorySectors;
|
|
uint32_t totalSectors;
|
|
uint32_t dataSectors;
|
|
uint16_t bytesPerSector;
|
|
uint16_t reservedSectorCount;
|
|
bool triedSpecifiedBackupBootSec = false;
|
|
bool triedBackupBootSecAtAddress6 = false;
|
|
|
|
// Get the Boot sector
|
|
//if ( (*drive->driveConfig->funcSectorRead) (drive->mediaParameters, drive->firstPartitionSector, drive->dataBuffer) != true)
|
|
if (FILEIO_DRIVER_SectorRead(drive->driveId, drive->firstPartitionSector, drive->dataBuffer) != true)
|
|
{
|
|
error = FILEIO_ERROR_BAD_SECTOR_READ;
|
|
}
|
|
else
|
|
{
|
|
drive->bufferStatusPtr->dataBufferCachedSector = drive->firstPartitionSector;
|
|
ptrBootSector = (FILEIO_BOOT_SECTOR *)drive->dataBuffer;
|
|
|
|
do //test each possible boot sector (FAT32 can have backup boot sectors)
|
|
{
|
|
|
|
//Verify the Boot Sector has a valid signature
|
|
if((ptrBootSector->signature0 != FILEIO_FAT_GOOD_SIGN_0) || (ptrBootSector->signature1 != FILEIO_FAT_GOOD_SIGN_1))
|
|
{
|
|
error = FILEIO_ERROR_NOT_FORMATTED;
|
|
}
|
|
else
|
|
{
|
|
do //loop just to allow a break to jump out of this section of code
|
|
{
|
|
#ifdef __XC8__
|
|
// Load count of sectors per cluster
|
|
drive->sectorsPerCluster = ptrBootSector->biosParameterBlock.fat16.sectorsPerCluster;
|
|
// Load the sector number of the first FAT sector
|
|
drive->firstFatSector = drive->firstPartitionSector + ptrBootSector->biosParameterBlock.fat16.reservedSectorCount;
|
|
// Load the count of FAT tables
|
|
drive->fatCopyCount = ptrBootSector->biosParameterBlock.fat16.fatCount;
|
|
// Load the size of the FATs
|
|
drive->fatSectorCount = ptrBootSector->biosParameterBlock.fat16.sectorsPerFat;
|
|
if(drive->fatSectorCount == 0)
|
|
{
|
|
drive->fatSectorCount = ptrBootSector->biosParameterBlock.fat32.sectorsPerFat32;
|
|
}
|
|
// Calculate the location of the root sector (for FAT12/16)
|
|
drive->firstRootSector = drive->firstFatSector + (uint32_t)(drive->fatCopyCount * (uint32_t)drive->fatSectorCount);
|
|
// Determine the max size of the root (will be 0 for FAT32)
|
|
drive->rootDirectoryEntryCount = ptrBootSector->biosParameterBlock.fat16.rootDirectoryEntryCount;
|
|
|
|
// Determine the total number of sectors in the partition
|
|
if(ptrBootSector->biosParameterBlock.fat16.totalSectors16 != 0)
|
|
{
|
|
totalSectors = ptrBootSector->biosParameterBlock.fat16.totalSectors16;
|
|
}
|
|
else
|
|
{
|
|
totalSectors = ptrBootSector->biosParameterBlock.fat16.totalSectors32;
|
|
}
|
|
|
|
// Calculate the number of bytes in each sector
|
|
bytesPerSector = ptrBootSector->biosParameterBlock.fat16.bytesPerSector;
|
|
if( (bytesPerSector == 0) || ((bytesPerSector & 1) == 1) )
|
|
{
|
|
error = FILEIO_ERROR_UNSUPPORTED_SECTOR_SIZE;
|
|
break; //break out of the do while loop
|
|
}
|
|
|
|
// Calculate the number of sectors in the root (will be 0 for FAT32)
|
|
rootDirectorySectors = ((ptrBootSector->biosParameterBlock.fat16.rootDirectoryEntryCount * 32) + (ptrBootSector->biosParameterBlock.fat16.bytesPerSector - 1)) / ptrBootSector->biosParameterBlock.fat16.bytesPerSector;
|
|
// Calculate the number of data sectors on the card
|
|
dataSectors = totalSectors - (drive->firstRootSector + rootDirectorySectors);
|
|
// Calculate the maximum number of clusters on the card
|
|
drive->partitionClusterCount = dataSectors / drive->sectorsPerCluster;
|
|
|
|
#else // PIC24/30/33
|
|
|
|
// Read the count of reserved sectors
|
|
reservedSectorCount = ReadRam16bit(drive->dataBuffer, BSI_RESRVSEC);
|
|
// Load the count of sectors per cluster
|
|
drive->sectorsPerCluster = *(drive->dataBuffer + BSI_SPC);
|
|
// Load the sector number of the first FAT sector
|
|
drive->firstFatSector = drive->firstPartitionSector + reservedSectorCount;
|
|
// Load the count of FAT tables
|
|
drive->fatCopyCount = *(drive->dataBuffer + BSI_FATCOUNT);
|
|
// Load the size of the FATs
|
|
drive->fatSectorCount = ReadRam16bit (drive->dataBuffer, BSI_SPF);
|
|
if(drive->fatSectorCount == 0)
|
|
{
|
|
drive->fatSectorCount = ReadRam32bit (drive->dataBuffer, BSI_FATSZ32);
|
|
}
|
|
// Calculate the location of the root sector (for FAT12/16)
|
|
drive->firstRootSector = drive->firstFatSector + (uint32_t)(drive->fatCopyCount * (uint32_t)drive->fatSectorCount);
|
|
// Determine the max size of the root (will be 0 for FAT32)
|
|
drive->rootDirectoryEntryCount = ReadRam16bit (drive->dataBuffer, BSI_ROOTDIRENTS);
|
|
|
|
// Determine the total number of sectors in the partition
|
|
totalSectors = ReadRam16bit (drive->dataBuffer, BSI_TOTSEC16);
|
|
if(totalSectors == 0 )
|
|
{
|
|
totalSectors = ReadRam32bit (drive->dataBuffer, BSI_TOTSEC32);
|
|
}
|
|
|
|
// Calculate the number of uint8_ts in each sector
|
|
bytesPerSector = ReadRam16bit (drive->dataBuffer, BSI_BPS);
|
|
if((bytesPerSector == 0) || ((bytesPerSector & 1) == 1))
|
|
{
|
|
error = FILEIO_ERROR_UNSUPPORTED_SECTOR_SIZE;
|
|
break;
|
|
}
|
|
|
|
// Calculate the number of sectors in the root (will be 0 for FAT32)
|
|
rootDirectorySectors = ((drive->rootDirectoryEntryCount * FILEIO_DIRECTORY_ENTRY_SIZE) + (bytesPerSector - 1)) / bytesPerSector;
|
|
// Calculate the number of data sectors on the card
|
|
dataSectors = totalSectors - (reservedSectorCount + (drive->fatCopyCount * drive->fatSectorCount) + rootDirectorySectors);
|
|
// Calculate the maximum number of clusters on the card
|
|
drive->partitionClusterCount = dataSectors / drive->sectorsPerCluster;
|
|
|
|
#endif
|
|
|
|
// Determine the file system type based on the number of clusters used
|
|
if(drive->partitionClusterCount < 4085)
|
|
{
|
|
drive->type = FILEIO_FILE_SYSTEM_TYPE_FAT12;
|
|
}
|
|
else
|
|
{
|
|
if(drive->partitionClusterCount < 65525)
|
|
{
|
|
drive->type = FILEIO_FILE_SYSTEM_TYPE_FAT16;
|
|
}
|
|
else
|
|
{
|
|
drive->type = FILEIO_FILE_SYSTEM_TYPE_FAT32;
|
|
}
|
|
}
|
|
|
|
if (drive->type == FILEIO_FILE_SYSTEM_TYPE_FAT32)
|
|
{
|
|
#ifdef __XC8__
|
|
drive->firstRootCluster = ptrBootSector->biosParameterBlock.fat32.firstClusterRootDirectory;
|
|
#else
|
|
drive->firstRootCluster = ReadRam32bit (drive->dataBuffer, BSI_ROOTCLUS);
|
|
#endif
|
|
drive->firstDataSector = drive->firstRootSector + rootDirectorySectors;
|
|
}
|
|
else
|
|
{
|
|
drive->firstRootCluster = 0;
|
|
drive->firstDataSector = drive->firstRootSector + (drive->rootDirectoryEntryCount >> 4);
|
|
}
|
|
|
|
if(bytesPerSector > FILEIO_CONFIG_MEDIA_SECTOR_SIZE)
|
|
{
|
|
error = FILEIO_ERROR_UNSUPPORTED_SECTOR_SIZE;
|
|
}
|
|
|
|
}while(0); // do/while loop designed to allow to break out if
|
|
// there is an error detected without returning
|
|
// from the function.
|
|
|
|
}
|
|
|
|
if ((drive->type == FILEIO_FILE_SYSTEM_TYPE_FAT32) || ((error != FILEIO_ERROR_NONE) &&
|
|
((ptrBootSector->biosParameterBlock.fat32.bootSignature == 0x29) ||
|
|
(ptrBootSector->biosParameterBlock.fat32.bootSignature == 0x28))))
|
|
{
|
|
//Check for possible errors in the formatting
|
|
if((ptrBootSector->biosParameterBlock.fat32.totalSectors16 != 0)
|
|
|| ((ptrBootSector->biosParameterBlock.fat32.bootSignature != 0x29) &&
|
|
(ptrBootSector->biosParameterBlock.fat32.bootSignature != 0x28))
|
|
)
|
|
{
|
|
error = FILEIO_ERROR_NOT_FORMATTED;
|
|
}
|
|
|
|
//If there were formatting errors then in FAT32 we can try to use
|
|
// the backup boot sector
|
|
if((error != FILEIO_ERROR_NONE) && (triedSpecifiedBackupBootSec == false))
|
|
{
|
|
triedSpecifiedBackupBootSec = true;
|
|
|
|
// if ((*drive->driveConfig->funcSectorRead) (drive->mediaParameters, drive->firstPartitionSector + ptrBootSector->biosParameterBlock.fat32.backupBootSector, drive->dataBuffer) != true)
|
|
if (FILEIO_DRIVER_SectorRead(drive->driveId, drive->firstPartitionSector + ptrBootSector->biosParameterBlock.fat32.backupBootSector, drive->dataBuffer) != true)
|
|
{
|
|
error = FILEIO_ERROR_BAD_SECTOR_READ;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
error = FILEIO_ERROR_NONE;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if((error != FILEIO_ERROR_NONE) && (triedBackupBootSecAtAddress6 == false))
|
|
{
|
|
triedBackupBootSecAtAddress6 = true;
|
|
|
|
//Here we are using the magic number 6 because the FAT32 specification
|
|
// recommends that "No value other than 6 is recommended." We've
|
|
// already tried using the value specified in the BPB_BkBootSec
|
|
// field and it must have failed
|
|
// if ((*drive->driveConfig->funcSectorRead) (drive->mediaParameters, drive->firstPartitionSector + 6, drive->dataBuffer) != true)
|
|
if (FILEIO_DRIVER_SectorRead(drive->driveId, drive->firstPartitionSector + 6, drive->dataBuffer) != true)
|
|
{
|
|
error = FILEIO_ERROR_BAD_SECTOR_READ;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
error = FILEIO_ERROR_NONE;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
} //type == FAT32
|
|
break;
|
|
}
|
|
while(1);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
int FILEIO_DriveUnmount (const uint16_t driveId)
|
|
{
|
|
FILEIO_DRIVE * drive;
|
|
uint8_t i;
|
|
|
|
drive = FILEIO_CharToDrive (driveId);
|
|
|
|
if (drive == NULL)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
else
|
|
{
|
|
#if defined (FILEIO_CONFIG_MULTIPLE_BUFFER_MODE_DISABLE)
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
if (drive->bufferStatusPtr->driveOwner == drive)
|
|
{
|
|
FILEIO_FlushBuffer (drive, FILEIO_BUFFER_FAT);
|
|
FILEIO_FlushBuffer (drive, FILEIO_BUFFER_DATA);
|
|
drive->bufferStatusPtr->driveOwner = NULL;
|
|
}
|
|
#endif
|
|
#else
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
FILEIO_FlushBuffer (drive, FILEIO_BUFFER_FAT);
|
|
FILEIO_FlushBuffer (drive, FILEIO_BUFFER_DATA);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
FILEIO_DRIVER_MediaDeinitialize(driveId);
|
|
// drive->driveConfig->funcMediaDeinit(drive->mediaParameters);
|
|
|
|
for (i = 0; i < FILEIO_CONFIG_MAX_DRIVES; i++)
|
|
{
|
|
if (gDriveArray[i].driveId == driveId)
|
|
{
|
|
gDriveSlotOpen[i] = true;
|
|
gDriveArray[i].mount = false;
|
|
gDriveArray[i].driveId = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if (globalParameters.currentWorkingDirectory.drive == drive)
|
|
{
|
|
globalParameters.currentWorkingDirectory.cluster = 0;
|
|
globalParameters.currentWorkingDirectory.drive = NULL;
|
|
}
|
|
|
|
return FILEIO_RESULT_SUCCESS;
|
|
}
|
|
|
|
const uint16_t gShortFileNameCharacters[17] =
|
|
{
|
|
'!', '#', '$', '%', '&', '\'', '(', ')', '-', '@', '^', '_', '`', '{', '}', '~', ' '
|
|
};
|
|
|
|
const uint16_t gLongFileNameCharacters[9] =
|
|
{
|
|
'\\', '/', ':', '*', '?', '"', '<', '>', '|'
|
|
};
|
|
|
|
|
|
uint8_t FILEIO_FileNameTypeGet(const uint16_t * fileName, bool partialStringSearch)
|
|
{
|
|
uint16_t c;
|
|
uint8_t i;
|
|
FILEIO_NAME_TYPE type = FILEIO_NAME_SHORT;
|
|
bool foundRadix = false;
|
|
uint8_t count = 0;
|
|
|
|
if (*fileName == '.')
|
|
{
|
|
c = *(fileName + 1);
|
|
if ((c == 0) || (c == FILEIO_CONFIG_DELIMITER))
|
|
{
|
|
return FILEIO_NAME_DOT;
|
|
}
|
|
if (c == '.')
|
|
{
|
|
c = *(fileName + 2);
|
|
if ((c == 0) || (c == FILEIO_CONFIG_DELIMITER))
|
|
{
|
|
return FILEIO_NAME_DOT;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Iterate though the whole file name
|
|
while (((c = (uint8_t)*fileName++) != 0) && (c != FILEIO_CONFIG_DELIMITER))
|
|
{
|
|
if (type == FILEIO_NAME_SHORT)
|
|
{
|
|
count++;
|
|
if (!(((c >= 'A') && (c <= 'Z')) ||
|
|
((c >= '0') && (c <= '9')) ||
|
|
(c >= 128)))
|
|
{
|
|
if (c == '.')
|
|
{
|
|
if (!foundRadix)
|
|
{
|
|
foundRadix = true;
|
|
count = 0;
|
|
}
|
|
else
|
|
{
|
|
type = FILEIO_NAME_LONG;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// C could still be a special character
|
|
for (i = 0; i < sizeof (gShortFileNameCharacters) / 2; i++)
|
|
{
|
|
if (c == gShortFileNameCharacters[i])
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (i == sizeof (gShortFileNameCharacters) / 2)
|
|
{
|
|
if (!partialStringSearch || ((c != '?') && (c != '*')))
|
|
{
|
|
// Switch the type to long
|
|
type = FILEIO_NAME_LONG;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (foundRadix)
|
|
{
|
|
if (count > 3)
|
|
{
|
|
type = FILEIO_NAME_LONG;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (count > 8)
|
|
{
|
|
type = FILEIO_NAME_LONG;
|
|
}
|
|
}
|
|
}
|
|
if (type == FILEIO_NAME_LONG)
|
|
{
|
|
for (i = 0; i < sizeof (gLongFileNameCharacters) / 2; i++)
|
|
{
|
|
if (c == gLongFileNameCharacters[i])
|
|
{
|
|
if (!partialStringSearch || ((c != '?') && (c != '*')))
|
|
{
|
|
type = FILEIO_NAME_INVALID;
|
|
return type;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return type;
|
|
}
|
|
|
|
void FILEIO_FormatShortFileName (const uint16_t * fileName, FILEIO_OBJECT * filePtr)
|
|
{
|
|
uint16_t c;
|
|
uint8_t i = 0;
|
|
|
|
while (((c = *fileName++) != '.') && (c != 0) && (c != FILEIO_CONFIG_DELIMITER))
|
|
{
|
|
filePtr->name[i++] = c;
|
|
}
|
|
|
|
while (i < 8)
|
|
{
|
|
filePtr->name[i++] = 0x20;
|
|
}
|
|
|
|
if (c == '.')
|
|
{
|
|
while ((c = *fileName++) != 0)
|
|
{
|
|
filePtr->name[i++] = c;
|
|
}
|
|
}
|
|
|
|
while (i < 11)
|
|
{
|
|
filePtr->name[i++] = 0x20;
|
|
}
|
|
|
|
filePtr->lfnPtr = NULL;
|
|
}
|
|
|
|
|
|
int FILEIO_Open (FILEIO_OBJECT * filePtr, const uint16_t * fileName, uint16_t mode)
|
|
{
|
|
FILEIO_ERROR_TYPE error;
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
uint16_t entryHandle;
|
|
#endif
|
|
FILEIO_DIRECTORY directory;
|
|
uint8_t fileNameType;
|
|
uint32_t currentCluster;
|
|
uint16_t currentClusterOffset = 0;
|
|
|
|
fileName = FILEIO_CacheDirectory (&directory, (uint16_t *)fileName, false);
|
|
|
|
if (fileName == NULL)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
currentCluster = directory.cluster;
|
|
|
|
#if defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
if (mode & (FILEIO_OPEN_WRITE | FILEIO_OPEN_CREATE | FILEIO_OPEN_TRUNCATE))
|
|
{
|
|
directory.drive->error = FILEIO_ERROR_WRITE_PROTECTED;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
#endif
|
|
|
|
//if((*directory.drive->driveConfig->funcWriteProtectGet)(directory.drive->mediaParameters) && ((mode & (FILEIO_OPEN_WRITE | FILEIO_OPEN_CREATE | FILEIO_OPEN_TRUNCATE)) != 0))
|
|
if((FILEIO_DRIVER_WriteProtectStateGet(directory.drive->driveId) && ((mode & (FILEIO_OPEN_WRITE | FILEIO_OPEN_CREATE | FILEIO_OPEN_TRUNCATE)) != 0)))
|
|
{
|
|
directory.drive->error = FILEIO_ERROR_WRITE_PROTECTED;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
// Check to ensure that a file object was allocated
|
|
if (filePtr == NULL)
|
|
{
|
|
directory.drive->error = FILEIO_ERROR_TOO_MANY_FILES_OPEN;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
fileNameType = FILEIO_FileNameTypeGet(fileName, false);
|
|
|
|
if (fileNameType == FILEIO_NAME_SHORT)
|
|
{
|
|
currentCluster = directory.cluster;
|
|
currentClusterOffset = 0;
|
|
// Short file name
|
|
FILEIO_FormatShortFileName (fileName, filePtr);
|
|
// Search in 'directory' for an entry matching filePtr->name, starting at entry 0 in directory->cluster and returning the result in filePtr
|
|
error = FILEIO_FindShortFileName (&directory, filePtr, (uint8_t *)filePtr->name, ¤tCluster, ¤tClusterOffset, 0, 0, FILEIO_SEARCH_ENTRY_MATCH);
|
|
}
|
|
else if (fileNameType == FILEIO_NAME_LONG)
|
|
{
|
|
// Long file name
|
|
currentCluster = directory.cluster;
|
|
currentClusterOffset = 0;
|
|
// Search in 'directory' for an entry matching fileName, starting at entry 0 in directory->cluster and returning the short file name in filePtr.
|
|
// The long file name will be cached in lfnData
|
|
filePtr->lfnPtr = (uint16_t *)fileName;
|
|
filePtr->lfnLen = FILEIO_strlen16 ((uint16_t *)fileName);
|
|
error = FILEIO_FindLongFileName (&directory, filePtr, ¤tCluster, ¤tClusterOffset, 0, 0, FILEIO_SEARCH_ENTRY_MATCH);
|
|
}
|
|
else
|
|
{
|
|
directory.drive->error = FILEIO_ERROR_INVALID_FILENAME;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
if (error == FILEIO_ERROR_NONE)
|
|
{
|
|
// File was found
|
|
if (((mode & FILEIO_OPEN_TRUNCATE) == FILEIO_OPEN_TRUNCATE) || !FILEIO_IsClusterAllocated(&directory, filePtr))
|
|
{
|
|
entryHandle = filePtr->entry;
|
|
error = FILEIO_EraseFile (filePtr, &entryHandle, true);
|
|
mode |= FILEIO_OPEN_CREATE;
|
|
}
|
|
else
|
|
{
|
|
// Use the CREATE flag to indicate whether the file should be created
|
|
mode &= ~FILEIO_OPEN_CREATE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We couldn't find the file
|
|
if (error == FILEIO_ERROR_DONE)
|
|
{
|
|
// File wasn't found
|
|
if ((mode & FILEIO_OPEN_CREATE) == FILEIO_OPEN_CREATE)
|
|
{
|
|
error = FILEIO_ERROR_NONE;
|
|
filePtr->disk = directory.drive;
|
|
filePtr->baseClusterDir = directory.cluster;
|
|
filePtr->currentClusterDir = directory.cluster;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (error == FILEIO_ERROR_NONE)
|
|
{
|
|
if ((mode & FILEIO_OPEN_CREATE) == FILEIO_OPEN_CREATE)
|
|
{
|
|
// Try to create the file (or replace it, if the file was truncated
|
|
entryHandle = 0;
|
|
|
|
error = FILEIO_DirectoryEntryCreate (filePtr, &entryHandle, 0, true);
|
|
|
|
mode |= FILEIO_OPEN_APPEND;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (error == FILEIO_ERROR_NONE)
|
|
{
|
|
if ((mode & FILEIO_OPEN_READ) == FILEIO_OPEN_READ)
|
|
{
|
|
filePtr->flags.readEnabled = true;
|
|
}
|
|
else
|
|
{
|
|
filePtr->flags.readEnabled = false;
|
|
}
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
if ((mode & FILEIO_OPEN_WRITE) == FILEIO_OPEN_WRITE)
|
|
{
|
|
filePtr->flags.writeEnabled = true;
|
|
}
|
|
else
|
|
{
|
|
filePtr->flags.writeEnabled = false;
|
|
}
|
|
#endif
|
|
|
|
if ((mode & FILEIO_OPEN_APPEND) == FILEIO_OPEN_APPEND)
|
|
{
|
|
int result = FILEIO_Seek (filePtr, 0, FILEIO_SEEK_END);
|
|
if (result != FILEIO_RESULT_SUCCESS)
|
|
{
|
|
error = FILEIO_ERROR_SEEK_ERROR;
|
|
}
|
|
else
|
|
{
|
|
mode &= ~FILEIO_OPEN_APPEND;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check to ensure no errors occured
|
|
if (error != FILEIO_ERROR_NONE)
|
|
{
|
|
directory.drive->error = error;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
return FILEIO_RESULT_SUCCESS;
|
|
}
|
|
|
|
bool FILEIO_IsClusterAllocated(FILEIO_DIRECTORY * directory, FILEIO_OBJECT * filePtr)
|
|
{
|
|
FILEIO_ERROR_TYPE error;
|
|
uint32_t currentCluster = filePtr->baseClusterDir;
|
|
uint16_t currentClusterOffset = 0;
|
|
FILEIO_DIRECTORY_ENTRY * entry;
|
|
|
|
if (currentCluster == 0)
|
|
{
|
|
currentCluster = directory->drive->firstRootCluster;
|
|
}
|
|
|
|
entry = FILEIO_DirectoryEntryCache (directory, &error, ¤tCluster, ¤tClusterOffset, filePtr->entry);
|
|
|
|
if (entry == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ((entry->firstClusterHigh == 0) && (entry->firstClusterLow == 0))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
uint32_t FILEIO_FullClusterNumberGet (FILEIO_DIRECTORY_ENTRY * entry)
|
|
{
|
|
uint32_t result;
|
|
|
|
result = entry->firstClusterHigh;
|
|
result <<= 16;
|
|
result |= entry->firstClusterLow;
|
|
|
|
return result;
|
|
}
|
|
|
|
uint16_t * FILEIO_CacheDirectory (FILEIO_DIRECTORY * dir, uint16_t * path, bool createDirectories)
|
|
{
|
|
uint16_t pathLen;
|
|
#if !defined (FILEIO_CONFIG_DIRECTORY_DISABLE)
|
|
uint16_t i;
|
|
#endif
|
|
|
|
pathLen = FILEIO_strlen16 ((uint16_t *)path);
|
|
|
|
if (pathLen == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
memcpy (dir, &globalParameters.currentWorkingDirectory, sizeof (FILEIO_DIRECTORY));
|
|
|
|
#if !defined (FILEIO_CONFIG_DIRECTORY_DISABLE)
|
|
if (pathLen == 1)
|
|
{
|
|
#if defined (FILEIO_CONFIG_MULTIPLE_BUFFER_MODE_DISABLE)
|
|
if (FILEIO_GetSingleBuffer (dir->drive) != FILEIO_RESULT_SUCCESS)
|
|
{
|
|
return NULL;
|
|
}
|
|
#endif
|
|
return path;
|
|
}
|
|
|
|
// Check to see if the user explicitly specified the drive
|
|
if (*(path + 1) == ':')
|
|
{
|
|
// They did
|
|
dir->drive = FILEIO_CharToDrive (*path);
|
|
if (dir->drive == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
#if defined (FILEIO_CONFIG_MULTIPLE_BUFFER_MODE_DISABLE)
|
|
if (FILEIO_GetSingleBuffer (dir->drive) != FILEIO_RESULT_SUCCESS)
|
|
{
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
dir->cluster = dir->drive->firstRootCluster;
|
|
|
|
// Increment past the drive specifier
|
|
path += 2;
|
|
pathLen -= 2;
|
|
if (pathLen == 0)
|
|
{
|
|
return path;
|
|
}
|
|
|
|
if (*path == FILEIO_CONFIG_DELIMITER)
|
|
{
|
|
path++;
|
|
pathLen--;
|
|
if (pathLen == 0)
|
|
{
|
|
return path;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined (FILEIO_CONFIG_MULTIPLE_BUFFER_MODE_DISABLE)
|
|
if (FILEIO_GetSingleBuffer (dir->drive) != FILEIO_RESULT_SUCCESS)
|
|
{
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
// Find the next forward slash (indicates part of the path is a directory)
|
|
while ((i = FILEIO_FindNextDelimiter(path)) != -1)
|
|
{
|
|
// If someone terminated a directory path with a delimiter, break out of the loop
|
|
if (*(path + i) == FILEIO_CONFIG_DELIMITER)
|
|
{
|
|
if (*(path + i + 1) == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If i == 0, someone put two delimiters in a row or something strange; don't change directory
|
|
if (i != 0)
|
|
{
|
|
// Try to change dir to that directory
|
|
if (FILEIO_DirectoryChangeSingle (dir, path) != FILEIO_RESULT_SUCCESS)
|
|
{
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
if (createDirectories)
|
|
{
|
|
// If we couldn't change to that directory, it probably doesn't exist. Try to create it.
|
|
if (FILEIO_DirectoryMakeSingle (dir, path) != FILEIO_RESULT_SUCCESS)
|
|
{
|
|
return NULL;
|
|
}
|
|
// Now that it exists, try to change to it.
|
|
if (FILEIO_DirectoryChangeSingle (dir, path) != FILEIO_RESULT_SUCCESS)
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Increment i to include the delimiter
|
|
i++;
|
|
// Increment the path
|
|
path += i;
|
|
// Decrement the path length
|
|
pathLen -= i;
|
|
}
|
|
#endif
|
|
|
|
// Whatever is left must be our file name. dir will contain the drive and cluster of the directory.
|
|
return path;
|
|
}
|
|
|
|
#if !defined (FILEIO_CONFIG_DIRECTORY_DISABLE)
|
|
uint16_t FILEIO_FindNextDelimiter(const uint16_t * path)
|
|
{
|
|
uint16_t i = 0;
|
|
uint16_t c;
|
|
|
|
while (((c = *path++) != 0) && (c != FILEIO_CONFIG_DELIMITER))
|
|
{
|
|
i++;
|
|
}
|
|
|
|
if (c == 0)
|
|
{
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if !defined (FILEIO_CONFIG_DIRECTORY_DISABLE)
|
|
FILEIO_RESULT FILEIO_DirectoryChangeSingle (FILEIO_DIRECTORY * directory, uint16_t * path)
|
|
{
|
|
FILEIO_ERROR_TYPE error;
|
|
uint8_t fileNameType;
|
|
uint32_t currentCluster;
|
|
uint16_t currentClusterOffset;
|
|
FILEIO_OBJECT file;
|
|
FILEIO_OBJECT * filePtr = &file;
|
|
|
|
fileNameType = FILEIO_FileNameTypeGet(path, false);
|
|
|
|
if (fileNameType == FILEIO_NAME_INVALID)
|
|
{
|
|
directory->drive->error = FILEIO_ERROR_INVALID_FILENAME;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
else if (fileNameType == FILEIO_NAME_SHORT)
|
|
{
|
|
currentCluster = directory->cluster;
|
|
currentClusterOffset = 0;
|
|
// Short file name
|
|
FILEIO_FormatShortFileName (path, filePtr);
|
|
// Search in 'directory' for an entry matching filePtr->name, starting at entry 0 in directory->cluster and returning the result in filePtr
|
|
error = FILEIO_FindShortFileName (directory, filePtr, (uint8_t *)filePtr->name, ¤tCluster, ¤tClusterOffset, 0, FILEIO_ATTRIBUTE_MASK, FILEIO_SEARCH_ENTRY_MATCH);
|
|
}
|
|
else if (fileNameType == FILEIO_NAME_DOT)
|
|
{
|
|
// If someone specified a '.' filename, we don't have to change anything
|
|
if (*(path + 1) != '.')
|
|
{
|
|
return FILEIO_RESULT_SUCCESS;
|
|
}
|
|
|
|
// If they specified a dotdot filename, cache the previous directory's cluster
|
|
{
|
|
FILEIO_DIRECTORY_ENTRY * entry;
|
|
|
|
currentCluster = directory->cluster;
|
|
currentClusterOffset = 0;
|
|
|
|
// Cache the .. entry
|
|
entry = FILEIO_DirectoryEntryCache (directory, &error, ¤tCluster, ¤tClusterOffset, 1);
|
|
if (error == FILEIO_ERROR_NONE)
|
|
{
|
|
directory->cluster = FILEIO_FullClusterNumberGet (entry);
|
|
return FILEIO_RESULT_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Long file name
|
|
currentCluster = directory->cluster;
|
|
currentClusterOffset = 0;
|
|
// Search in 'directory' for an entry matching fileName, starting at entry 0 in directory->cluster and returning the short file name in filePtr.
|
|
// The long file name will be cached in lfnData
|
|
filePtr->lfnPtr = path;
|
|
filePtr->lfnLen = FILEIO_lfnlen(path);
|
|
error = FILEIO_FindLongFileName (directory, filePtr, ¤tCluster, ¤tClusterOffset, 0, FILEIO_ATTRIBUTE_MASK, FILEIO_SEARCH_ENTRY_MATCH);
|
|
}
|
|
|
|
if (error == FILEIO_ERROR_NONE)
|
|
{
|
|
// Directory found
|
|
directory->cluster = filePtr->firstCluster;
|
|
}
|
|
else
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
return FILEIO_RESULT_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
#if !defined (FILEIO_CONFIG_DIRECTORY_DISABLE)
|
|
FILEIO_RESULT FILEIO_DirectoryMakeSingle (FILEIO_DIRECTORY * directory, uint16_t * path)
|
|
{
|
|
FILEIO_ERROR_TYPE error;
|
|
FILEIO_OBJECT file;
|
|
FILEIO_OBJECT * filePtr = &file;
|
|
FILEIO_DIRECTORY_ENTRY * entry;
|
|
FILEIO_TIMESTAMP timeStamp;
|
|
uint16_t entryOffset = 0;
|
|
uint32_t dot, dotdot;
|
|
uint32_t currentCluster;
|
|
uint16_t currentClusterOffset = 0;
|
|
uint8_t fileNameType;
|
|
|
|
file.baseClusterDir = directory->cluster;
|
|
if (file.baseClusterDir == 0)
|
|
{
|
|
file.baseClusterDir = directory->drive->firstRootCluster;
|
|
}
|
|
file.disk = directory->drive;
|
|
|
|
fileNameType = FILEIO_FileNameTypeGet(path, false);
|
|
|
|
if ((fileNameType == FILEIO_NAME_INVALID) || (fileNameType == FILEIO_NAME_DOT))
|
|
{
|
|
directory->drive->error = FILEIO_ERROR_INVALID_FILENAME;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
else if (fileNameType == FILEIO_NAME_SHORT)
|
|
{
|
|
// Short file name
|
|
FILEIO_FormatShortFileName (path, filePtr);
|
|
}
|
|
else
|
|
{
|
|
filePtr->lfnPtr = (uint16_t *)path;
|
|
filePtr->lfnLen = FILEIO_lfnlen ((uint16_t *)path);
|
|
}
|
|
|
|
if (FILEIO_DirectoryEntryCreate (filePtr, &entryOffset, FILEIO_ATTRIBUTE_DIRECTORY, true) != FILEIO_ERROR_NONE)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
if (file.baseClusterDir == ((FILEIO_DRIVE *)file.disk)->firstRootCluster)
|
|
{
|
|
dotdot = 0;
|
|
}
|
|
else
|
|
{
|
|
dotdot = file.baseClusterDir;
|
|
}
|
|
|
|
file.currentClusterDir = file.baseClusterDir;
|
|
directory->cluster = file.baseClusterDir;
|
|
currentCluster = file.baseClusterDir;
|
|
|
|
entry = FILEIO_DirectoryEntryCache (directory, &error, ¤tCluster, ¤tClusterOffset, entryOffset);
|
|
|
|
if (entry == NULL)
|
|
{
|
|
directory->drive->error = error;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
timeStamp.timeMs = entry->createTimeMs;
|
|
timeStamp.time.value = entry->createTime;
|
|
timeStamp.date.value = entry->createDate;
|
|
|
|
dot = FILEIO_FullClusterNumberGet (entry);
|
|
|
|
directory->cluster = file.firstCluster;
|
|
currentCluster = file.firstCluster;
|
|
currentClusterOffset = 0;
|
|
entryOffset = 0;
|
|
|
|
entry = FILEIO_DirectoryEntryCache (directory, &error, ¤tCluster, ¤tClusterOffset, entryOffset);
|
|
|
|
directory->cluster = file.baseClusterDir;
|
|
|
|
if (FILEIO_DotEntryWrite(directory->drive, dot, dotdot, &timeStamp))
|
|
{
|
|
return FILEIO_RESULT_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
#if !defined (FILEIO_CONFIG_DIRECTORY_DISABLE)
|
|
int FILEIO_DotEntryWrite (FILEIO_DRIVE * drive, uint32_t dot, uint32_t dotdot, FILEIO_TIMESTAMP * timeStamp)
|
|
{
|
|
FILEIO_DIRECTORY_ENTRY * entryPtr;
|
|
uint32_t sector;
|
|
|
|
memset(drive->dataBuffer, 0x00, drive->sectorSize);
|
|
|
|
entryPtr = (FILEIO_DIRECTORY_ENTRY *)drive->dataBuffer;
|
|
|
|
memset (drive->dataBuffer, 0x20, FILEIO_FILE_NAME_LENGTH_8P3_NO_RADIX);
|
|
entryPtr->name[0] = '.';
|
|
entryPtr->attributes = FILEIO_ATTRIBUTE_DIRECTORY;
|
|
entryPtr->reserved0 = 0x00;
|
|
entryPtr->firstClusterLow = (uint16_t)(dot & 0x0000FFFF); // Lower 16 bit address
|
|
entryPtr->firstClusterHigh = (uint16_t)((dot & 0x0FFF0000)>> 16); // Higher 16 bit address. FAT32 uses only 28 bits. Mask even higher nibble also.
|
|
|
|
entryPtr->fileSize = 0x00;
|
|
|
|
// Times need to be the same as the times in the directory entry
|
|
|
|
entryPtr->createTimeMs = timeStamp->timeMs; // millisecond stamp
|
|
entryPtr->createTime = timeStamp->time.value; // time created //
|
|
entryPtr->createDate = timeStamp->time.value; // date created (1/1/2004)
|
|
entryPtr->accessDate = 0x0000; // Last Access date
|
|
entryPtr->writeTime = 0x0000; // last update time
|
|
entryPtr->writeDate = 0x0000; // last update date
|
|
|
|
memcpy (drive->dataBuffer + sizeof (FILEIO_DIRECTORY_ENTRY), drive->dataBuffer, sizeof (FILEIO_DIRECTORY_ENTRY));
|
|
entryPtr = (FILEIO_DIRECTORY_ENTRY *)(drive->dataBuffer + sizeof (FILEIO_DIRECTORY_ENTRY));
|
|
entryPtr->name[1] = '.';
|
|
|
|
entryPtr->firstClusterLow = (uint16_t)(dotdot & 0x0000FFFF); // Lower 16 bit address
|
|
entryPtr->firstClusterHigh = (uint16_t)((dotdot & 0x0FFF0000)>> 16); // Higher 16 bit address. FAT32 uses only 28 bits. Mask even higher nibble also.
|
|
|
|
sector = FILEIO_ClusterToSector (drive, dot);
|
|
|
|
drive->bufferStatusPtr->flags.dataBufferNeedsWrite = true;
|
|
drive->bufferStatusPtr->dataBufferCachedSector = sector;
|
|
|
|
if (!FILEIO_FlushBuffer (drive, FILEIO_BUFFER_DATA))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
FILEIO_ERROR_TYPE FILEIO_FindShortFileName (FILEIO_DIRECTORY * directory, FILEIO_OBJECT * filePtr, uint8_t * fileName, uint32_t * currentCluster, uint16_t * currentClusterOffset, uint16_t entryOffset, uint16_t attributes, FILEIO_SEARCH_TYPE mode)
|
|
{
|
|
FILEIO_ERROR_TYPE error = FILEIO_ERROR_NONE;
|
|
FILEIO_DIRECTORY_ENTRY * entry;
|
|
|
|
if (*currentCluster == 0)
|
|
{
|
|
*currentCluster = directory->drive->firstRootCluster;
|
|
}
|
|
|
|
while(1)
|
|
{
|
|
do
|
|
{
|
|
entry = FILEIO_DirectoryEntryCache (directory, &error, currentCluster, currentClusterOffset, entryOffset);
|
|
if (error == FILEIO_ERROR_DONE)
|
|
{
|
|
break;
|
|
}
|
|
else if (error == FILEIO_ERROR_BAD_CACHE_READ)
|
|
{
|
|
directory->drive->error = FILEIO_ERROR_BAD_CACHE_READ;
|
|
return error;
|
|
}
|
|
|
|
entryOffset++;
|
|
} while (((entry->attributes == FILEIO_ATTRIBUTE_LONG_NAME) || (entry->attributes == FILEIO_ATTRIBUTE_VOLUME) || (((uint8_t)entry->name[0]) == FILEIO_DIRECTORY_ENTRY_DELETED)) && (entry->name[0] != FILEIO_DIRECTORY_ENTRY_EMPTY));
|
|
|
|
if ((error == FILEIO_ERROR_DONE) || (entry->name[0] == FILEIO_DIRECTORY_ENTRY_EMPTY))
|
|
{
|
|
return FILEIO_ERROR_DONE;
|
|
}
|
|
|
|
// Valid file entry was found
|
|
if (((mode & FILEIO_SEARCH_ENTRY_ATTRIBUTES) != FILEIO_SEARCH_ENTRY_ATTRIBUTES) || ((entry->attributes & attributes) == entry->attributes))
|
|
{
|
|
// File's attributes are valid or we aren't trying to match attributes
|
|
if (FILEIO_ShortFileNameCompare (fileName, (uint8_t *)entry->name, mode) == true)
|
|
{
|
|
// Found a match. Fill the result object with the file data
|
|
memcpy (filePtr->name, entry->name, FILEIO_FILE_NAME_LENGTH_8P3_NO_RADIX);
|
|
filePtr->disk = directory->drive;
|
|
filePtr->firstCluster = FILEIO_FullClusterNumberGet (entry);
|
|
filePtr->currentCluster = filePtr->firstCluster;
|
|
filePtr->currentSector = 0;
|
|
filePtr->currentOffset = 0;
|
|
filePtr->absoluteOffset = 0;
|
|
filePtr->size = entry->fileSize;
|
|
|
|
filePtr->attributes = entry->attributes;
|
|
if ((filePtr->attributes & FILEIO_ATTRIBUTE_DIRECTORY) == FILEIO_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
filePtr->timeMs = entry->createTimeMs;
|
|
filePtr->time = entry->createTime;
|
|
filePtr->date = entry->createDate;
|
|
}
|
|
else
|
|
{
|
|
filePtr->time = entry->writeTime;
|
|
filePtr->date = entry->writeDate;
|
|
}
|
|
filePtr->entry = --entryOffset;
|
|
filePtr->baseClusterDir = directory->cluster;
|
|
filePtr->currentClusterDir = directory->cluster;
|
|
|
|
return FILEIO_ERROR_NONE;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined (__XC8__)
|
|
// This return value is not reachable, but it is necessary to remove an XC8 compiler warning
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
FILEIO_ERROR_TYPE FILEIO_DirectoryEntryCreate (FILEIO_OBJECT * filePtr, uint16_t * entryHandle, uint8_t attributes, bool allocateDataCluster)
|
|
{
|
|
FILEIO_ERROR_TYPE error = FILEIO_ERROR_NONE;
|
|
uint32_t cluster;
|
|
|
|
*entryHandle = 0;
|
|
|
|
if (FILEIO_DirectoryEntryFindEmpty(filePtr, entryHandle) == FILEIO_ERROR_NONE)
|
|
{
|
|
// Allocate a data cluster to the file object, if necessary
|
|
if (allocateDataCluster)
|
|
{
|
|
cluster = FILEIO_CreateFirstCluster (filePtr);
|
|
error = ((FILEIO_DRIVE *)filePtr->disk)->error;
|
|
}
|
|
else
|
|
{
|
|
cluster = 0x0000000;
|
|
}
|
|
|
|
// Construct and populate the long file name entries
|
|
if (error == FILEIO_ERROR_NONE)
|
|
{
|
|
if (filePtr->lfnPtr != NULL)
|
|
{
|
|
error = FILEIO_DirectoryEntryLFNCreate (filePtr, entryHandle);
|
|
}
|
|
}
|
|
|
|
// Construct and populate the short file entry
|
|
if (error == FILEIO_ERROR_NONE)
|
|
{
|
|
error = FILEIO_DirectoryEntryPopulate(filePtr, entryHandle, attributes, cluster);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = FILEIO_ERROR_DIR_FULL;
|
|
}
|
|
|
|
((FILEIO_DRIVE *)filePtr->disk)->error = error;
|
|
return error;
|
|
}
|
|
#endif
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
uint32_t FILEIO_CreateFirstCluster (FILEIO_OBJECT * filePtr)
|
|
{
|
|
FILEIO_ERROR_TYPE error = FILEIO_ERROR_NONE;
|
|
FILEIO_DRIVE * drive = filePtr->disk;
|
|
uint32_t cluster;
|
|
|
|
cluster = FILEIO_FindEmptyCluster (filePtr->disk, 2);
|
|
|
|
if (cluster == 0)
|
|
{
|
|
error = FILEIO_ERROR_DRIVE_FULL;
|
|
}
|
|
else
|
|
{
|
|
// mark the cluster as taken, and last in chain
|
|
if (drive->type == FILEIO_FILE_SYSTEM_TYPE_FAT12)
|
|
{
|
|
if(FILEIO_FATWrite (drive, cluster, FILEIO_CLUSTER_VALUE_FAT12_EOF, false) == FILEIO_CLUSTER_VALUE_FAT16_FAIL)
|
|
{
|
|
error = FILEIO_ERROR_WRITE;
|
|
}
|
|
}
|
|
else if (drive->type == FILEIO_FILE_SYSTEM_TYPE_FAT16)
|
|
{
|
|
if(FILEIO_FATWrite (drive, cluster, FILEIO_CLUSTER_VALUE_FAT16_EOF, false) == FILEIO_CLUSTER_VALUE_FAT16_FAIL)
|
|
{
|
|
error = FILEIO_ERROR_WRITE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(FILEIO_FATWrite (drive, cluster, FILEIO_CLUSTER_VALUE_FAT32_EOF, false) == FILEIO_CLUSTER_VALUE_FAT32_FAIL)
|
|
{
|
|
error = FILEIO_ERROR_WRITE;
|
|
}
|
|
}
|
|
|
|
// lets erase this cluster
|
|
if(error == FILEIO_ERROR_NONE)
|
|
{
|
|
error = FILEIO_EraseCluster(drive, cluster);
|
|
}
|
|
}
|
|
|
|
if (!FILEIO_FlushBuffer (drive, FILEIO_BUFFER_FAT))
|
|
{
|
|
error = FILEIO_ERROR_WRITE;
|
|
}
|
|
|
|
filePtr->firstCluster = cluster;
|
|
filePtr->currentCluster = cluster;
|
|
|
|
drive->error = error;
|
|
|
|
return cluster;
|
|
}
|
|
#endif
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
FILEIO_ERROR_TYPE FILEIO_DirectoryEntryLFNCreate (FILEIO_OBJECT * filePtr, uint16_t * entryHandle)
|
|
{
|
|
char * source;
|
|
uint8_t checksum = 0;
|
|
uint8_t i;
|
|
uint8_t remainder, fileEntryCount, sequenceNumber;
|
|
uint16_t length;
|
|
uint16_t * tempNamePtr;
|
|
bool firstLoop = true;
|
|
FILEIO_DIRECTORY_ENTRY_LFN * lfnEntry;
|
|
FILEIO_DIRECTORY directory;
|
|
FILEIO_ERROR_TYPE error = FILEIO_ERROR_NONE;
|
|
uint32_t currentCluster;
|
|
uint16_t currentClusterOffset;
|
|
|
|
if (!FILEIO_AliasLFN(filePtr))
|
|
{
|
|
return FILEIO_ERROR_FILENAME_EXISTS;
|
|
}
|
|
|
|
source = filePtr->name;
|
|
|
|
// Calculate the short file name checksum for this file
|
|
for (i = 11; i != 0; i--)
|
|
{
|
|
checksum = ((checksum & 1) ? 0x80 : 0) + (checksum >> 1) + *source++;
|
|
}
|
|
|
|
length = filePtr->lfnLen;
|
|
|
|
// Calculate the number of entries required for the given file name
|
|
remainder = length % FILEIO_FILE_NAME_UTF16_CHARS_IN_LFN_ENTRY;
|
|
fileEntryCount = length / FILEIO_FILE_NAME_UTF16_CHARS_IN_LFN_ENTRY;
|
|
|
|
if (remainder || (length < (FILEIO_FILE_NAME_UTF16_CHARS_IN_LFN_ENTRY)))
|
|
{
|
|
fileEntryCount++;
|
|
tempNamePtr = filePtr->lfnPtr + length - remainder;
|
|
}
|
|
else
|
|
{
|
|
tempNamePtr = filePtr->lfnPtr + length - FILEIO_FILE_NAME_UTF16_CHARS_IN_LFN_ENTRY;
|
|
}
|
|
|
|
if (remainder == 0)
|
|
{
|
|
remainder = FILEIO_FILE_NAME_UTF16_CHARS_IN_LFN_ENTRY;
|
|
}
|
|
|
|
// Calculate maximum sequence number for LFN root entries
|
|
sequenceNumber = fileEntryCount | 0x40;
|
|
|
|
directory.cluster = filePtr->baseClusterDir;
|
|
directory.drive = filePtr->disk;
|
|
currentCluster = directory.cluster;
|
|
currentClusterOffset = 0;
|
|
|
|
while (fileEntryCount)
|
|
{
|
|
lfnEntry = (FILEIO_DIRECTORY_ENTRY_LFN *)FILEIO_DirectoryEntryCache (&directory, &error, ¤tCluster, ¤tClusterOffset, *entryHandle);
|
|
|
|
if (lfnEntry == NULL)
|
|
{
|
|
return FILEIO_ERROR_BAD_CACHE_READ;
|
|
}
|
|
|
|
((FILEIO_DRIVE *)filePtr->disk)->bufferStatusPtr->flags.dataBufferNeedsWrite = true;
|
|
|
|
lfnEntry->sequenceNumber = sequenceNumber--;
|
|
lfnEntry->attributes = FILEIO_ATTRIBUTE_LONG_NAME;
|
|
lfnEntry->type = 0;
|
|
lfnEntry->checksum = checksum;
|
|
lfnEntry->reserved0 = 0;
|
|
|
|
// Copy the name
|
|
if (firstLoop)
|
|
{
|
|
uint16_t tempArray[FILEIO_FILE_NAME_UTF16_CHARS_IN_LFN_ENTRY];
|
|
|
|
memcpy (tempArray, tempNamePtr, remainder << 1);
|
|
// If this name isn't an even multiple of FILEIO_NAME_NAME_UTF16_CHARS_IN_LFN_ENTRY, we need to null-terminate it
|
|
if (remainder != FILEIO_FILE_NAME_UTF16_CHARS_IN_LFN_ENTRY)
|
|
{
|
|
tempArray[remainder++] = 0x0000;
|
|
}
|
|
|
|
memset (tempArray + remainder, 0xFF, (FILEIO_FILE_NAME_UTF16_CHARS_IN_LFN_ENTRY - remainder) << 1);
|
|
|
|
memcpy (&lfnEntry->namePart1, tempArray, 10);
|
|
memcpy (&lfnEntry->namePart2, &tempArray[5], 12);
|
|
memcpy (&lfnEntry->namePart3, &tempArray[11], 4);
|
|
|
|
// Decrement tempNamePtr to the beginning of the next file entry
|
|
tempNamePtr -= FILEIO_FILE_NAME_UTF16_CHARS_IN_LFN_ENTRY;
|
|
|
|
firstLoop = false;
|
|
sequenceNumber &= (~0x40);
|
|
}
|
|
else
|
|
{
|
|
memcpy (&lfnEntry->namePart1, tempNamePtr, 10);
|
|
memcpy (&lfnEntry->namePart2, tempNamePtr + 5, 12);
|
|
memcpy (&lfnEntry->namePart3, tempNamePtr + 11, 4);
|
|
// Decrement tempNamePtr to the beginning of the next file entry
|
|
tempNamePtr -= FILEIO_FILE_NAME_UTF16_CHARS_IN_LFN_ENTRY;
|
|
}
|
|
|
|
*entryHandle = *entryHandle + 1;
|
|
fileEntryCount--;
|
|
}
|
|
|
|
// Store the next entry (the short file name entry) in the file pointer's entry field
|
|
filePtr->entry = *entryHandle;
|
|
|
|
// Don't bother to force a write; the entries will get updated when the short file name entry is written
|
|
return error;
|
|
}
|
|
#endif
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
bool FILEIO_AliasLFN (FILEIO_OBJECT * filePtr)
|
|
{
|
|
uint32_t loopIndex, loopIndexEnd;
|
|
int16_t index1, extIndex, lfnIndex;
|
|
uint8_t i, j;
|
|
uint8_t tempString[FILEIO_FILE_NAME_LENGTH_8P3_NO_RADIX];
|
|
uint16_t * templfnPtr;
|
|
uint16_t length;
|
|
FILEIO_DIRECTORY directory;
|
|
uint32_t currentCluster;
|
|
uint16_t currentClusterOffset;
|
|
bool forceTail = false;
|
|
uint8_t tailLen = 0;
|
|
|
|
templfnPtr = filePtr->lfnPtr;
|
|
length = filePtr->lfnLen;
|
|
|
|
// Initially fill the alias name with space characters
|
|
memset (filePtr->name, ' ', FILEIO_FILE_NAME_LENGTH_8P3_NO_RADIX);
|
|
|
|
// find the location where '.' is present
|
|
for(lfnIndex = length - 1; lfnIndex > 0; lfnIndex--)
|
|
{
|
|
if (templfnPtr[lfnIndex] == '.')
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
index1 = lfnIndex + 1;
|
|
if(lfnIndex)
|
|
{
|
|
// Complete the extension part as per the FAT specifications
|
|
for(extIndex = 8, j = 0; (index1 < length) && (j < 3); index1++)
|
|
{
|
|
// Convert lower-case to upper-case
|
|
i = (uint8_t)templfnPtr[index1];
|
|
if ((i >= 0x61) && (i <= 0x7A))
|
|
{
|
|
filePtr->name[extIndex++] = i - 0x20;
|
|
}
|
|
else if (i == ' ')
|
|
{
|
|
continue;
|
|
}
|
|
else if ((i == 0x2B) || (i == 0x2C) || (i == 0x3B) ||
|
|
(i == 0x3D) || (i == 0x5B) || (i == 0x5D) ||
|
|
(templfnPtr[index1] > 0xFF))
|
|
{
|
|
filePtr->name[extIndex++] = '_';
|
|
forceTail = true;
|
|
}
|
|
else if (i == FILEIO_CONFIG_DELIMITER)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
filePtr->name[extIndex++] = i;
|
|
}
|
|
|
|
j++;
|
|
}
|
|
|
|
extIndex = lfnIndex;
|
|
}
|
|
else
|
|
{
|
|
extIndex = length;
|
|
}
|
|
|
|
// Fill the base part as per the FAT specifications
|
|
for(index1 = 0, j = 0; ((index1 < extIndex) && (j < 8)); index1++)
|
|
{
|
|
// Convert lower-case to upper-case
|
|
i = (uint8_t)templfnPtr[index1];
|
|
if ((i >= 0x61) && (i <= 0x7A))
|
|
{
|
|
filePtr->name[j] = i - 0x20;
|
|
}
|
|
else if ((i == ' ') || (i == '.'))
|
|
{
|
|
continue;
|
|
}
|
|
else if ((i == 0x2B) || (i == 0x2C) || (i == 0x3B) ||
|
|
(i == 0x3D) || (i == 0x5B) || (i == 0x5D) ||
|
|
(templfnPtr[index1] > 0xFF))
|
|
{
|
|
filePtr->name[j] = '_';
|
|
forceTail = true;
|
|
}
|
|
else
|
|
{
|
|
filePtr->name[j] = i;
|
|
}
|
|
|
|
j++;
|
|
}
|
|
|
|
filePtr->attributes = FILEIO_ATTRIBUTE_ARCHIVE;
|
|
|
|
// Check to see if we need to append a numeric index to the name
|
|
|
|
directory.cluster = filePtr->baseClusterDir;
|
|
directory.drive = filePtr->disk;
|
|
currentCluster = directory.cluster;
|
|
currentClusterOffset = 0;
|
|
|
|
// See if the current short file name exists or if we need to force a tail because our LFN contained unconvertable unicode characters
|
|
if (forceTail || (FILEIO_FindShortFileName (&directory, filePtr, (uint8_t *)&filePtr->name, ¤tCluster, ¤tClusterOffset, 0, 0, FILEIO_SEARCH_ENTRY_MATCH) == FILEIO_ERROR_NONE))
|
|
{
|
|
memcpy (tempString, &filePtr->name, FILEIO_FILE_NAME_LENGTH_8P3_NO_RADIX);
|
|
|
|
// The max number of name characters with a numeric index is 6
|
|
if (j > 6)
|
|
{
|
|
j = 6;
|
|
}
|
|
|
|
loopIndex = 1;
|
|
|
|
// Outer loop - Adjust the length of the tail and add the '~' character
|
|
for ( ; j > 0; j--)
|
|
{
|
|
tempString[j] = '~';
|
|
switch (j)
|
|
{
|
|
case 6:
|
|
tailLen = 2;
|
|
loopIndexEnd = 10;
|
|
break;
|
|
case 5:
|
|
tailLen = 3;
|
|
loopIndexEnd = 100;
|
|
break;
|
|
case 4:
|
|
tailLen = 4;
|
|
loopIndexEnd = 1000;
|
|
break;
|
|
case 3:
|
|
tailLen = 5;
|
|
loopIndexEnd = 10000;
|
|
break;
|
|
case 2:
|
|
tailLen = 6;
|
|
loopIndexEnd = 100000;
|
|
break;
|
|
case 1:
|
|
tailLen = 7;
|
|
loopIndexEnd = 1000000;
|
|
break;
|
|
}
|
|
|
|
// Inner loop - increase the value of the numeric index until we find one that isn't taken
|
|
for ( ; loopIndex < loopIndexEnd; loopIndex++)
|
|
{
|
|
currentCluster = directory.cluster;
|
|
currentClusterOffset = 0;
|
|
|
|
switch (tailLen)
|
|
{
|
|
case 7:
|
|
tempString[2] = (loopIndex % 1000000)/100000 + '0';
|
|
case 6:
|
|
tempString[3] = (loopIndex % 100000)/10000 + '0';
|
|
case 5:
|
|
tempString[4] = (loopIndex % 10000)/1000 + '0';
|
|
case 4:
|
|
tempString[5] = (loopIndex % 1000)/100 + '0';
|
|
case 3:
|
|
tempString[6] = (loopIndex % (uint8_t)100)/10 + '0';
|
|
case 2:
|
|
tempString[7] = loopIndex % (uint8_t)10 + '0';
|
|
break;
|
|
}
|
|
|
|
// See if the file is found
|
|
if(FILEIO_FindShortFileName (&directory, filePtr, tempString, ¤tCluster, ¤tClusterOffset, 0, 0, FILEIO_SEARCH_ENTRY_MATCH) == FILEIO_ERROR_DONE)
|
|
{
|
|
memcpy (filePtr->name, tempString, FILEIO_FILE_NAME_LENGTH_8P3_NO_RADIX);
|
|
return true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
void FILEIO_ShortFileNameGet (FILEIO_OBJECT * filePtr, char * buffer)
|
|
{
|
|
FILEIO_ShortFileNameConvert (buffer, filePtr->name);
|
|
}
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
FILEIO_ERROR_TYPE FILEIO_DirectoryEntryPopulate(FILEIO_OBJECT * filePtr, uint16_t * entryHandle, uint8_t attributes, uint32_t cluster)
|
|
{
|
|
FILEIO_ERROR_TYPE error = FILEIO_ERROR_NONE;
|
|
FILEIO_DIRECTORY_ENTRY * entry;
|
|
FILEIO_DIRECTORY directory;
|
|
uint32_t currentCluster;
|
|
uint16_t currentClusterOffset = 0;
|
|
FILEIO_TIMESTAMP timeStamp;
|
|
|
|
directory.cluster = filePtr->baseClusterDir;
|
|
directory.drive = filePtr->disk;
|
|
|
|
currentCluster = filePtr->baseClusterDir;
|
|
|
|
entry = FILEIO_DirectoryEntryCache (&directory, &error, ¤tCluster, ¤tClusterOffset, *entryHandle);
|
|
|
|
if (entry == NULL)
|
|
{
|
|
((FILEIO_DRIVE *)filePtr->disk)->error = FILEIO_ERROR_BAD_CACHE_READ;
|
|
return FILEIO_ERROR_BAD_CACHE_READ;
|
|
}
|
|
|
|
strncpy (entry->name, filePtr->name, FILEIO_FILE_NAME_LENGTH_8P3_NO_RADIX);
|
|
|
|
entry->attributes = attributes;
|
|
entry->reserved0 = 0x00;
|
|
entry->firstClusterLow = (cluster & 0x0000FFFF);
|
|
entry->firstClusterHigh = (cluster & 0x0FFF0000) >> 16; // FAT32 only uses 28 bits of the upper word. Mask off the other four bits
|
|
entry->fileSize = 0x00000000;
|
|
|
|
// FILEIO_GetTimestamp(&timeStamp);
|
|
// if (timestampGet != NULL)
|
|
// {
|
|
// (*timestampGet)(&timeStamp);
|
|
// }
|
|
|
|
entry->createTimeMs = timeStamp.timeMs;
|
|
entry->createTime = timeStamp.time.value;
|
|
entry->createDate = timeStamp.date.value;
|
|
entry->writeTime = timeStamp.time.value;
|
|
entry->writeDate = timeStamp.date.value;
|
|
|
|
// Populate the file object
|
|
filePtr->firstCluster = cluster;
|
|
filePtr->currentCluster = cluster;
|
|
filePtr->currentSector = 0;
|
|
filePtr->currentOffset = 0;
|
|
filePtr->absoluteOffset = 0;
|
|
filePtr->size = entry->fileSize;
|
|
filePtr->attributes = entry->attributes;
|
|
if ((filePtr->attributes & FILEIO_ATTRIBUTE_DIRECTORY) == FILEIO_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
filePtr->timeMs = entry->createTimeMs;
|
|
filePtr->time = entry->createTime;
|
|
filePtr->date = entry->createDate;
|
|
}
|
|
else
|
|
{
|
|
filePtr->time = entry->writeTime;
|
|
filePtr->date = entry->writeDate;
|
|
}
|
|
filePtr->entry = *entryHandle;
|
|
filePtr->baseClusterDir = directory.cluster;
|
|
filePtr->currentClusterDir = directory.cluster;
|
|
|
|
((FILEIO_DRIVE *)filePtr->disk)->bufferStatusPtr->flags.dataBufferNeedsWrite = true;
|
|
|
|
return error;
|
|
}
|
|
#endif
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
FILEIO_ERROR_TYPE FILEIO_DirectoryEntryFindEmpty (FILEIO_OBJECT * filePtr, uint16_t * entryOffset)
|
|
{
|
|
FILEIO_ERROR_TYPE error = FILEIO_ERROR_NONE;
|
|
FILEIO_DIRECTORY_ENTRY * entry;
|
|
FILEIO_DIRECTORY directory;
|
|
uint32_t currentCluster = filePtr->baseClusterDir;
|
|
uint32_t tempCurrentCluster;
|
|
uint16_t currentClusterOffset = 0;
|
|
uint8_t fileEntryCount;
|
|
uint8_t foundEntryCount;
|
|
uint16_t tempHandle, tempHandle2;
|
|
enum {NOT_FOUND, FOUND, ERROR} status = NOT_FOUND;
|
|
|
|
directory.drive = filePtr->disk;
|
|
directory.cluster = filePtr->baseClusterDir;
|
|
|
|
if (filePtr->lfnPtr)
|
|
{
|
|
fileEntryCount = (filePtr->lfnLen / FILEIO_FILE_NAME_UTF16_CHARS_IN_LFN_ENTRY) + 1;
|
|
if ((filePtr->lfnLen % FILEIO_FILE_NAME_UTF16_CHARS_IN_LFN_ENTRY) ||
|
|
(filePtr->lfnLen < FILEIO_FILE_NAME_UTF16_CHARS_IN_LFN_ENTRY))
|
|
{
|
|
fileEntryCount++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fileEntryCount = 1;
|
|
}
|
|
|
|
tempHandle2 = *entryOffset;
|
|
|
|
while (status == NOT_FOUND)
|
|
{
|
|
foundEntryCount = 0;
|
|
tempHandle = tempHandle2;
|
|
|
|
// Find [fileEntryCount] empty entries
|
|
do
|
|
{
|
|
tempCurrentCluster = currentCluster;
|
|
entry = FILEIO_DirectoryEntryCache (&directory, &error, ¤tCluster, ¤tClusterOffset, tempHandle2);
|
|
|
|
tempHandle2++;
|
|
} while ((entry != NULL) && (((uint8_t)(entry->name[0]) == FILEIO_DIRECTORY_ENTRY_DELETED) || (entry->name[0] == FILEIO_DIRECTORY_ENTRY_EMPTY)) && (++foundEntryCount < fileEntryCount));
|
|
|
|
if (entry == NULL)
|
|
{
|
|
if ((currentCluster == ((FILEIO_DRIVE *)filePtr->disk)->firstRootCluster) && (((FILEIO_DRIVE *)filePtr->disk)->type != FILEIO_FILE_SYSTEM_TYPE_FAT32))
|
|
{
|
|
status = ERROR;
|
|
}
|
|
else
|
|
{
|
|
currentCluster = tempCurrentCluster;
|
|
if (FILEIO_ClusterAllocate (filePtr->disk, ¤tCluster, true) == FILEIO_ERROR_DRIVE_FULL)
|
|
{
|
|
status = ERROR;
|
|
}
|
|
else
|
|
{
|
|
status = FOUND;
|
|
}
|
|
}
|
|
}
|
|
else if (foundEntryCount == fileEntryCount)
|
|
{
|
|
status = FOUND;
|
|
}
|
|
}
|
|
|
|
*entryOffset = tempHandle;
|
|
|
|
if (status == FOUND)
|
|
{
|
|
return FILEIO_ERROR_NONE;
|
|
}
|
|
else
|
|
{
|
|
return FILEIO_ERROR_DONE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
FILEIO_ERROR_TYPE FILEIO_ClusterAllocate (FILEIO_DRIVE * drive, uint32_t * cluster, bool eraseCluster)
|
|
{
|
|
uint32_t newCluster;
|
|
|
|
newCluster = FILEIO_FindEmptyCluster (drive, *cluster);
|
|
if (newCluster == 0)
|
|
{
|
|
return FILEIO_ERROR_DRIVE_FULL;
|
|
}
|
|
|
|
if (drive->type == FILEIO_FILE_SYSTEM_TYPE_FAT12)
|
|
{
|
|
FILEIO_FATWrite (drive, newCluster, FILEIO_CLUSTER_VALUE_FAT12_EOF, false);
|
|
}
|
|
else if (drive->type == FILEIO_FILE_SYSTEM_TYPE_FAT16)
|
|
{
|
|
FILEIO_FATWrite (drive, newCluster, FILEIO_CLUSTER_VALUE_FAT16_EOF, false);
|
|
}
|
|
else if (drive->type == FILEIO_FILE_SYSTEM_TYPE_FAT32)
|
|
{
|
|
FILEIO_FATWrite (drive, newCluster, FILEIO_CLUSTER_VALUE_FAT32_EOF, false);
|
|
}
|
|
|
|
FILEIO_FATWrite (drive, *cluster, newCluster, false);
|
|
|
|
*cluster = newCluster;
|
|
|
|
if (eraseCluster)
|
|
{
|
|
return FILEIO_EraseCluster (drive, newCluster);
|
|
}
|
|
else
|
|
{
|
|
return FILEIO_ERROR_NONE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
uint32_t FILEIO_FindEmptyCluster (FILEIO_DRIVE * drive, uint32_t baseCluster)
|
|
{
|
|
uint32_t cluster = 0x0;
|
|
uint32_t currentCluster, endClusterLimit, clusterFailValue;
|
|
|
|
/* Settings based on FAT type */
|
|
switch (drive->type)
|
|
{
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT32:
|
|
endClusterLimit = FILEIO_CLUSTER_VALUE_FAT32_END;
|
|
clusterFailValue = FILEIO_CLUSTER_VALUE_FAT32_FAIL;
|
|
break;
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT12:
|
|
endClusterLimit = FILEIO_CLUSTER_VALUE_FAT12_END;
|
|
clusterFailValue = FILEIO_CLUSTER_VALUE_FAT16_FAIL;
|
|
break;
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT16:
|
|
default:
|
|
endClusterLimit = FILEIO_CLUSTER_VALUE_FAT16_END;
|
|
clusterFailValue = FILEIO_CLUSTER_VALUE_FAT16_FAIL;
|
|
break;
|
|
}
|
|
|
|
// just in case
|
|
if(baseCluster < 2)
|
|
baseCluster = 2;
|
|
|
|
currentCluster = baseCluster;
|
|
|
|
// sequentially scan through the FAT looking for an empty cluster
|
|
while(currentCluster)
|
|
{
|
|
// look at its value
|
|
if ((cluster = FILEIO_FATRead(drive, currentCluster)) == clusterFailValue)
|
|
{
|
|
currentCluster = 0;
|
|
break;
|
|
}
|
|
|
|
// check if empty cluster found
|
|
if (cluster == FILEIO_CLUSTER_VALUE_EMPTY)
|
|
break;
|
|
|
|
currentCluster++; // check next cluster in FAT
|
|
|
|
// check if reached last cluster in FAT, re-start from top
|
|
if ((cluster == endClusterLimit) || (currentCluster >= (drive->partitionClusterCount + 2)))
|
|
{
|
|
currentCluster = 2;
|
|
}
|
|
|
|
// check if full circle done, disk full
|
|
if ( currentCluster == baseCluster)
|
|
{
|
|
currentCluster = 0;
|
|
break;
|
|
}
|
|
} // scanning for an empty cluster
|
|
|
|
return(currentCluster);
|
|
}
|
|
#endif
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
FILEIO_ERROR_TYPE FILEIO_EraseCluster (FILEIO_DRIVE * drive, uint32_t cluster)
|
|
{
|
|
FILEIO_ERROR_TYPE error = FILEIO_ERROR_NONE;
|
|
uint32_t sector = FILEIO_ClusterToSector (drive, cluster);
|
|
uint8_t i;
|
|
|
|
if (!FILEIO_FlushBuffer (drive, FILEIO_BUFFER_DATA))
|
|
{
|
|
drive->error = FILEIO_ERROR_WRITE;
|
|
return FILEIO_ERROR_WRITE;
|
|
}
|
|
|
|
memset (drive->dataBuffer, 0x00, drive->sectorSize);
|
|
|
|
for (i = 0; (i < drive->sectorsPerCluster) && (error == FILEIO_ERROR_NONE); i++)
|
|
{
|
|
// if (!(*drive->driveConfig->funcSectorWrite)(drive->mediaParameters, sector++, drive->dataBuffer, false))
|
|
if (!FILEIO_DRIVER_SectorWrite(drive->driveId, sector++, drive->dataBuffer, false))
|
|
{
|
|
error = FILEIO_ERROR_WRITE;
|
|
}
|
|
}
|
|
|
|
// As an optimization, set the cached sector to the first sector of the cluster. They're all zero anyway, now.
|
|
drive->bufferStatusPtr->dataBufferCachedSector = sector - drive->sectorsPerCluster;
|
|
drive->bufferStatusPtr->flags.dataBufferNeedsWrite = false;
|
|
|
|
drive->error = error;
|
|
return error;
|
|
}
|
|
#endif
|
|
|
|
FILEIO_DIRECTORY_ENTRY * FILEIO_DirectoryEntryCache (FILEIO_DIRECTORY * directory, FILEIO_ERROR_TYPE * error, uint32_t * currentCluster, uint16_t * currentClusterOffset, uint16_t entryOffset)
|
|
{
|
|
FILEIO_DRIVE * disk = directory->drive;
|
|
FILEIO_DIRECTORY_ENTRY * entry;
|
|
uint8_t directoryEntriesPerSector = disk->sectorSize / FILEIO_DIRECTORY_ENTRY_SIZE;
|
|
uint16_t totalSectorOffset = entryOffset / directoryEntriesPerSector;
|
|
uint32_t sector;
|
|
|
|
if ((disk->type != FILEIO_FILE_SYSTEM_TYPE_FAT32) && (*currentCluster == FILEIO_FIXED_ROOT_DIRECTORY_CLUSTER_NUMBER))
|
|
{
|
|
if (entryOffset >= disk->rootDirectoryEntryCount)
|
|
{
|
|
*error = FILEIO_ERROR_DONE;
|
|
return NULL;
|
|
}
|
|
// We're looking for the file in the FAT12/16 root directory
|
|
sector = FILEIO_ClusterToSector (directory->drive, directory->cluster);
|
|
sector += totalSectorOffset;
|
|
}
|
|
else
|
|
{
|
|
// Find the cluster/sector with the specified directory entry offset
|
|
uint16_t totalClusterOffset = totalSectorOffset / disk->sectorsPerCluster;
|
|
if (*currentClusterOffset > totalClusterOffset)
|
|
{
|
|
*currentClusterOffset = 0;
|
|
*currentCluster = directory->cluster;
|
|
}
|
|
|
|
totalSectorOffset -= (disk->sectorsPerCluster * (*currentClusterOffset));
|
|
|
|
while (*currentClusterOffset < totalClusterOffset)
|
|
{
|
|
*currentCluster = FILEIO_FATRead (disk, *currentCluster);
|
|
// Switch based on FAT type
|
|
switch (disk->type)
|
|
{
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT32:
|
|
if (*currentCluster == FILEIO_CLUSTER_VALUE_FAT32_EOF)
|
|
{
|
|
*error = FILEIO_ERROR_DONE;
|
|
return NULL;
|
|
}
|
|
break;
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT12:
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT16:
|
|
default:
|
|
if (*currentCluster == FILEIO_CLUSTER_VALUE_FAT16_EOF)
|
|
{
|
|
*error = FILEIO_ERROR_DONE;
|
|
return NULL;
|
|
}
|
|
break;
|
|
}
|
|
*currentClusterOffset = *currentClusterOffset + 1;
|
|
totalSectorOffset -= disk->sectorsPerCluster;
|
|
}
|
|
|
|
// currentCluster points to the cluster we need. Convert to sectors and add the sector offset.
|
|
sector = FILEIO_ClusterToSector (disk, *currentCluster);
|
|
sector += totalSectorOffset;
|
|
}
|
|
|
|
if (disk->bufferStatusPtr->dataBufferCachedSector != sector)
|
|
{
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
if (!FILEIO_FlushBuffer (disk, FILEIO_BUFFER_DATA))
|
|
{
|
|
*error = FILEIO_ERROR_WRITE;
|
|
return NULL;
|
|
}
|
|
#endif
|
|
// if ((*disk->driveConfig->funcSectorRead) (disk->mediaParameters, sector, disk->dataBuffer) != true)
|
|
if (FILEIO_DRIVER_SectorRead(disk->driveId, sector, disk->dataBuffer) != true)
|
|
{
|
|
*error = FILEIO_ERROR_BAD_SECTOR_READ;
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
disk->bufferStatusPtr->dataBufferCachedSector = sector;
|
|
#if defined (FILEIO_CONFIG_MULTIPLE_BUFFER_MODE_DISABLE)
|
|
disk->bufferStatusPtr->driveOwner = disk;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
entry = (FILEIO_DIRECTORY_ENTRY *)((FILEIO_DIRECTORY_ENTRY *)disk->dataBuffer + (entryOffset % directoryEntriesPerSector));
|
|
|
|
*error = FILEIO_ERROR_NONE;
|
|
|
|
return entry;
|
|
}
|
|
|
|
FILEIO_ERROR_TYPE FILEIO_ForceRecache (FILEIO_DRIVE * disk)
|
|
{
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
if (!FILEIO_FlushBuffer (disk, FILEIO_BUFFER_DATA))
|
|
{
|
|
return FILEIO_ERROR_WRITE;
|
|
}
|
|
#endif
|
|
//if ((*disk->driveConfig->funcSectorRead) (disk->mediaParameters, disk->bufferStatusPtr->dataBufferCachedSector, disk->dataBuffer) != true)v
|
|
if (FILEIO_DRIVER_SectorRead(disk->driveId, disk->bufferStatusPtr->dataBufferCachedSector, disk->dataBuffer) != true)
|
|
{
|
|
return FILEIO_ERROR_BAD_SECTOR_READ;
|
|
}
|
|
|
|
return FILEIO_ERROR_NONE;
|
|
}
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
bool FILEIO_FlushBuffer (FILEIO_DRIVE * disk, FILEIO_BUFFER_ID bufferId)
|
|
{
|
|
#if defined (FILEIO_CONFIG_MULTIPLE_BUFFER_MODE_DISABLE)
|
|
disk = disk->bufferStatusPtr->driveOwner;
|
|
|
|
if (disk == NULL)
|
|
{
|
|
return true;
|
|
}
|
|
#endif
|
|
switch (bufferId)
|
|
{
|
|
case FILEIO_BUFFER_DATA:
|
|
if (disk->bufferStatusPtr->flags.dataBufferNeedsWrite)
|
|
{
|
|
//if (!(*disk->driveConfig->funcSectorWrite)(disk->mediaParameters, disk->bufferStatusPtr->dataBufferCachedSector, disk->dataBuffer, false) )
|
|
if (!FILEIO_DRIVER_SectorWrite(disk->driveId, disk->bufferStatusPtr->dataBufferCachedSector, disk->dataBuffer, false) )
|
|
{
|
|
return false;
|
|
}
|
|
disk->bufferStatusPtr->flags.dataBufferNeedsWrite = false;
|
|
}
|
|
break;
|
|
case FILEIO_BUFFER_FAT:
|
|
if (disk->bufferStatusPtr->flags.fatBufferNeedsWrite)
|
|
{
|
|
uint32_t sector = disk->bufferStatusPtr->fatBufferCachedSector;
|
|
uint8_t i;
|
|
for (i = 0; i < disk->fatCopyCount; i++, sector += disk->fatSectorCount)
|
|
{
|
|
//if (! (*disk->driveConfig->funcSectorWrite)(disk->mediaParameters, sector, disk->fatBuffer, false) )
|
|
if(!FILEIO_DRIVER_SectorWrite(disk->driveId, sector, disk->fatBuffer, false))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
disk->bufferStatusPtr->flags.fatBufferNeedsWrite = false;
|
|
}
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
bool FILEIO_ShortFileNameCompare (uint8_t * fileName1, uint8_t * fileName2, uint8_t mode)
|
|
{
|
|
if ((mode & FILEIO_SEARCH_PARTIAL_STRING_SEARCH) == FILEIO_SEARCH_PARTIAL_STRING_SEARCH)
|
|
{
|
|
uint8_t character;
|
|
uint8_t i;
|
|
|
|
// Compare short filename
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
character = fileName1[i];
|
|
if (character == '*')
|
|
break;
|
|
if (character != '?')
|
|
{
|
|
if (character != fileName2[i])
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Compare extension
|
|
for (i = 8; i < FILEIO_FILE_NAME_LENGTH_8P3_NO_RADIX; i++)
|
|
{
|
|
character = fileName1[i];
|
|
if (character == '*')
|
|
break;
|
|
if (character != '?')
|
|
{
|
|
if (character != fileName2[i])
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If we need an exact match, just use memcmp
|
|
return (memcmp(fileName1, fileName2, FILEIO_FILE_NAME_LENGTH_8P3_NO_RADIX) == 0) ? true : false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
uint32_t FILEIO_ClusterToSector(FILEIO_DRIVE * disk, uint32_t cluster)
|
|
{
|
|
uint32_t sector;
|
|
|
|
// Rt: Settings based on FAT type
|
|
switch (disk->type)
|
|
{
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT32:
|
|
// In FAT32, there is no separate ROOT region. It is as well stored in DATA region
|
|
sector = (((uint32_t)cluster-2) * disk->sectorsPerCluster) + disk->firstDataSector;
|
|
break;
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT12:
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT16:
|
|
default:
|
|
// The root dir takes up cluster 0 and 1
|
|
if((cluster == 0) || (cluster == 1))
|
|
sector = disk->firstRootSector + cluster;
|
|
else
|
|
sector = (((uint32_t)cluster-2) * disk->sectorsPerCluster) + disk->firstDataSector;
|
|
break;
|
|
}
|
|
|
|
return(sector);
|
|
|
|
}
|
|
|
|
uint32_t FILEIO_FATRead (FILEIO_DRIVE * disk, uint32_t currentCluster)
|
|
{
|
|
uint8_t q;
|
|
uint32_t p, l; // "l" is the sector Address
|
|
uint32_t c = 0, d, ClusterFailValue,LastClusterLimit; // ClusterEntries
|
|
|
|
/* Settings based on FAT type */
|
|
switch (disk->type)
|
|
{
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT32:
|
|
p = (uint32_t)currentCluster * 4;
|
|
q = 0; // "q" not used for FAT32, only initialized to remove a warning
|
|
ClusterFailValue = FILEIO_CLUSTER_VALUE_FAT32_FAIL;
|
|
LastClusterLimit = FILEIO_CLUSTER_VALUE_FAT32_EOF;
|
|
break;
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT12:
|
|
p = (uint32_t) currentCluster *3; // Mulby1.5 to find cluster pos in FAT
|
|
q = p&1;
|
|
p >>= 1;
|
|
ClusterFailValue = FILEIO_CLUSTER_VALUE_FAT16_FAIL;
|
|
LastClusterLimit = FILEIO_CLUSTER_VALUE_FAT12_EOF;
|
|
break;
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT16:
|
|
default:
|
|
p = (uint32_t)currentCluster *2; // Mulby 2 to find cluster pos in FAT
|
|
q = 0; // "q" not used for FAT16, only initialized to remove a warning
|
|
ClusterFailValue = FILEIO_CLUSTER_VALUE_FAT16_FAIL;
|
|
LastClusterLimit = FILEIO_CLUSTER_VALUE_FAT16_EOF;
|
|
break;
|
|
}
|
|
|
|
l = disk->firstFatSector + (p / disk->sectorSize); //
|
|
p &= disk->sectorSize - 1; // Restrict 'p' within the FATbuffer size
|
|
|
|
// Check if the appropriate FAT sector is already loaded
|
|
if (disk->bufferStatusPtr->fatBufferCachedSector == l)
|
|
{
|
|
if (disk->type == FILEIO_FILE_SYSTEM_TYPE_FAT32)
|
|
{
|
|
c = ReadRam32bit (disk->fatBuffer, p);
|
|
}
|
|
else
|
|
{
|
|
if(disk->type == FILEIO_FILE_SYSTEM_TYPE_FAT16)
|
|
{
|
|
c = ReadRam16bit (disk->fatBuffer, p);
|
|
}
|
|
else if(disk->type == FILEIO_FILE_SYSTEM_TYPE_FAT12)
|
|
{
|
|
c = *(disk->fatBuffer + p);
|
|
if (q)
|
|
{
|
|
c >>= 4;
|
|
}
|
|
// Check if the MSB is across the sector boundry
|
|
p = (p +1) & (disk->sectorSize-1);
|
|
if (p == 0)
|
|
{
|
|
// Start by writing the sector we just worked on to the card
|
|
// if we need to
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
if (!FILEIO_FlushBuffer (disk, FILEIO_BUFFER_FAT))
|
|
{
|
|
return ClusterFailValue;
|
|
}
|
|
#endif
|
|
//if (!(*disk->driveConfig->funcSectorRead) (disk->mediaParameters, l+1, disk->fatBuffer))
|
|
if (!FILEIO_DRIVER_SectorRead(disk->driveId, l+1, disk->fatBuffer))
|
|
{
|
|
disk->bufferStatusPtr->fatBufferCachedSector = 0xFFFFFFFF;
|
|
return ClusterFailValue;
|
|
}
|
|
else
|
|
{
|
|
disk->bufferStatusPtr->fatBufferCachedSector = l + 1;
|
|
}
|
|
}
|
|
d = *(disk->fatBuffer + p);
|
|
if (q)
|
|
{
|
|
c += (d <<4);
|
|
}
|
|
else
|
|
{
|
|
c += ((d & 0x0F)<<8);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If there's a currently open FAT sector,
|
|
// write it back before reading into the buffer
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
if (!FILEIO_FlushBuffer (disk, FILEIO_BUFFER_FAT))
|
|
{
|
|
return ClusterFailValue;
|
|
}
|
|
#endif
|
|
//if (!(*disk->driveConfig->funcSectorRead) (disk->mediaParameters, l, disk->fatBuffer))
|
|
if (!FILEIO_DRIVER_SectorRead(disk->driveId, l, disk->fatBuffer))
|
|
{
|
|
disk->bufferStatusPtr->fatBufferCachedSector = 0xFFFFFFFF; // Note: It is Sector not Cluster.
|
|
return ClusterFailValue;
|
|
}
|
|
else
|
|
{
|
|
disk->bufferStatusPtr->fatBufferCachedSector = l;
|
|
|
|
if (disk->type == FILEIO_FILE_SYSTEM_TYPE_FAT32)
|
|
{
|
|
c = ReadRam32bit (disk->fatBuffer, p);
|
|
}
|
|
else
|
|
{
|
|
if(disk->type == FILEIO_FILE_SYSTEM_TYPE_FAT16)
|
|
{
|
|
c = ReadRam16bit (disk->fatBuffer, p);
|
|
}
|
|
else if (disk->type == FILEIO_FILE_SYSTEM_TYPE_FAT12)
|
|
{
|
|
c = *(disk->fatBuffer + p);
|
|
if (q)
|
|
{
|
|
c >>= 4;
|
|
}
|
|
p = (p +1) & (disk->sectorSize-1);
|
|
d = *(disk->fatBuffer + p);
|
|
if (q)
|
|
{
|
|
c += (d <<4);
|
|
}
|
|
else
|
|
{
|
|
c += ((d & 0x0F)<<8);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Normalize it so 0xFFFF is an error
|
|
if (c >= LastClusterLimit)
|
|
{
|
|
c = LastClusterLimit;
|
|
}
|
|
|
|
return c;
|
|
} // ReadFAT
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
FILEIO_ERROR_TYPE FILEIO_EraseFile (FILEIO_OBJECT * filePtr, uint16_t * entryHandle, bool eraseData)
|
|
{
|
|
FILEIO_ERROR_TYPE error;
|
|
FILEIO_DIRECTORY_ENTRY * entry;
|
|
FILEIO_DRIVE * disk = filePtr->disk;
|
|
FILEIO_DIRECTORY directory;
|
|
uint32_t currentCluster;
|
|
uint16_t currentClusterOffset;
|
|
uint16_t tempEntryHandle = *entryHandle;
|
|
uint8_t sequenceNumber;
|
|
|
|
error = FILEIO_ERROR_ERASE_FAIL;
|
|
|
|
directory.drive = filePtr->disk;
|
|
directory.cluster = filePtr->baseClusterDir;
|
|
currentCluster = filePtr->baseClusterDir;
|
|
if (currentCluster == 0)
|
|
{
|
|
currentCluster = disk->firstRootCluster;
|
|
}
|
|
currentClusterOffset = 0;
|
|
|
|
// Cache the short file entry
|
|
entry = FILEIO_DirectoryEntryCache (&directory, &error, ¤tCluster, ¤tClusterOffset, tempEntryHandle);
|
|
|
|
do
|
|
{
|
|
if (entry == NULL)
|
|
{
|
|
disk->error = error;
|
|
return error;
|
|
}
|
|
|
|
sequenceNumber = *((uint8_t *)entry);
|
|
|
|
if (((uint8_t)entry->name[0] == FILEIO_DIRECTORY_ENTRY_DELETED) || (entry->name[0] == FILEIO_DIRECTORY_ENTRY_EMPTY))
|
|
{
|
|
disk->error = FILEIO_ERROR_FILE_NOT_FOUND;
|
|
return FILEIO_ERROR_FILE_NOT_FOUND;
|
|
}
|
|
else
|
|
{
|
|
entry->name[0] = FILEIO_DIRECTORY_ENTRY_DELETED;
|
|
// Mark the cached sector as needing a write.
|
|
disk->bufferStatusPtr->flags.dataBufferNeedsWrite = true;
|
|
}
|
|
|
|
if (((entry->attributes == FILEIO_ATTRIBUTE_LONG_NAME) && ((sequenceNumber & 0x40) == 0x40)) || (tempEntryHandle == 0))
|
|
{
|
|
break;
|
|
}
|
|
|
|
tempEntryHandle--;
|
|
|
|
// Cache the previous entry
|
|
entry = FILEIO_DirectoryEntryCache (&directory, &error, ¤tCluster, ¤tClusterOffset, tempEntryHandle);
|
|
} while (entry->attributes == FILEIO_ATTRIBUTE_LONG_NAME);
|
|
|
|
if (error == FILEIO_ERROR_NONE)
|
|
{
|
|
// Check to make sure someone isn't trying to erase the root directory. This should never happen.
|
|
if (filePtr->firstCluster != disk->firstRootCluster)
|
|
{
|
|
if (eraseData)
|
|
{
|
|
error = FILEIO_EraseClusterChain (filePtr->firstCluster, disk) ? FILEIO_ERROR_NONE : FILEIO_ERROR_ERASE_FAIL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!FILEIO_FlushBuffer (filePtr->disk, FILEIO_BUFFER_DATA))
|
|
{
|
|
((FILEIO_DRIVE *)filePtr->disk)->error = FILEIO_ERROR_WRITE;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
if (!FILEIO_FlushBuffer (filePtr->disk, FILEIO_BUFFER_FAT))
|
|
{
|
|
((FILEIO_DRIVE *)filePtr->disk)->error = FILEIO_ERROR_WRITE;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
disk->error = error;
|
|
return error;
|
|
}
|
|
#endif
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
FILEIO_ERROR_TYPE FILEIO_EraseClusterChain (uint32_t cluster, FILEIO_DRIVE * disk)
|
|
{
|
|
uint32_t nextCluster, clusterFailed, clusterFinal;
|
|
FILEIO_ERROR_TYPE error = FILEIO_ERROR_NONE;
|
|
|
|
switch (disk->type)
|
|
{
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT32:
|
|
clusterFailed = FILEIO_CLUSTER_VALUE_FAT32_FAIL;
|
|
clusterFinal = FILEIO_CLUSTER_VALUE_FAT32_EOF;
|
|
break;
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT12:
|
|
clusterFailed = FILEIO_CLUSTER_VALUE_FAT16_FAIL;
|
|
clusterFinal = FILEIO_CLUSTER_VALUE_FAT12_EOF;
|
|
break;
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT16:
|
|
default:
|
|
clusterFailed = FILEIO_CLUSTER_VALUE_FAT16_FAIL;
|
|
clusterFinal = FILEIO_CLUSTER_VALUE_FAT16_EOF;
|
|
break;
|
|
}
|
|
|
|
if ((cluster == 0) || (cluster == 1))
|
|
{
|
|
error = FILEIO_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
else
|
|
{
|
|
while (error == FILEIO_ERROR_NONE)
|
|
{
|
|
if ((nextCluster = FILEIO_FATRead (disk, cluster)) == clusterFailed)
|
|
{
|
|
error = FILEIO_ERROR_DONE;
|
|
}
|
|
else
|
|
{
|
|
if ((nextCluster == 0) || (nextCluster == 1))
|
|
{
|
|
error = FILEIO_ERROR_INVALID_ARGUMENT;
|
|
}
|
|
else
|
|
{
|
|
if (nextCluster >= clusterFinal)
|
|
{
|
|
error = FILEIO_ERROR_DONE;
|
|
}
|
|
|
|
if (FILEIO_FATWrite (disk, cluster, FILEIO_CLUSTER_VALUE_EMPTY, false) == clusterFailed)
|
|
{
|
|
error = FILEIO_ERROR_WRITE;
|
|
}
|
|
|
|
cluster = nextCluster;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FILEIO_FATWrite (disk, 0, 0, true);
|
|
|
|
return error;
|
|
}
|
|
#endif
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
uint32_t FILEIO_FATWrite (FILEIO_DRIVE *disk, uint32_t currentCluster, uint32_t value, uint8_t forceWrite)
|
|
{
|
|
uint8_t q, c;
|
|
uint32_t p, l, clusterFailValue;
|
|
FILEIO_BUFFER_STATUS * statusPtr = disk->bufferStatusPtr;
|
|
|
|
if ((disk->type != FILEIO_FILE_SYSTEM_TYPE_FAT32) && (disk->type != FILEIO_FILE_SYSTEM_TYPE_FAT16) && (disk->type != FILEIO_FILE_SYSTEM_TYPE_FAT12))
|
|
{
|
|
return FILEIO_CLUSTER_VALUE_FAT32_FAIL;
|
|
}
|
|
|
|
/* Settings based on FAT type */
|
|
switch (disk->type)
|
|
{
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT32:
|
|
clusterFailValue = FILEIO_CLUSTER_VALUE_FAT32_FAIL;
|
|
break;
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT12:
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT16:
|
|
default:
|
|
clusterFailValue = FILEIO_CLUSTER_VALUE_FAT16_FAIL;
|
|
break;
|
|
}
|
|
|
|
// The only purpose for calling this function with forceWrite
|
|
// is to write the current FAT sector to the card
|
|
if (forceWrite)
|
|
{
|
|
FILEIO_FlushBuffer (disk, FILEIO_BUFFER_FAT);
|
|
return 0;
|
|
}
|
|
|
|
/* Settings based on FAT type */
|
|
switch (disk->type)
|
|
{
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT32:
|
|
p = (uint32_t)currentCluster *4; // "p" is the position in "gFATBuffer" for corresponding cluster.
|
|
q = 0; // "q" not used for FAT32, only initialized to remove a warning
|
|
break;
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT12:
|
|
p = (uint32_t) currentCluster * 3; // "p" is the position in "gFATBuffer" for corresponding cluster.
|
|
q = p & 1; // Odd or even?
|
|
p >>= 1;
|
|
break;
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT16:
|
|
default:
|
|
p = (uint32_t) currentCluster *2; // "p" is the position in "gFATBuffer" for corresponding cluster.
|
|
q = 0; // "q" not used for FAT16, only initialized to remove a warning
|
|
break;
|
|
}
|
|
|
|
l = disk->firstFatSector + (p / disk->sectorSize); //
|
|
p &= disk->sectorSize - 1; // Restrict 'p' within the FATbuffer size
|
|
|
|
if (disk->bufferStatusPtr->fatBufferCachedSector != l)
|
|
{
|
|
// If we are loading a new sector then write
|
|
// the current one to the card if we need to
|
|
if (!FILEIO_FlushBuffer (disk, FILEIO_BUFFER_FAT))
|
|
{
|
|
return clusterFailValue;
|
|
}
|
|
|
|
// Load the new sector
|
|
//if (!(*disk->driveConfig->funcSectorRead) (disk->mediaParameters, l, disk->fatBuffer))
|
|
if (!FILEIO_DRIVER_SectorRead(disk->driveId, l, disk->fatBuffer))
|
|
{
|
|
statusPtr->fatBufferCachedSector = 0xFFFFFFFF;
|
|
return clusterFailValue;
|
|
}
|
|
else
|
|
{
|
|
statusPtr->fatBufferCachedSector = l;
|
|
}
|
|
}
|
|
|
|
if (disk->type == FILEIO_FILE_SYSTEM_TYPE_FAT32) // Refer page 16 of FAT requirement.
|
|
{
|
|
*(disk->fatBuffer + p) = ((value & 0x000000ff)); // lsb,1st uint8_t of cluster value
|
|
*(disk->fatBuffer + p+1) = ((value & 0x0000ff00) >> 8);
|
|
*(disk->fatBuffer + p+2) = ((value & 0x00ff0000) >> 16);
|
|
*(disk->fatBuffer + p+3) = ((value & 0x0f000000) >> 24); // the MSB nibble is supposed to be "0" in FAT32. So mask it.
|
|
}
|
|
else
|
|
{
|
|
if (disk->type == FILEIO_FILE_SYSTEM_TYPE_FAT16)
|
|
{
|
|
*(disk->fatBuffer+ p) = value; //lsB
|
|
*(disk->fatBuffer + p+1) = ((value&0x0000ff00) >> 8); // msB
|
|
}
|
|
else if (disk->type == FILEIO_FILE_SYSTEM_TYPE_FAT12)
|
|
{
|
|
// Get the current uint8_t from the FAT
|
|
c = *(disk->fatBuffer + p);
|
|
if (q)
|
|
{
|
|
c = ((value & 0x0F) << 4) | ( c & 0x0F);
|
|
}
|
|
else
|
|
{
|
|
c = (value & 0xFF);
|
|
}
|
|
// Write in those bits
|
|
*(disk->fatBuffer + p) = c;
|
|
|
|
// FAT12 entries can cross sector boundaries
|
|
// Check if we need to load a new sector
|
|
p = (p +1) & (disk->sectorSize-1);
|
|
if (p == 0)
|
|
{
|
|
// call this function to update the FAT on the card
|
|
if (!FILEIO_FlushBuffer (disk, FILEIO_BUFFER_FAT))
|
|
{
|
|
return clusterFailValue;
|
|
}
|
|
|
|
// Load the next sector
|
|
//if (!(*disk->driveConfig->funcSectorRead) (disk->mediaParameters, l +1, disk->fatBuffer))
|
|
if (!FILEIO_DRIVER_SectorRead(disk->driveId, l +1, disk->fatBuffer))
|
|
{
|
|
statusPtr->fatBufferCachedSector = 0xFFFFFFFF;
|
|
return clusterFailValue;
|
|
}
|
|
else
|
|
{
|
|
statusPtr->fatBufferCachedSector = l + 1;
|
|
}
|
|
}
|
|
|
|
// Get the second uint8_t of the table entry
|
|
c = *(disk->fatBuffer + p);
|
|
if (q)
|
|
{
|
|
c = (value >> 4);
|
|
}
|
|
else
|
|
{
|
|
c = ((value >> 8) & 0x0F) | (c & 0xF0);
|
|
}
|
|
*(disk->fatBuffer + p) = c;
|
|
}
|
|
}
|
|
statusPtr->flags.fatBufferNeedsWrite = true;
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
FILEIO_ERROR_TYPE FILEIO_NextClusterGet (FILEIO_OBJECT * fo, uint32_t count)
|
|
{
|
|
uint32_t nextCluster, currentCluster, clusterFailValue, lastClustervalue;
|
|
FILEIO_ERROR_TYPE error = FILEIO_ERROR_NONE;
|
|
FILEIO_DRIVE * disk;
|
|
|
|
disk = fo->disk;
|
|
|
|
/* Settings based on FAT type */
|
|
switch (disk->type)
|
|
{
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT32:
|
|
lastClustervalue = FILEIO_CLUSTER_VALUE_FAT32_EOF;
|
|
clusterFailValue = FILEIO_CLUSTER_VALUE_FAT32_FAIL;
|
|
break;
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT12:
|
|
lastClustervalue = FILEIO_CLUSTER_VALUE_FAT12_EOF;
|
|
clusterFailValue = FILEIO_CLUSTER_VALUE_FAT16_FAIL;
|
|
break;
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT16:
|
|
default:
|
|
lastClustervalue = FILEIO_CLUSTER_VALUE_FAT16_EOF;
|
|
clusterFailValue = FILEIO_CLUSTER_VALUE_FAT16_FAIL;
|
|
break;
|
|
}
|
|
|
|
// loop n times
|
|
do
|
|
{
|
|
// get the next cluster link from FAT
|
|
currentCluster = fo->currentCluster;
|
|
if ((nextCluster = FILEIO_FATRead( disk, currentCluster)) == clusterFailValue)
|
|
{
|
|
error = FILEIO_ERROR_BAD_SECTOR_READ;
|
|
}
|
|
else
|
|
{
|
|
// check if cluster value is valid
|
|
if (nextCluster >= (disk->partitionClusterCount + 2))
|
|
{
|
|
error = FILEIO_ERROR_INVALID_CLUSTER;
|
|
}
|
|
|
|
// compare against max value of a cluster in FAT
|
|
// return if eof
|
|
if (nextCluster >= lastClustervalue) // check against eof
|
|
{
|
|
error = FILEIO_ERROR_EOF;
|
|
}
|
|
}
|
|
|
|
// update the file object structure
|
|
fo->currentCluster = nextCluster;
|
|
|
|
} while ((--count > 0) && (error == FILEIO_ERROR_NONE));// loop end
|
|
|
|
return(error);
|
|
} // get next cluster
|
|
|
|
|
|
int FILEIO_Close(FILEIO_OBJECT * filePtr)
|
|
{
|
|
int result = FILEIO_RESULT_SUCCESS;
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
result = FILEIO_Flush (filePtr);
|
|
#endif
|
|
|
|
filePtr->flags.readEnabled = false;
|
|
filePtr->flags.writeEnabled = false;
|
|
|
|
return result;
|
|
}
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
int FILEIO_Flush (FILEIO_OBJECT * filePtr)
|
|
{
|
|
int result = FILEIO_RESULT_SUCCESS;
|
|
|
|
FILEIO_TIMESTAMP timeStamp;
|
|
FILEIO_DIRECTORY_ENTRY * entry;
|
|
FILEIO_DIRECTORY directory;
|
|
uint32_t currentCluster;
|
|
uint16_t currentClusterOffset = 0;
|
|
FILEIO_ERROR_TYPE error;
|
|
|
|
#if defined (FILEIO_CONFIG_MULTIPLE_BUFFER_MODE_DISABLE)
|
|
if (FILEIO_GetSingleBuffer (filePtr->disk) != FILEIO_RESULT_SUCCESS)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
if(filePtr->flags.writeEnabled)
|
|
{
|
|
// Write the current data sector to the disk
|
|
if (!FILEIO_FlushBuffer (filePtr->disk, FILEIO_BUFFER_DATA))
|
|
{
|
|
((FILEIO_DRIVE *)filePtr->disk)->error = FILEIO_ERROR_WRITE;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
// Write the current FAT sector to the disk
|
|
if (!FILEIO_FlushBuffer (filePtr->disk, FILEIO_BUFFER_FAT))
|
|
{
|
|
((FILEIO_DRIVE *)filePtr->disk)->error = FILEIO_ERROR_WRITE;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
// Read the FAT entry from the physical media. This is required because
|
|
// some physical media cache the entries in RAM and only write them
|
|
// after a time expires for until the sector is accessed again.
|
|
FILEIO_FATRead (filePtr->disk, filePtr->currentCluster);
|
|
|
|
directory.drive = filePtr->disk;
|
|
directory.cluster = filePtr->baseClusterDir;
|
|
currentCluster = filePtr->baseClusterDir;
|
|
// Get the file entry
|
|
entry = FILEIO_DirectoryEntryCache (&directory, &error, ¤tCluster, ¤tClusterOffset, filePtr->entry);
|
|
|
|
if (entry == NULL)
|
|
{
|
|
((FILEIO_DRIVE *)filePtr->disk)->error = FILEIO_ERROR_BAD_CACHE_READ;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
// FILEIO_GetTimestamp(&timeStamp);
|
|
// if (timestampGet != NULL)
|
|
// {
|
|
// (*timestampGet)(&timeStamp);
|
|
// }
|
|
|
|
// update the time
|
|
entry->writeTime = timeStamp.time.value;
|
|
entry->writeDate = timeStamp.date.value;
|
|
|
|
entry->fileSize = filePtr->size;
|
|
|
|
entry->attributes = filePtr->attributes;
|
|
|
|
((FILEIO_DRIVE *)filePtr->disk)->bufferStatusPtr->flags.dataBufferNeedsWrite = true;
|
|
|
|
// just write the last entry in
|
|
if(FILEIO_FlushBuffer (filePtr->disk, FILEIO_BUFFER_DATA))
|
|
{
|
|
// Read the folder entry from the physical media. This is required because
|
|
// some physical media cache the entries in RAM and only write them
|
|
// after a time expires for until the sector is accessed again.
|
|
((FILEIO_DRIVE *)filePtr->disk)->error = FILEIO_ForceRecache (filePtr->disk);
|
|
if (((FILEIO_DRIVE *)filePtr->disk)->error == FILEIO_ERROR_NONE)
|
|
{
|
|
result = FILEIO_RESULT_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
result = FILEIO_RESULT_FAILURE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
((FILEIO_DRIVE *)filePtr->disk)->error = FILEIO_ERROR_WRITE;
|
|
result = FILEIO_RESULT_FAILURE;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
long FILEIO_Tell (FILEIO_OBJECT * filePtr)
|
|
{
|
|
((FILEIO_DRIVE *)filePtr->disk)->error = FILEIO_ERROR_NONE;
|
|
return (filePtr->absoluteOffset);
|
|
}
|
|
|
|
int FILEIO_Seek(FILEIO_OBJECT * filePtr, int32_t offset, int whence)
|
|
{
|
|
uint32_t numsector, temp; // lba of first sector of first cluster
|
|
FILEIO_DRIVE* disk; // pointer to disk structure
|
|
uint8_t test;
|
|
long offset2 = offset;
|
|
|
|
disk = filePtr->disk;
|
|
|
|
#if defined (FILEIO_CONFIG_MULTIPLE_BUFFER_MODE_DISABLE)
|
|
if (FILEIO_GetSingleBuffer (disk) != FILEIO_RESULT_SUCCESS)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
#endif
|
|
|
|
switch(whence)
|
|
{
|
|
case FILEIO_SEEK_CUR:
|
|
// Apply the offset to the current position
|
|
offset2 += filePtr->absoluteOffset;
|
|
break;
|
|
case FILEIO_SEEK_END:
|
|
// Apply the offset to the end of the file
|
|
offset2 = filePtr->size - offset2;
|
|
break;
|
|
case FILEIO_SEEK_SET:
|
|
// automatically there
|
|
default:
|
|
break;
|
|
}
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
if (!FILEIO_FlushBuffer (filePtr->disk, FILEIO_BUFFER_DATA))
|
|
{
|
|
disk->error = FILEIO_ERROR_WRITE;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
#endif
|
|
|
|
// start from the beginning
|
|
filePtr->currentCluster = filePtr->firstCluster;
|
|
|
|
if (offset2 > filePtr->size)
|
|
{
|
|
disk->error = FILEIO_ERROR_INVALID_ARGUMENT;
|
|
return FILEIO_RESULT_FAILURE; // past the limits
|
|
}
|
|
else
|
|
{
|
|
// set the new postion
|
|
filePtr->absoluteOffset = offset2;
|
|
|
|
// figure out how many sectors
|
|
numsector = offset2 / disk->sectorSize;
|
|
|
|
// figure out how many uint8_ts off of the offset
|
|
offset2 = offset2 - (numsector * disk->sectorSize);
|
|
filePtr->currentOffset = offset2;
|
|
|
|
// figure out how many clusters
|
|
temp = numsector / disk->sectorsPerCluster;
|
|
|
|
// figure out the stranded sectors
|
|
numsector = numsector - (disk->sectorsPerCluster * temp);
|
|
filePtr->currentSector = numsector;
|
|
|
|
// if we are in the current cluster stay there
|
|
if (temp > 0)
|
|
{
|
|
test = FILEIO_NextClusterGet(filePtr, temp);
|
|
if (test != FILEIO_ERROR_NONE)
|
|
{
|
|
if (test == FILEIO_ERROR_EOF)
|
|
{
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
if (filePtr->flags.writeEnabled)
|
|
{
|
|
// load the previous cluster
|
|
filePtr->currentCluster = filePtr->firstCluster;
|
|
// Don't perform this operation if there's only one cluster
|
|
if (temp != 1)
|
|
{
|
|
test = FILEIO_NextClusterGet(filePtr, temp - 1);
|
|
}
|
|
if (FILEIO_ClusterAllocate (disk, &filePtr->currentCluster, false) != FILEIO_ERROR_NONE)
|
|
{
|
|
disk->error = FILEIO_ERROR_COULD_NOT_GET_CLUSTER;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
// sec and currentOffset should already be zero
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
filePtr->currentCluster = filePtr->firstCluster;
|
|
if (temp != 1)
|
|
{
|
|
test = FILEIO_NextClusterGet(filePtr, temp - 1);
|
|
}
|
|
else
|
|
{
|
|
test = FILEIO_ERROR_NONE;
|
|
}
|
|
if (test != FILEIO_ERROR_NONE)
|
|
{
|
|
disk->error = FILEIO_ERROR_COULD_NOT_GET_CLUSTER;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
filePtr->currentOffset = disk->sectorSize;
|
|
filePtr->currentSector = disk->sectorsPerCluster - 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
disk->error = FILEIO_ERROR_COULD_NOT_GET_CLUSTER;
|
|
return FILEIO_RESULT_FAILURE; // past the limits
|
|
}
|
|
}
|
|
}
|
|
|
|
// Determine the lba of the selected sector and load
|
|
temp = FILEIO_ClusterToSector(disk, filePtr->currentCluster);
|
|
|
|
// now the extra sectors
|
|
numsector = filePtr->currentSector;
|
|
temp += numsector;
|
|
|
|
//if(!(*disk->driveConfig->funcSectorRead)(disk->mediaParameters, temp, disk->dataBuffer) )
|
|
if(!FILEIO_DRIVER_SectorRead(disk->driveId, temp, disk->dataBuffer) )
|
|
{
|
|
disk->error = FILEIO_ERROR_BAD_CACHE_READ;
|
|
return FILEIO_RESULT_FAILURE; // Bad read
|
|
}
|
|
disk->bufferStatusPtr->dataBufferCachedSector = temp;
|
|
}
|
|
|
|
disk->error = FILEIO_ERROR_NONE;
|
|
|
|
return FILEIO_RESULT_SUCCESS;
|
|
}
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
size_t FILEIO_Write (const void * buffer, size_t size, size_t count, FILEIO_OBJECT * filePtr)
|
|
{
|
|
FILEIO_ERROR_TYPE error;
|
|
uint8_t * data = (uint8_t *) buffer;
|
|
FILEIO_DRIVE * disk = filePtr->disk;
|
|
uint32_t currentSector;
|
|
size_t dataWritten = 0;
|
|
uint16_t writeCount;
|
|
size_t length = size * count;
|
|
|
|
if (!filePtr->flags.writeEnabled)
|
|
{
|
|
disk->error = FILEIO_ERROR_READ_ONLY;
|
|
return 0;
|
|
}
|
|
|
|
// Check to see if the user has passed in no data
|
|
if (length == 0)
|
|
{
|
|
disk->error = FILEIO_ERROR_NONE;
|
|
return 0;
|
|
}
|
|
|
|
#if defined (FILEIO_CONFIG_MULTIPLE_BUFFER_MODE_DISABLE)
|
|
if (FILEIO_GetSingleBuffer (disk) != FILEIO_RESULT_SUCCESS)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
//if ((*disk->driveConfig->funcWriteProtectGet)(disk->mediaParameters))
|
|
if (FILEIO_DRIVER_WriteProtectStateGet(disk->driveId))
|
|
{
|
|
disk->error = FILEIO_ERROR_WRITE_PROTECTED;
|
|
return 0;
|
|
}
|
|
|
|
while (length != 0)
|
|
{
|
|
if (filePtr->currentOffset == disk->sectorSize)
|
|
{
|
|
filePtr->currentOffset = 0;
|
|
filePtr->currentSector++;
|
|
if (filePtr->currentSector == disk->sectorsPerCluster)
|
|
{
|
|
uint32_t tempCluster = filePtr->currentCluster;
|
|
filePtr->currentSector = 0;
|
|
// Load/allocate the next cluster
|
|
if ((error = FILEIO_NextClusterGet (filePtr, 1)) == FILEIO_ERROR_EOF)
|
|
{
|
|
filePtr->currentCluster = tempCluster;
|
|
// Allocate a new cluster
|
|
error = FILEIO_ClusterAllocate (disk, &filePtr->currentCluster, false);
|
|
}
|
|
|
|
if (error != FILEIO_ERROR_NONE)
|
|
{
|
|
disk->error = error;
|
|
return dataWritten;
|
|
}
|
|
}
|
|
}
|
|
|
|
currentSector = FILEIO_ClusterToSector (disk, filePtr->currentCluster);
|
|
currentSector += filePtr->currentSector;
|
|
|
|
// Cache the required sector, if necessary
|
|
if (disk->bufferStatusPtr->dataBufferCachedSector != currentSector)
|
|
{
|
|
if (!FILEIO_FlushBuffer (disk, FILEIO_BUFFER_DATA))
|
|
{
|
|
disk->error = FILEIO_ERROR_WRITE;
|
|
return FILEIO_ERROR_WRITE;
|
|
}
|
|
|
|
// if ((*disk->driveConfig->funcSectorRead) (disk->mediaParameters, currentSector, disk->dataBuffer) != true)
|
|
if (FILEIO_DRIVER_SectorRead(disk->driveId, currentSector, disk->dataBuffer) != true)
|
|
{
|
|
disk->error = FILEIO_ERROR_BAD_SECTOR_READ;
|
|
return dataWritten;
|
|
}
|
|
else
|
|
{
|
|
disk->bufferStatusPtr->dataBufferCachedSector = currentSector;
|
|
#if defined (FILEIO_CONFIG_MULTIPLE_BUFFER_MODE_DISABLE)
|
|
disk->bufferStatusPtr->driveOwner = disk;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
writeCount = ((disk->sectorSize - filePtr->currentOffset) > length) ? length : (disk->sectorSize - filePtr->currentOffset);
|
|
memcpy (disk->dataBuffer + filePtr->currentOffset, data, writeCount);
|
|
disk->bufferStatusPtr->flags.dataBufferNeedsWrite = true;
|
|
data += writeCount;
|
|
filePtr->currentOffset += writeCount;
|
|
dataWritten += writeCount;
|
|
length -= writeCount;
|
|
}
|
|
|
|
filePtr->size += dataWritten;
|
|
filePtr->absoluteOffset += dataWritten;
|
|
|
|
return dataWritten;
|
|
}
|
|
#endif
|
|
|
|
size_t FILEIO_Read (void * buffer, size_t size, size_t count, FILEIO_OBJECT * filePtr)
|
|
{
|
|
FILEIO_ERROR_TYPE error;
|
|
uint8_t * data = (uint8_t *) buffer;
|
|
FILEIO_DRIVE * disk = filePtr->disk;
|
|
uint32_t currentSector;
|
|
size_t dataRead = 0;
|
|
uint16_t readCount;
|
|
size_t length = size * count;
|
|
|
|
if (!filePtr->flags.readEnabled)
|
|
{
|
|
disk->error = FILEIO_ERROR_WRITE_ONLY;
|
|
return 0;
|
|
}
|
|
|
|
// Check to see if the user has passed in no data
|
|
if (length == 0)
|
|
{
|
|
disk->error = FILEIO_ERROR_NONE;
|
|
return 0;
|
|
}
|
|
|
|
#if defined (FILEIO_CONFIG_MULTIPLE_BUFFER_MODE_DISABLE)
|
|
if (FILEIO_GetSingleBuffer (disk) != FILEIO_RESULT_SUCCESS)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
while (length != 0)
|
|
{
|
|
if (filePtr->currentOffset == disk->sectorSize)
|
|
{
|
|
filePtr->currentOffset = 0;
|
|
filePtr->currentSector++;
|
|
if (filePtr->currentSector == disk->sectorsPerCluster)
|
|
{
|
|
filePtr->currentSector = 0;
|
|
// Load/allocate the next cluster
|
|
error = FILEIO_NextClusterGet (filePtr, 1);
|
|
|
|
if (error != FILEIO_ERROR_NONE)
|
|
{
|
|
disk->error = error;
|
|
return dataRead;
|
|
}
|
|
}
|
|
}
|
|
|
|
currentSector = FILEIO_ClusterToSector (disk, filePtr->currentCluster);
|
|
currentSector += filePtr->currentSector;
|
|
|
|
// Cache the required sector, if necessary
|
|
if (disk->bufferStatusPtr->dataBufferCachedSector != currentSector)
|
|
{
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
if (!FILEIO_FlushBuffer (disk, FILEIO_BUFFER_DATA))
|
|
{
|
|
disk->error = FILEIO_ERROR_WRITE;
|
|
return FILEIO_ERROR_WRITE;
|
|
}
|
|
#endif
|
|
|
|
//if ((*disk->driveConfig->funcSectorRead) (disk->mediaParameters, currentSector, disk->dataBuffer) != true)
|
|
if(FILEIO_DRIVER_SectorRead(disk->driveId, currentSector, disk->dataBuffer) != true)
|
|
{
|
|
disk->error = FILEIO_ERROR_BAD_SECTOR_READ;
|
|
return dataRead;
|
|
}
|
|
else
|
|
{
|
|
disk->bufferStatusPtr->dataBufferCachedSector = currentSector;
|
|
#if defined (FILEIO_CONFIG_MULTIPLE_BUFFER_MODE_DISABLE)
|
|
disk->bufferStatusPtr->driveOwner = disk;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
readCount = ((disk->sectorSize - filePtr->currentOffset) > length) ? length : (disk->sectorSize - filePtr->currentOffset);
|
|
if ((filePtr->size - filePtr->absoluteOffset) < readCount)
|
|
{
|
|
readCount = filePtr->size - filePtr->absoluteOffset;
|
|
length = readCount;
|
|
}
|
|
memcpy (data, disk->dataBuffer + filePtr->currentOffset, readCount);
|
|
data += readCount;
|
|
filePtr->currentOffset += readCount;
|
|
filePtr->absoluteOffset += readCount;
|
|
dataRead += readCount;
|
|
length -= readCount;
|
|
}
|
|
|
|
return dataRead;
|
|
}
|
|
|
|
bool FILEIO_Eof (FILEIO_OBJECT * filePtr)
|
|
{
|
|
return (filePtr->absoluteOffset == filePtr->size) ? true : false;
|
|
}
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
int FILEIO_Remove (const uint16_t * pathName)
|
|
{
|
|
FILEIO_OBJECT file;
|
|
FILEIO_OBJECT * filePtr = &file;
|
|
FILEIO_ERROR_TYPE error;
|
|
uint16_t entryHandle;
|
|
FILEIO_DIRECTORY directory;
|
|
uint8_t fileNameType;
|
|
uint32_t currentCluster;
|
|
uint16_t currentClusterOffset = 0;
|
|
uint16_t * fileName;
|
|
|
|
fileName = (uint16_t *)FILEIO_CacheDirectory (&directory, (uint16_t *)pathName, false);
|
|
|
|
if (fileName == NULL)
|
|
{
|
|
globalParameters.currentWorkingDirectory.drive->error = FILEIO_ERROR_INVALID_ARGUMENT;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
currentCluster = directory.cluster;
|
|
|
|
// if((*directory.drive->driveConfig->funcWriteProtectGet)(directory.drive->mediaParameters))
|
|
if(FILEIO_DRIVER_WriteProtectStateGet(directory.drive->driveId))
|
|
{
|
|
directory.drive->error = FILEIO_ERROR_WRITE_PROTECTED;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
fileNameType = FILEIO_FileNameTypeGet(fileName, false);
|
|
|
|
if ((fileNameType == FILEIO_NAME_INVALID) || (fileNameType == FILEIO_NAME_DOT))
|
|
{
|
|
directory.drive->error = FILEIO_ERROR_INVALID_FILENAME;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
else if (fileNameType == FILEIO_NAME_SHORT)
|
|
{
|
|
currentCluster = directory.cluster;
|
|
currentClusterOffset = 0;
|
|
// Short file name
|
|
FILEIO_FormatShortFileName (fileName, filePtr);
|
|
// Search in 'directory' for an entry matching filePtr->name, starting at entry 0 in directory->cluster and returning the result in filePtr
|
|
error = FILEIO_FindShortFileName (&directory, filePtr, (uint8_t *)filePtr->name, ¤tCluster, ¤tClusterOffset, 0, 0, FILEIO_SEARCH_ENTRY_MATCH);
|
|
}
|
|
else if (fileNameType == FILEIO_NAME_LONG)
|
|
{
|
|
// Long file name
|
|
currentCluster = directory.cluster;
|
|
currentClusterOffset = 0;
|
|
// Search in 'directory' for an entry matching fileName, starting at entry 0 in directory->cluster and returning the short file name in filePtr.
|
|
// The long file name will be cached in lfnData
|
|
filePtr->lfnPtr = (uint16_t *)fileName;
|
|
filePtr->lfnLen = FILEIO_strlen16 ((uint16_t *)fileName);
|
|
error = FILEIO_FindLongFileName (&directory, filePtr, ¤tCluster, ¤tClusterOffset, 0, 0, FILEIO_SEARCH_ENTRY_MATCH);
|
|
}
|
|
|
|
if (error == FILEIO_ERROR_NONE)
|
|
{
|
|
entryHandle = filePtr->entry;
|
|
if ((filePtr->attributes & FILEIO_ATTRIBUTE_DIRECTORY) == FILEIO_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
error = FILEIO_ERROR_DELETE_DIR;
|
|
}
|
|
else
|
|
{
|
|
error = FILEIO_EraseFile (filePtr, &entryHandle, true);
|
|
}
|
|
}
|
|
|
|
// Check to ensure no errors occured
|
|
if (error != FILEIO_ERROR_NONE)
|
|
{
|
|
directory.drive->error = error;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
return FILEIO_RESULT_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
int FILEIO_Rename (const uint16_t * oldPathname, const uint16_t * newFilename)
|
|
{
|
|
FILEIO_OBJECT file;
|
|
FILEIO_OBJECT * filePtr = &file;
|
|
FILEIO_ERROR_TYPE error;
|
|
FILEIO_DIRECTORY_ENTRY * entry;
|
|
uint16_t entryHandle;
|
|
FILEIO_DIRECTORY directory;
|
|
uint8_t oldFileNameType;
|
|
uint8_t newFileNameType;
|
|
uint32_t currentCluster;
|
|
uint16_t currentClusterOffset = 0;
|
|
uint16_t * oldFilename;
|
|
|
|
oldFilename = FILEIO_CacheDirectory (&directory, (uint16_t *)oldPathname, false);
|
|
|
|
if (oldFilename == NULL)
|
|
{
|
|
globalParameters.currentWorkingDirectory.drive->error = FILEIO_ERROR_INVALID_ARGUMENT;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
currentCluster = directory.cluster;
|
|
|
|
// if((*directory.drive->driveConfig->funcWriteProtectGet)(directory.drive->mediaParameters))
|
|
if(FILEIO_DRIVER_WriteProtectStateGet(directory.drive->driveId))
|
|
{
|
|
directory.drive->error = FILEIO_ERROR_WRITE_PROTECTED;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
// Check to see if the new filename already exists
|
|
newFileNameType = FILEIO_FileNameTypeGet(newFilename, false);
|
|
|
|
if ((newFileNameType == FILEIO_NAME_INVALID) || (newFileNameType == FILEIO_NAME_DOT))
|
|
{
|
|
directory.drive->error = FILEIO_ERROR_INVALID_FILENAME;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
else if (newFileNameType == FILEIO_NAME_SHORT)
|
|
{
|
|
currentCluster = directory.cluster;
|
|
currentClusterOffset = 0;
|
|
// Short file name
|
|
FILEIO_FormatShortFileName (newFilename, filePtr);
|
|
// Search in 'directory' for an entry matching filePtr->name, starting at entry 0 in directory->cluster and returning the result in filePtr
|
|
error = FILEIO_FindShortFileName (&directory, filePtr, (uint8_t *)filePtr->name, ¤tCluster, ¤tClusterOffset, 0, 0, FILEIO_SEARCH_ENTRY_MATCH);
|
|
}
|
|
else if (newFileNameType == FILEIO_NAME_LONG)
|
|
{
|
|
// Long file name
|
|
currentCluster = directory.cluster;
|
|
currentClusterOffset = 0;
|
|
// Search in 'directory' for an entry matching fileName, starting at entry 0 in directory->cluster and returning the short file name in filePtr.
|
|
// The long file name will be cached in lfnData
|
|
filePtr->lfnPtr = (uint16_t *)newFilename;
|
|
filePtr->lfnLen = FILEIO_strlen16 ((uint16_t *)newFilename);
|
|
error = FILEIO_FindLongFileName (&directory, filePtr, ¤tCluster, ¤tClusterOffset, 0, 0, FILEIO_SEARCH_ENTRY_MATCH);
|
|
}
|
|
|
|
if (error == FILEIO_ERROR_NONE)
|
|
{
|
|
directory.drive->error = FILEIO_ERROR_FILENAME_EXISTS;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
// Try to find the old filename
|
|
oldFileNameType = FILEIO_FileNameTypeGet(oldFilename, false);
|
|
|
|
currentClusterOffset = 0;
|
|
currentCluster = directory.cluster;
|
|
|
|
if ((oldFileNameType == FILEIO_NAME_INVALID) || (oldFileNameType == FILEIO_NAME_DOT))
|
|
{
|
|
directory.drive->error = FILEIO_ERROR_INVALID_FILENAME;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
else if (oldFileNameType == FILEIO_NAME_SHORT)
|
|
{
|
|
currentCluster = directory.cluster;
|
|
currentClusterOffset = 0;
|
|
// Short file name
|
|
FILEIO_FormatShortFileName (oldFilename, filePtr);
|
|
// Search in 'directory' for an entry matching filePtr->name, starting at entry 0 in directory->cluster and returning the result in filePtr
|
|
error = FILEIO_FindShortFileName (&directory, filePtr, (uint8_t *)filePtr->name, ¤tCluster, ¤tClusterOffset, 0, 0, FILEIO_SEARCH_ENTRY_MATCH);
|
|
}
|
|
else if (oldFileNameType == FILEIO_NAME_LONG)
|
|
{
|
|
// Long file name
|
|
currentCluster = directory.cluster;
|
|
currentClusterOffset = 0;
|
|
// Search in 'directory' for an entry matching fileName, starting at entry 0 in directory->cluster and returning the short file name in filePtr.
|
|
// The long file name will be cached in lfnData
|
|
filePtr->lfnPtr = (uint16_t *)oldFilename;
|
|
filePtr->lfnLen = FILEIO_strlen16 ((uint16_t *)oldFilename);
|
|
error = FILEIO_FindLongFileName (&directory, filePtr, ¤tCluster, ¤tClusterOffset, 0, 0, FILEIO_SEARCH_ENTRY_MATCH);
|
|
}
|
|
|
|
// Check to ensure that the old file was found
|
|
if (error != FILEIO_ERROR_NONE)
|
|
{
|
|
directory.drive->error = FILEIO_ERROR_FILE_NOT_FOUND;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
entryHandle = filePtr->entry;
|
|
|
|
// The file was found. Replace the name.
|
|
if ((oldFileNameType == FILEIO_NAME_SHORT) && (newFileNameType == FILEIO_NAME_SHORT))
|
|
{
|
|
entry = FILEIO_DirectoryEntryCache (&directory, &error, ¤tCluster, ¤tClusterOffset, entryHandle);
|
|
FILEIO_FormatShortFileName (newFilename, filePtr);
|
|
memcpy (entry->name, filePtr->name, FILEIO_FILE_NAME_LENGTH_8P3_NO_RADIX);
|
|
}
|
|
else
|
|
{
|
|
uint32_t oldCluster = filePtr->firstCluster;
|
|
uint32_t oldSize = filePtr->size;
|
|
|
|
// Erase the previous entry
|
|
if (FILEIO_EraseFile (filePtr, &entryHandle, false) != FILEIO_ERROR_NONE)
|
|
{
|
|
directory.drive->error = FILEIO_ERROR_ERASE_FAIL;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
if (newFileNameType == FILEIO_NAME_SHORT)
|
|
{
|
|
// Old file name was long, new file name is short
|
|
FILEIO_FormatShortFileName (newFilename, filePtr);
|
|
filePtr->lfnPtr = NULL;
|
|
}
|
|
else
|
|
{
|
|
// Old file name was short or long, new file name is long
|
|
filePtr->lfnPtr = (uint16_t *)newFilename;
|
|
filePtr->lfnLen = FILEIO_strlen16 ((uint16_t *)newFilename);
|
|
}
|
|
|
|
error = FILEIO_DirectoryEntryCreate (filePtr, &entryHandle, filePtr->attributes, false);
|
|
|
|
entry = FILEIO_DirectoryEntryCache (&directory, &error, ¤tCluster, ¤tClusterOffset, entryHandle);
|
|
|
|
// Store the existing file's location and size in the new entry
|
|
entry->firstClusterLow = (oldCluster & 0x0000FFFF);
|
|
entry->firstClusterHigh = (oldCluster & 0x0FFF0000) >> 16;
|
|
entry->fileSize = oldSize;
|
|
}
|
|
|
|
directory.drive->bufferStatusPtr->flags.dataBufferNeedsWrite = true;
|
|
|
|
// Try to flush the buffer (since the user can't manually flush the data to ensure it writes immediately)
|
|
if (!FILEIO_FlushBuffer (directory.drive, FILEIO_BUFFER_DATA))
|
|
{
|
|
directory.drive->error = FILEIO_ERROR_WRITE;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
return FILEIO_RESULT_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
FILEIO_ERROR_TYPE FILEIO_ErrorGet (uint16_t driveId)
|
|
{
|
|
FILEIO_DRIVE * drive = FILEIO_CharToDrive (driveId);
|
|
|
|
if (drive == NULL)
|
|
{
|
|
return FILEIO_ERROR_DRIVE_NOT_FOUND;
|
|
}
|
|
else
|
|
{
|
|
return drive->error;
|
|
}
|
|
}
|
|
|
|
void FILEIO_ErrorClear (uint16_t driveId)
|
|
{
|
|
FILEIO_DRIVE * drive = FILEIO_CharToDrive (driveId);
|
|
|
|
if (drive != NULL)
|
|
{
|
|
drive->error = FILEIO_ERROR_NONE;
|
|
}
|
|
}
|
|
|
|
int FILEIO_GetChar (FILEIO_OBJECT * handle)
|
|
{
|
|
char c;
|
|
|
|
if (FILEIO_Read(&c, 1, 1, handle) != 1)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
else
|
|
{
|
|
return (int)c;
|
|
}
|
|
}
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
int FILEIO_PutChar (char c, FILEIO_OBJECT * handle)
|
|
{
|
|
if (FILEIO_Write(&c, 1, 1, handle) != 1)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
else
|
|
{
|
|
return FILEIO_RESULT_SUCCESS;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if !defined (FILEIO_CONFIG_DIRECTORY_DISABLE)
|
|
int FILEIO_DirectoryChange (const uint16_t * path)
|
|
{
|
|
uint16_t * finalPath;
|
|
FILEIO_DIRECTORY directory;
|
|
uint16_t pathLen;
|
|
|
|
finalPath = FILEIO_CacheDirectory (&directory, (uint16_t *)path, false);
|
|
|
|
if (finalPath == NULL)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
pathLen = FILEIO_strlen16 (finalPath);
|
|
|
|
if (pathLen != 0)
|
|
{
|
|
// Try to change to the final directory
|
|
if (FILEIO_DirectoryChangeSingle (&directory, finalPath) != FILEIO_RESULT_SUCCESS)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
}
|
|
|
|
// Directory was changed successfully
|
|
globalParameters.currentWorkingDirectory.drive = directory.drive;
|
|
globalParameters.currentWorkingDirectory.cluster = directory.cluster;
|
|
|
|
return FILEIO_RESULT_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
#if !defined (FILEIO_CONFIG_DIRECTORY_DISABLE)
|
|
int FILEIO_DirectoryMake (const uint16_t * path)
|
|
{
|
|
uint16_t * finalPath;
|
|
FILEIO_DIRECTORY directory;
|
|
uint16_t pathLen;
|
|
|
|
finalPath = FILEIO_CacheDirectory (&directory, (uint16_t *)path, true);
|
|
|
|
if (finalPath == NULL)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
pathLen = FILEIO_strlen16 (finalPath);
|
|
|
|
if (pathLen != 0)
|
|
{
|
|
// Try to change to the final directory
|
|
if (FILEIO_DirectoryChangeSingle (&directory, finalPath) != FILEIO_RESULT_SUCCESS)
|
|
{
|
|
// If we couldn't change to that directory, it probably doesn't exist. Try to create it.
|
|
if (FILEIO_DirectoryMakeSingle (&directory, finalPath) != FILEIO_RESULT_SUCCESS)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
// Now that it exists, try to change to it.
|
|
if (FILEIO_DirectoryChangeSingle (&directory, finalPath) != FILEIO_RESULT_SUCCESS)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Directory were created successfully
|
|
return FILEIO_RESULT_SUCCESS;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
#if !defined (FILEIO_CONFIG_DIRECTORY_DISABLE)
|
|
int FILEIO_DirectoryRemove (const uint16_t * path)
|
|
{
|
|
uint16_t * finalPath;
|
|
FILEIO_DIRECTORY_ENTRY * entry;
|
|
FILEIO_ERROR_TYPE error;
|
|
FILEIO_DIRECTORY directory;
|
|
FILEIO_DIRECTORY deletedDirectory;
|
|
uint16_t pathLen;
|
|
uint32_t currentCluster;
|
|
uint16_t currentClusterOffset = 0;
|
|
uint16_t entryOffset = 2;
|
|
|
|
finalPath = FILEIO_CacheDirectory (&directory, (uint16_t *)path, false);
|
|
|
|
memcpy (&deletedDirectory, &directory, sizeof (FILEIO_DIRECTORY));
|
|
|
|
if (finalPath == NULL)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
// if ((*directory.drive->driveConfig->funcWriteProtectGet)(directory.drive->mediaParameters))
|
|
if (FILEIO_DRIVER_WriteProtectStateGet(directory.drive->driveId))
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
// Change to the final directory (if the user didn't terminate the path with a delimiter)
|
|
pathLen = FILEIO_strlen16 (finalPath);
|
|
if (pathLen != 0)
|
|
{
|
|
if (FILEIO_DirectoryChangeSingle (&deletedDirectory, finalPath) != FILEIO_RESULT_SUCCESS)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
}
|
|
|
|
// Don't let the user try to remove the root directory
|
|
if (deletedDirectory.cluster == deletedDirectory.drive->firstRootCluster)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
// Check to make sure that the final directory is empty so we don't orphan files
|
|
currentCluster = deletedDirectory.cluster;
|
|
do
|
|
{
|
|
entry = FILEIO_DirectoryEntryCache (&deletedDirectory, &error, ¤tCluster, ¤tClusterOffset, entryOffset);
|
|
if (entry == NULL)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
if ((entry->name[0] != FILEIO_DIRECTORY_ENTRY_EMPTY) && ((uint8_t)(entry->name[0]) != FILEIO_DIRECTORY_ENTRY_DELETED))
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
} while (entry->name[0] != FILEIO_DIRECTORY_ENTRY_EMPTY);
|
|
|
|
return FILEIO_DirectoryRemoveSingle (&directory, finalPath);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
#if !defined (FILEIO_CONFIG_DIRECTORY_DISABLE)
|
|
int FILEIO_DirectoryRemoveSingle (FILEIO_DIRECTORY * directory, uint16_t * path)
|
|
{
|
|
FILEIO_OBJECT file;
|
|
FILEIO_ERROR_TYPE error;
|
|
uint8_t fileNameType;
|
|
uint32_t currentCluster = directory->cluster;
|
|
uint16_t currentClusterOffset = 0;
|
|
|
|
fileNameType = FILEIO_FileNameTypeGet(path, false);
|
|
|
|
if ((fileNameType == FILEIO_NAME_INVALID) || (fileNameType == FILEIO_NAME_DOT))
|
|
{
|
|
directory->drive->error = FILEIO_ERROR_INVALID_FILENAME;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
else if (fileNameType == FILEIO_NAME_SHORT)
|
|
{
|
|
currentCluster = directory->cluster;
|
|
currentClusterOffset = 0;
|
|
// Short file name
|
|
FILEIO_FormatShortFileName (path, &file);
|
|
// Search in 'directory' for an entry matching filePtr->name, starting at entry 0 in directory->cluster and returning the result in filePtr
|
|
error = FILEIO_FindShortFileName (directory, &file, (uint8_t *)file.name, ¤tCluster, ¤tClusterOffset, 0, FILEIO_ATTRIBUTE_MASK, FILEIO_SEARCH_ENTRY_MATCH);
|
|
}
|
|
else if (fileNameType == FILEIO_NAME_LONG)
|
|
{
|
|
// Long file name
|
|
currentCluster = directory->cluster;
|
|
currentClusterOffset = 0;
|
|
// Search in 'directory' for an entry matching fileName, starting at entry 0 in directory->cluster and returning the short file name in filePtr.
|
|
// The long file name will be cached in lfnData
|
|
file.lfnPtr = (uint16_t *)path;
|
|
file.lfnLen = FILEIO_strlen16 ((uint16_t *)path);
|
|
error = FILEIO_FindLongFileName (directory, &file, ¤tCluster, ¤tClusterOffset, 0, 0, FILEIO_SEARCH_ENTRY_MATCH);
|
|
|
|
}
|
|
|
|
if (error != FILEIO_ERROR_NONE)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
if ((file.attributes & FILEIO_ATTRIBUTE_DIRECTORY) != FILEIO_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
directory->drive->error = FILEIO_ERROR_DELETE_FILE;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
if (FILEIO_EraseFile (&file, &file.entry, true) == FILEIO_ERROR_NONE)
|
|
{
|
|
return FILEIO_RESULT_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if !defined (FILEIO_CONFIG_DIRECTORY_DISABLE)
|
|
uint16_t FILEIO_DirectoryGetCurrent (uint16_t * buffer, uint16_t size)
|
|
{
|
|
uint16_t * bufferEnd;
|
|
FILEIO_DRIVE * drive = globalParameters.currentWorkingDirectory.drive;
|
|
uint32_t cluster = globalParameters.currentWorkingDirectory.cluster;
|
|
uint32_t currentCluster, currentClusterTemp;
|
|
uint16_t currentClusterOffset;
|
|
uint16_t entryOffset;
|
|
FILEIO_DIRECTORY directory;
|
|
FILEIO_DIRECTORY_ENTRY * entry;
|
|
FILEIO_ERROR_TYPE error;
|
|
uint32_t tempCluster;
|
|
int16_t i = 0, index = 0, tempIndex;
|
|
int16_t j;
|
|
bool bufferOverflow = false;
|
|
char aChar;
|
|
uint16_t charCount = 0;
|
|
const uint16_t dotdotPath[3] = {'.', '.', 0};
|
|
uint8_t checksum;
|
|
uint8_t * source;
|
|
|
|
memcpy (&directory, &globalParameters.currentWorkingDirectory, sizeof (FILEIO_DIRECTORY));
|
|
|
|
#if defined (FILEIO_CONFIG_MULTIPLE_BUFFER_MODE_DISABLE)
|
|
if (FILEIO_GetSingleBuffer (drive) != FILEIO_RESULT_SUCCESS)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
drive->error = FILEIO_ERROR_NONE;
|
|
|
|
// Set up the return value
|
|
if ((buffer == NULL) || (size == 0))
|
|
{
|
|
drive->error = FILEIO_ERROR_INVALID_ARGUMENT;
|
|
return 0;
|
|
}
|
|
|
|
bufferEnd = buffer + size - 1;
|
|
|
|
// Loop backwards though all subdirectories
|
|
while ((cluster != 0) && (cluster != drive->firstRootCluster))
|
|
{
|
|
// Change to parent directory
|
|
if (FILEIO_DirectoryChangeSingle(&directory, (uint16_t *)dotdotPath) == FILEIO_RESULT_FAILURE)
|
|
{
|
|
drive->error = FILEIO_ERROR_DIR_NOT_FOUND;
|
|
return 0;
|
|
}
|
|
|
|
// Try to find the directory entry for our directory
|
|
currentCluster = directory.cluster;
|
|
currentClusterOffset = 0;
|
|
if (currentCluster == 0)
|
|
{
|
|
currentCluster = drive->firstRootCluster;
|
|
entryOffset = 0;
|
|
}
|
|
else
|
|
{
|
|
entryOffset = 2;
|
|
}
|
|
|
|
do
|
|
{
|
|
entry = FILEIO_DirectoryEntryCache (&directory, &error, ¤tCluster, ¤tClusterOffset, entryOffset++);
|
|
if (((tempCluster = FILEIO_FullClusterNumberGet(entry)) == cluster) && ((uint8_t)entry->name[0] != FILEIO_DIRECTORY_ENTRY_DELETED)&& (entry->attributes != FILEIO_ATTRIBUTE_LONG_NAME))
|
|
{
|
|
break;
|
|
}
|
|
} while ((entry != NULL) && (entry->name[0] != FILEIO_DIRECTORY_ENTRY_EMPTY));
|
|
|
|
if (tempCluster != cluster)
|
|
{
|
|
drive->error = FILEIO_ERROR_DIR_NOT_FOUND;
|
|
return 0;
|
|
}
|
|
|
|
cluster = directory.cluster;
|
|
|
|
checksum = 0;
|
|
source = (uint8_t *)entry->name;
|
|
|
|
for (i = 11; i != 0; i--)
|
|
{
|
|
checksum = ((checksum & 1) ? 0x80 : 0) + (checksum >> 1) + *source++;
|
|
}
|
|
|
|
currentClusterTemp = currentCluster;
|
|
if (FILEIO_LongFileNameCache(&directory, entryOffset - 1, currentCluster, checksum) == FILEIO_LFN_SUCCESS)
|
|
{
|
|
// The long file name is cached
|
|
// Copy it backwards into the buffer
|
|
j = FILEIO_strlen16 (lfnBuffer);
|
|
charCount += j;
|
|
for (j -= 1; j >= 0; j--)
|
|
{
|
|
*(buffer + index++) = lfnBuffer[j];
|
|
if (index == size)
|
|
{
|
|
index = 0;
|
|
bufferOverflow = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// There's no long file name entry associated with this directory
|
|
// Copy and pad the short file name
|
|
|
|
// Ensure that the original entry is still cached
|
|
currentCluster = currentClusterTemp;
|
|
entry = FILEIO_DirectoryEntryCache (&directory, &error, ¤tCluster, ¤tClusterOffset, entryOffset - 1);
|
|
|
|
// We have found our directory in the parent directory. Copy the name into the buffer (backwards)
|
|
// Copy the extension.
|
|
j = 10;
|
|
while (entry->name[j] == 0x20)
|
|
{
|
|
j--;
|
|
}
|
|
if (j >= 8)
|
|
{
|
|
charCount += (j - 8) + 1;
|
|
while (j >= 8)
|
|
{
|
|
*(buffer + index++) = entry->name[j--];
|
|
// This is a circular buffer
|
|
// Any unaccomadatable values will be overwritten
|
|
if (index == size)
|
|
{
|
|
index = 0;
|
|
bufferOverflow = true;
|
|
}
|
|
}
|
|
|
|
charCount++;
|
|
*(buffer + index++) = '.';
|
|
if (index == size)
|
|
{
|
|
index = 0;
|
|
bufferOverflow = true;
|
|
}
|
|
}
|
|
|
|
// Copy the name
|
|
while (entry->name[j] == 0x20)
|
|
{
|
|
j--;
|
|
}
|
|
|
|
charCount += j + 1;
|
|
while (j >= 0)
|
|
{
|
|
*(buffer + index++) = entry->name[j--];
|
|
// This is a circular buffer
|
|
// Any unnecessary values will be overwritten
|
|
if (index == size)
|
|
{
|
|
index = 0;
|
|
bufferOverflow = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
charCount++;
|
|
*(buffer + index++) = FILEIO_CONFIG_DELIMITER;
|
|
|
|
if (index == size)
|
|
{
|
|
index = 0;
|
|
bufferOverflow = true;
|
|
}
|
|
}
|
|
|
|
charCount += 2;
|
|
// We have reached the root. Copy the drive ID into the buffer.
|
|
*(buffer + index++) = ':';
|
|
if (index == size)
|
|
{
|
|
index = 0;
|
|
bufferOverflow = true;
|
|
}
|
|
*(buffer + index++) = drive->driveId;
|
|
if (index == size)
|
|
{
|
|
index = 0;
|
|
bufferOverflow = true;
|
|
}
|
|
|
|
|
|
// Reverse the contents of the buffer.
|
|
// Point the index back at the last char in the string
|
|
if (index == 0)
|
|
{
|
|
index = size;
|
|
}
|
|
else
|
|
{
|
|
index--;
|
|
}
|
|
|
|
i = 0;
|
|
// Swap the chars in the buffer so they are in the right places
|
|
if (bufferOverflow)
|
|
{
|
|
tempIndex = index;
|
|
// Swap the overflowed values in the buffer
|
|
while ((index - i) > 0)
|
|
{
|
|
aChar = *(buffer + i);
|
|
*(buffer + i) = * (buffer + index);
|
|
*(buffer + index) = aChar;
|
|
index--;
|
|
i++;
|
|
}
|
|
|
|
// Point at the non-overflowed values
|
|
i = tempIndex + 1;
|
|
index = bufferEnd - buffer;
|
|
|
|
// Swap the non-overflowed values into the right places
|
|
while ((index - i) > 0)
|
|
{
|
|
aChar = *(buffer + i);
|
|
*(buffer + i) = * (buffer + index);
|
|
*(buffer + index) = aChar;
|
|
index--;
|
|
i++;
|
|
}
|
|
// All the values should be in the right place now
|
|
// Null-terminate the string
|
|
*(bufferEnd) = 0;
|
|
}
|
|
else
|
|
{
|
|
// There was no overflow, just do one set of swaps
|
|
tempIndex = index;
|
|
while ((index - i) > 0)
|
|
{
|
|
aChar = *(buffer + i);
|
|
*(buffer + i) = * (buffer + index);
|
|
*(buffer + index) = aChar;
|
|
index--;
|
|
i++;
|
|
}
|
|
*(buffer + tempIndex + 1) = 0;
|
|
}
|
|
|
|
return charCount;
|
|
}
|
|
#endif
|
|
|
|
#if !defined (FILEIO_CONFIG_SEARCH_DISABLE)
|
|
int FILEIO_Find (const uint16_t * fileName, unsigned int attr, FILEIO_SEARCH_RECORD * record, bool newSearch)
|
|
{
|
|
FILEIO_DIRECTORY directory;
|
|
uint8_t fileNameType;
|
|
FILEIO_ERROR_TYPE error;
|
|
FILEIO_OBJECT file;
|
|
uint16_t * fileWithoutDirectory;
|
|
|
|
if (newSearch)
|
|
{
|
|
fileWithoutDirectory = FILEIO_CacheDirectory (&directory, (uint16_t *)fileName, false);
|
|
|
|
if (fileWithoutDirectory == NULL)
|
|
{
|
|
globalParameters.currentWorkingDirectory.drive->error = FILEIO_ERROR_INVALID_ARGUMENT;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
record->pathOffset = fileWithoutDirectory - fileName;
|
|
record->currentClusterOffset = 0;
|
|
record->currentDirCluster = directory.cluster;
|
|
record->baseDirCluster = directory.cluster;
|
|
record->driveId = directory.drive->driveId;
|
|
record->currentEntryOffset = 0;
|
|
}
|
|
else
|
|
{
|
|
directory.drive = FILEIO_CharToDrive (record->driveId);
|
|
directory.cluster = record->baseDirCluster;
|
|
|
|
#if defined (FILEIO_CONFIG_MULTIPLE_BUFFER_MODE_DISABLE)
|
|
if (FILEIO_GetSingleBuffer (directory.drive) != FILEIO_RESULT_SUCCESS)
|
|
{
|
|
directory.drive->error = FILEIO_ERROR_WRITE;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
#endif
|
|
|
|
fileWithoutDirectory = (uint16_t *)fileName + record->pathOffset;
|
|
}
|
|
|
|
fileNameType = FILEIO_FileNameTypeGet(fileWithoutDirectory, true);
|
|
|
|
if ((fileNameType == FILEIO_NAME_INVALID) || (fileNameType == FILEIO_NAME_DOT))
|
|
{
|
|
directory.drive->error = FILEIO_ERROR_INVALID_FILENAME;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
else if (fileNameType == FILEIO_NAME_SHORT)
|
|
{
|
|
// Short file name
|
|
FILEIO_FormatShortFileName (fileWithoutDirectory, &file);
|
|
// Search in 'directory' for an entry matching filePtr->name, starting at entry 0 in directory->cluster and returning the result in filePtr
|
|
error = FILEIO_FindShortFileName (&directory, &file, (uint8_t *)&file.name, &record->currentDirCluster, &record->currentClusterOffset, record->currentEntryOffset, attr, FILEIO_SEARCH_PARTIAL_STRING_SEARCH | FILEIO_SEARCH_ENTRY_ATTRIBUTES);
|
|
}
|
|
else if (fileNameType == FILEIO_NAME_LONG)
|
|
{
|
|
// Long file name
|
|
// Search in 'directory' for an entry matching fileName, starting at entry 0 in directory->cluster and returning the short file name in filePtr.
|
|
// The long file name will be cached in lfnBuffer
|
|
file.lfnPtr = (uint16_t *)fileWithoutDirectory;
|
|
file.lfnLen = FILEIO_strlen16 ((uint16_t *)fileWithoutDirectory);
|
|
error = FILEIO_FindLongFileName (&directory, &file, &record->currentDirCluster, &record->currentClusterOffset, record->currentEntryOffset, attr, FILEIO_SEARCH_PARTIAL_STRING_SEARCH | FILEIO_SEARCH_ENTRY_ATTRIBUTES);
|
|
}
|
|
|
|
if (error != FILEIO_ERROR_NONE)
|
|
{
|
|
directory.drive->error = error;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
// Copy file data into the result structure
|
|
FILEIO_ShortFileNameConvert ((char *)record->shortFileName, (char *)file.name);
|
|
record->attributes = file.attributes;
|
|
record->fileSize = file.size;
|
|
record->timeStamp.date.value = file.date;
|
|
record->timeStamp.time.value = file.time;
|
|
record->timeStamp.timeMs = file.timeMs;
|
|
|
|
record->currentEntryOffset = file.entry + 1;
|
|
|
|
return FILEIO_RESULT_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
#if !defined (FILEIO_CONFIG_SEARCH_DISABLE)
|
|
int FILEIO_LongFileNameGet (FILEIO_SEARCH_RECORD * record, uint16_t * buffer, uint16_t length)
|
|
{
|
|
uint32_t currentCluster = record->currentDirCluster;
|
|
uint16_t currentClusterOffset = record->currentClusterOffset;
|
|
uint16_t entryOffset = record->currentEntryOffset - 1;
|
|
FILEIO_DIRECTORY_ENTRY * entry;
|
|
FILEIO_ERROR_TYPE error = FILEIO_ERROR_NONE;
|
|
FILEIO_DIRECTORY directory;
|
|
uint8_t checksum = 0;
|
|
uint8_t * source;
|
|
uint16_t i;
|
|
|
|
directory.cluster = record->baseDirCluster;
|
|
directory.drive = FILEIO_CharToDrive(record->driveId);
|
|
|
|
if (directory.drive == NULL)
|
|
{
|
|
globalParameters.currentWorkingDirectory.drive->error = FILEIO_ERROR_INVALID_ARGUMENT;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
entry = FILEIO_DirectoryEntryCache (&directory, &error, ¤tCluster, ¤tClusterOffset, entryOffset);
|
|
if ((entry == NULL) || (error != FILEIO_ERROR_NONE))
|
|
{
|
|
directory.drive->error = error;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
checksum = 0;
|
|
source = (uint8_t *)entry->name;
|
|
|
|
for (i = 11; i != 0; i--)
|
|
{
|
|
checksum = ((checksum & 1) ? 0x80 : 0) + (checksum >> 1) + *source++;
|
|
}
|
|
|
|
if (FILEIO_LongFileNameCache(&directory, entryOffset, currentCluster, checksum) == FILEIO_LFN_SUCCESS)
|
|
{
|
|
i = FILEIO_strlen16 (lfnBuffer);
|
|
if (i < length)
|
|
{
|
|
length = i;
|
|
}
|
|
|
|
memcpy (buffer, lfnBuffer, (length + 1) << 1);
|
|
|
|
return FILEIO_RESULT_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
directory.drive->error = FILEIO_ERROR_NO_LONG_FILE_NAME;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
#if !defined (FILEIO_CONFIG_FORMAT_DISABLE)
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
int FILEIO_CreateMBR (FILEIO_DRIVE_CONFIG * config, void * mediaParameters, uint32_t firstSector, uint32_t sectorCount)
|
|
{
|
|
FILEIO_MASTER_BOOT_RECORD * partition;
|
|
FILEIO_BUFFER_STATUS * bufferStatusPtr;
|
|
uint8_t * dataBuffer;
|
|
uint32_t cylHeadSec = 0x00000000;
|
|
uint32_t tempSector;
|
|
|
|
if ((firstSector == 0) || (sectorCount <= 1))
|
|
return FILEIO_RESULT_FAILURE;
|
|
|
|
if (firstSector > (sectorCount - 1))
|
|
return FILEIO_RESULT_FAILURE;
|
|
|
|
#if defined (FILEIO_CONFIG_MULTIPLE_BUFFER_MODE_DISABLE)
|
|
bufferStatusPtr = &bufferStatus;
|
|
dataBuffer = gDataBuffer;
|
|
|
|
// Use the last drive's buffer for this operation (it's the least likely to be in use)
|
|
if (bufferStatusPtr->driveOwner != NULL)
|
|
{
|
|
if (bufferStatusPtr->flags.dataBufferNeedsWrite)
|
|
{
|
|
if (! (*((const FILEIO_DRIVE_CONFIG *)(bufferStatusPtr->driveOwner))->funcSectorWrite)(mediaParameters, bufferStatusPtr->dataBufferCachedSector, dataBuffer, false))
|
|
{
|
|
return false;
|
|
}
|
|
bufferStatusPtr->flags.dataBufferNeedsWrite = false;
|
|
}
|
|
}
|
|
|
|
bufferStatusPtr->driveOwner = NULL;
|
|
#else
|
|
bufferStatusPtr = &bufferStatus[FILEIO_CONFIG_MAX_DRIVES - 1];
|
|
dataBuffer = gDataBuffer[FILEIO_CONFIG_MAX_DRIVES - 1];
|
|
// Use the last drive's buffer for this operation (it's the least likely to be in use)
|
|
if (!gDriveSlotOpen[FILEIO_CONFIG_MAX_DRIVES - 1])
|
|
{
|
|
if (bufferStatusPtr->flags.dataBufferNeedsWrite)
|
|
{
|
|
if (! (*config->funcSectorWrite)(config->mediaParameters, bufferStatusPtr->dataBufferCachedSector, dataBuffer, false))
|
|
{
|
|
return false;
|
|
}
|
|
bufferStatusPtr->flags.dataBufferNeedsWrite = false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
bufferStatusPtr->dataBufferCachedSector = 0xFFFFFFFF;
|
|
|
|
memset (dataBuffer, 0x00, FILEIO_CONFIG_MEDIA_SECTOR_SIZE);
|
|
|
|
partition = (FILEIO_MASTER_BOOT_RECORD *) dataBuffer;
|
|
|
|
// Set Cylinder-head-sector address of the first sector
|
|
tempSector = firstSector;
|
|
cylHeadSec = (tempSector / (unsigned int)16065 ) << 14;
|
|
tempSector %= 16065;
|
|
cylHeadSec |= (tempSector / 63) << 6;
|
|
tempSector %= 63;
|
|
cylHeadSec |= tempSector + 1;
|
|
*(dataBuffer + 447) = (uint8_t)((cylHeadSec >> 16) & 0xFF);
|
|
*(dataBuffer + 448) = (uint8_t)((cylHeadSec >> 8) & 0xFF);
|
|
*(dataBuffer + 449) = (uint8_t)((cylHeadSec) & 0xFF);
|
|
|
|
// Set the count of sectors
|
|
partition->partition0.sectorCount = sectorCount - firstSector;
|
|
|
|
// Set the partition type
|
|
// We only support creating FAT12 and FAT16 MBRs at this time
|
|
if (partition->partition0.sectorCount < 0x1039)
|
|
{
|
|
// FAT12
|
|
partition->partition0.fileSystemDescriptor = 0x01;
|
|
}
|
|
else if (partition->partition0.sectorCount <= 0x3FFD5F)
|
|
{
|
|
// FAT16
|
|
partition->partition0.fileSystemDescriptor = 0x06;
|
|
}
|
|
else
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
// Set the LBA of the first sector
|
|
partition->partition0.lbaFirstSector = firstSector;
|
|
|
|
// Set the Cylinder-head-sector address of the last sector
|
|
tempSector = firstSector + sectorCount - 1;
|
|
cylHeadSec = (tempSector / (unsigned int)16065 ) << 14;
|
|
tempSector %= 16065;
|
|
cylHeadSec |= (tempSector / 63) << 6;
|
|
tempSector %= 63;
|
|
cylHeadSec |= tempSector + 1;
|
|
*(dataBuffer + 451) = (uint8_t)((cylHeadSec >> 16) & 0xFF);
|
|
*(dataBuffer + 452) = (uint8_t)((cylHeadSec >> 8) & 0xFF);
|
|
*(dataBuffer + 453) = (uint8_t)((cylHeadSec) & 0xFF);
|
|
|
|
// Set the boot descriptor. This will be 0, since we won't
|
|
// be booting anything from our device probably
|
|
partition->partition0.bootDescriptor = 0x00;
|
|
|
|
// Set the signature codes
|
|
partition->signature0 = 0x55;
|
|
partition->signature1 = 0xAA;
|
|
|
|
if (!(*config->funcSectorWrite)(mediaParameters, 0, dataBuffer, true))
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
else
|
|
{
|
|
return FILEIO_RESULT_SUCCESS;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if !defined (FILEIO_CONFIG_FORMAT_DISABLE)
|
|
#if !defined (FILEIO_CONFIG_WRITE_DISABLE)
|
|
int FILEIO_Format (FILEIO_DRIVE_CONFIG * config, void * mediaParameters, FILEIO_FORMAT_MODE mode, uint32_t serialNumber, char * volumeId)
|
|
{
|
|
FILEIO_MASTER_BOOT_RECORD * masterBootRecord;
|
|
uint32_t sectorCount, dataClusters, rootDirSectors;
|
|
FILEIO_BOOT_SECTOR * bootSec;
|
|
FILEIO_DRIVE d;
|
|
FILEIO_DRIVE * disk = &d;
|
|
uint16_t j;
|
|
uint32_t fatSize, test;
|
|
uint32_t index;
|
|
FILEIO_MEDIA_INFORMATION * mediaInfo;
|
|
FILEIO_BUFFER_STATUS * bufferStatusPtr;
|
|
FILEIO_DRIVE * tempDriveOwner;
|
|
|
|
#if defined (FILEIO_CONFIG_MULTIPLE_BUFFER_MODE_DISABLE)
|
|
bufferStatusPtr = &bufferStatus;
|
|
d.dataBuffer = gDataBuffer;
|
|
d.fatBuffer = gFATBuffer;
|
|
|
|
if (bufferStatusPtr->driveOwner != NULL)
|
|
{
|
|
FILEIO_DRIVE_CONFIG * bufferDriveConfig = (FILEIO_DRIVE_CONFIG *)((FILEIO_DRIVE *)bufferStatusPtr->driveOwner)->driveConfig;
|
|
|
|
if (bufferStatusPtr->flags.dataBufferNeedsWrite)
|
|
{
|
|
if (!(*bufferDriveConfig->funcSectorWrite)(mediaParameters, bufferStatusPtr->dataBufferCachedSector, disk->dataBuffer, false))
|
|
{
|
|
return false;
|
|
}
|
|
bufferStatusPtr->flags.dataBufferNeedsWrite = false;
|
|
}
|
|
if (bufferStatusPtr->flags.fatBufferNeedsWrite)
|
|
{
|
|
if (! (*bufferDriveConfig->funcSectorWrite)(mediaParameters, bufferStatusPtr->fatBufferCachedSector, disk->fatBuffer, false))
|
|
{
|
|
return false;
|
|
}
|
|
bufferStatusPtr->flags.fatBufferNeedsWrite = false;
|
|
}
|
|
}
|
|
#else
|
|
bufferStatusPtr = &bufferStatus[FILEIO_CONFIG_MAX_DRIVES - 1];
|
|
d.dataBuffer = gDataBuffer[FILEIO_CONFIG_MAX_DRIVES - 1];
|
|
d.fatBuffer = gFatBuffer[FILEIO_CONFIG_MAX_DRIVES - 1];
|
|
|
|
if (!gDriveSlotOpen[FILEIO_CONFIG_MAX_DRIVES - 1])
|
|
{
|
|
if (bufferStatusPtr->flags.dataBufferNeedsWrite)
|
|
{
|
|
if (!(*config->funcSectorWrite)(mediaParameters, bufferStatusPtr->dataBufferCachedSector, disk->dataBuffer, false))
|
|
{
|
|
return false;
|
|
}
|
|
bufferStatusPtr->flags.dataBufferNeedsWrite = false;
|
|
}
|
|
if (bufferStatusPtr->flags.fatBufferNeedsWrite)
|
|
{
|
|
if (! (*config->funcSectorWrite)(mediaParameters, bufferStatusPtr->fatBufferCachedSector, disk->fatBuffer, false))
|
|
{
|
|
return false;
|
|
}
|
|
bufferStatusPtr->flags.fatBufferNeedsWrite = false;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
bufferStatusPtr->dataBufferCachedSector = 0xFFFFFFFF;
|
|
bufferStatusPtr->fatBufferCachedSector = 0xFFFFFFFF;
|
|
|
|
disk->bufferStatusPtr = bufferStatusPtr;
|
|
disk->driveConfig = config;
|
|
|
|
if(config->funcIOInit != NULL)
|
|
{
|
|
(*config->funcIOInit)(mediaParameters);
|
|
}
|
|
|
|
#if defined (__XC8__)
|
|
// XC8 cannot parse this operation without several intermediate steps
|
|
{
|
|
FILEIO_DRIVER_MediaInitialize funcMediaInit = config->funcMediaInit;
|
|
void * mediaParameters = config->mediaParameters;
|
|
|
|
mediaInfo = (*funcMediaInit)(mediaParameters);
|
|
}
|
|
#else
|
|
mediaInfo = (*config->funcMediaInit)(mediaParameters);
|
|
#endif
|
|
|
|
if (mediaInfo->errorCode != MEDIA_NO_ERROR)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
if ((*config->funcSectorRead)(mediaParameters, 0x00, disk->dataBuffer) == false)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
// Check if the card has no MBR
|
|
bootSec = (FILEIO_BOOT_SECTOR *) disk->dataBuffer;
|
|
if((bootSec->signature0 == FILEIO_FAT_GOOD_SIGN_0) && (bootSec->signature1 == FILEIO_FAT_GOOD_SIGN_1))
|
|
{
|
|
// Technically, the OEM name is not for indication
|
|
// The alternative is to read the CIS from attribute
|
|
// memory. See the PCMCIA metaformat for more details
|
|
#if defined (__XC16__) || defined (__XC32__)
|
|
if ((*(disk->dataBuffer + BSI_FSTYPE ) == 'F') && \
|
|
(*(disk->dataBuffer + BSI_FSTYPE + 1 ) == 'A') && \
|
|
(*(disk->dataBuffer + BSI_FSTYPE + 2 ) == 'T') && \
|
|
(*(disk->dataBuffer + BSI_FSTYPE + 3 ) == '1') && \
|
|
(*(disk->dataBuffer + BSI_BOOTSIG) == 0x29))
|
|
#else
|
|
if ((bootSec->biosParameterBlock.fat16.fileSystemType[0] == 'F') && \
|
|
(bootSec->biosParameterBlock.fat16.fileSystemType[1] == 'A') && \
|
|
(bootSec->biosParameterBlock.fat16.fileSystemType[2] == 'T') && \
|
|
(bootSec->biosParameterBlock.fat16.fileSystemType[3] == '1') && \
|
|
(bootSec->biosParameterBlock.fat16.bootSignature == 0x29))
|
|
#endif
|
|
{
|
|
/* Mark that we do not have a MBR;
|
|
this is not actualy used - is here only to remove a compilation warning */
|
|
masterBootRecord = (FILEIO_MASTER_BOOT_RECORD *) NULL;
|
|
switch (mode)
|
|
{
|
|
case FILEIO_FORMAT_BOOT_SECTOR:
|
|
// not enough info to construct our own boot sector
|
|
return FILEIO_RESULT_FAILURE;
|
|
case FILEIO_FORMAT_ERASE:
|
|
// We have to determine the operating system, and the
|
|
// locations and sizes of the root dir and FAT, and the
|
|
// count of FATs
|
|
disk->firstPartitionSector = 0;
|
|
tempDriveOwner = bufferStatusPtr->driveOwner;
|
|
bufferStatusPtr->driveOwner = disk;
|
|
if (FILEIO_LoadBootSector (disk) != FILEIO_ERROR_NONE)
|
|
{
|
|
bufferStatusPtr->driveOwner = tempDriveOwner;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
bufferStatusPtr->driveOwner = tempDriveOwner;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
masterBootRecord = (FILEIO_MASTER_BOOT_RECORD *) disk->dataBuffer;
|
|
disk->firstPartitionSector = masterBootRecord->partition0.lbaFirstSector;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* If the signature is not correct, this is neither a MBR, nor a VBR */
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
switch (mode)
|
|
{
|
|
// True: Rewrite the whole boot sector
|
|
case FILEIO_FORMAT_BOOT_SECTOR:
|
|
sectorCount = masterBootRecord->partition0.sectorCount;
|
|
|
|
if (sectorCount < 0x1039)
|
|
{
|
|
disk->type = FILEIO_FILE_SYSTEM_TYPE_FAT12;
|
|
// Format to FAT12 only if there are too few sectors to format
|
|
// as FAT16
|
|
masterBootRecord->partition0.fileSystemDescriptor = 0x01;
|
|
if ((*config->funcSectorWrite) (mediaParameters, 0x00, disk->dataBuffer, true) == false)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
if (sectorCount >= 0x1028)
|
|
{
|
|
// More than 0x18 sectors for FATs, 0x20 for root dir,
|
|
// 0x8 reserved, and 0xFED for data
|
|
// So double the number of sectors in a cluster to reduce
|
|
// the number of data clusters used
|
|
disk->sectorsPerCluster = 2;
|
|
}
|
|
else
|
|
{
|
|
// One sector per cluster
|
|
disk->sectorsPerCluster = 1;
|
|
}
|
|
|
|
// Prepare a boot sector
|
|
memset (disk->dataBuffer, 0x00, FILEIO_CONFIG_MEDIA_SECTOR_SIZE);
|
|
|
|
// Last digit of file system name (FAT12 )
|
|
disk->dataBuffer[58] = '2';
|
|
|
|
// Calculate the size of the FAT
|
|
fatSize = (sectorCount - 0x21 + (2*disk->sectorsPerCluster));
|
|
test = (341 * disk->sectorsPerCluster) + 2;
|
|
fatSize = (fatSize + (test-1)) / test;
|
|
|
|
disk->fatCopyCount = 0x02;
|
|
disk->rootDirectoryEntryCount = 0x200;
|
|
|
|
disk->fatSectorCount = fatSize;
|
|
}
|
|
else if (sectorCount <= 0x3FFD5F)
|
|
{
|
|
disk->type = FILEIO_FILE_SYSTEM_TYPE_FAT16;
|
|
// Format to FAT16
|
|
masterBootRecord->partition0.fileSystemDescriptor = 0x06;
|
|
if ((*config->funcSectorWrite)(mediaParameters, 0x00, disk->dataBuffer, true) == false)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
dataClusters = sectorCount - 0x218;
|
|
// Figure out how many sectors per cluster we need
|
|
disk->sectorsPerCluster = 1;
|
|
while (dataClusters > 0xFFED)
|
|
{
|
|
disk->sectorsPerCluster *= 2;
|
|
dataClusters /= 2;
|
|
}
|
|
// This shouldnt happen
|
|
if (disk->sectorsPerCluster > 128)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
// Prepare a boot sector
|
|
memset (disk->dataBuffer, 0x00, FILEIO_CONFIG_MEDIA_SECTOR_SIZE);
|
|
|
|
// Last digit of file system name (FAT16 )
|
|
disk->dataBuffer[58] = '6';
|
|
|
|
// Calculate the size of the FAT
|
|
fatSize = (sectorCount - 0x21 + (2*disk->sectorsPerCluster));
|
|
test = (256 * disk->sectorsPerCluster) + 2;
|
|
fatSize = (fatSize + (test-1)) / test;
|
|
|
|
disk->fatCopyCount = 0x02;
|
|
disk->rootDirectoryEntryCount = 0x200;
|
|
|
|
disk->fatSectorCount = fatSize;
|
|
}
|
|
else
|
|
{
|
|
disk->type = FILEIO_FILE_SYSTEM_TYPE_FAT32;
|
|
// Format to FAT32
|
|
masterBootRecord->partition0.fileSystemDescriptor = 0x0B;
|
|
if ((*config->funcSectorWrite)(mediaParameters, 0x00, disk->dataBuffer, true) == false)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
#ifdef FORMAT_SECTORS_PER_CLUSTER
|
|
disk->sectorsPerCluster = FORMAT_SECTORS_PER_CLUSTER;
|
|
dataClusters = sectorCount / disk->sectorsPerCluster;
|
|
|
|
/* FAT32: 65526 < Number of clusters < 4177918 */
|
|
if ((dataClusters <= 65526) || (dataClusters >= 4177918))
|
|
{
|
|
bufferStatusPtr->driveOwner = tempDriveOwner;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
#else
|
|
/* FAT32: 65526 < Number of clusters < 4177918 */
|
|
dataClusters = sectorCount;
|
|
// Figure out how many sectors per cluster we need
|
|
disk->sectorsPerCluster = 1;
|
|
while (dataClusters > 0x3FBFFE)
|
|
{
|
|
disk->sectorsPerCluster *= 2;
|
|
dataClusters /= 2;
|
|
}
|
|
#endif
|
|
// Check the cluster size: FAT32 supports 512, 1024, 2048, 4096, 8192, 16K, 32K, 64K
|
|
if (disk->sectorsPerCluster > 128)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
// Prepare a boot sector
|
|
memset (disk->dataBuffer, 0x00, FILEIO_CONFIG_MEDIA_SECTOR_SIZE);
|
|
|
|
// Calculate the size of the FAT
|
|
fatSize = (sectorCount - 0x20);
|
|
test = (128 * disk->sectorsPerCluster) + 1;
|
|
fatSize = (fatSize + (test-1)) / test;
|
|
|
|
disk->fatCopyCount = 0x02;
|
|
disk->rootDirectoryEntryCount = 0x200;
|
|
|
|
disk->fatSectorCount = fatSize;
|
|
}
|
|
|
|
// Non-file system specific values
|
|
disk->dataBuffer[0] = 0xEB; //Jump instruction
|
|
disk->dataBuffer[1] = 0x3C;
|
|
disk->dataBuffer[2] = 0x90;
|
|
disk->dataBuffer[3] = 'M'; //OEM Name "MCHP FAT"
|
|
disk->dataBuffer[4] = 'C';
|
|
disk->dataBuffer[5] = 'H';
|
|
disk->dataBuffer[6] = 'P';
|
|
disk->dataBuffer[7] = ' ';
|
|
disk->dataBuffer[8] = 'F';
|
|
disk->dataBuffer[9] = 'A';
|
|
disk->dataBuffer[10] = 'T';
|
|
|
|
disk->dataBuffer[11] = 0x00; //Sector size
|
|
disk->dataBuffer[12] = 0x02;
|
|
|
|
disk->dataBuffer[13] = disk->sectorsPerCluster; //Sectors per cluster
|
|
|
|
if ((disk->type == FILEIO_FILE_SYSTEM_TYPE_FAT12) || (disk->type == FILEIO_FILE_SYSTEM_TYPE_FAT16))
|
|
{
|
|
disk->dataBuffer[14] = 0x08; //Reserved sector count
|
|
disk->dataBuffer[15] = 0x00;
|
|
disk->firstFatSector = 0x08 + disk->firstPartitionSector;
|
|
|
|
disk->dataBuffer[16] = 0x02; //number of FATs
|
|
|
|
disk->dataBuffer[17] = 0x00; //Max number of root directory entries - 512 files allowed
|
|
disk->dataBuffer[18] = 0x02;
|
|
|
|
disk->dataBuffer[19] = 0x00; //total sectors
|
|
disk->dataBuffer[20] = 0x00;
|
|
|
|
disk->dataBuffer[21] = 0xF8; //Media Descriptor
|
|
|
|
disk->dataBuffer[22] = fatSize & 0xFF; //Sectors per FAT
|
|
disk->dataBuffer[23] = (fatSize >> 8) & 0xFF;
|
|
|
|
disk->dataBuffer[24] = 0x3F; //Sectors per track
|
|
disk->dataBuffer[25] = 0x00;
|
|
|
|
disk->dataBuffer[26] = 0xFF; //Number of heads
|
|
disk->dataBuffer[27] = 0x00;
|
|
|
|
// Hidden sectors = sectors between the MBR and the boot sector
|
|
disk->dataBuffer[28] = (uint8_t)(disk->firstPartitionSector & 0xFF);
|
|
disk->dataBuffer[29] = (uint8_t)((disk->firstPartitionSector / 0x100) & 0xFF);
|
|
disk->dataBuffer[30] = (uint8_t)((disk->firstPartitionSector / 0x10000) & 0xFF);
|
|
disk->dataBuffer[31] = (uint8_t)((disk->firstPartitionSector / 0x1000000) & 0xFF);
|
|
|
|
// Total Sectors = same as sectors in the partition from MBR
|
|
disk->dataBuffer[32] = (uint8_t)(sectorCount & 0xFF);
|
|
disk->dataBuffer[33] = (uint8_t)((sectorCount / 0x100) & 0xFF);
|
|
disk->dataBuffer[34] = (uint8_t)((sectorCount / 0x10000) & 0xFF);
|
|
disk->dataBuffer[35] = (uint8_t)((sectorCount / 0x1000000) & 0xFF);
|
|
|
|
disk->dataBuffer[36] = 0x00; // Physical drive number
|
|
|
|
disk->dataBuffer[37] = 0x00; // Reserved (current head)
|
|
|
|
disk->dataBuffer[38] = 0x29; // Signature code
|
|
|
|
disk->dataBuffer[39] = (uint8_t)(serialNumber & 0xFF);
|
|
disk->dataBuffer[40] = (uint8_t)((serialNumber / 0x100) & 0xFF);
|
|
disk->dataBuffer[41] = (uint8_t)((serialNumber / 0x10000) & 0xFF);
|
|
disk->dataBuffer[42] = (uint8_t)((serialNumber / 0x1000000) & 0xFF);
|
|
|
|
// Volume ID
|
|
if (volumeId != NULL)
|
|
{
|
|
for (index = 0; (*(volumeId + index) != 0) && (index < 11); index++)
|
|
{
|
|
disk->dataBuffer[index + 43] = *(volumeId + index);
|
|
}
|
|
while (index < 11)
|
|
{
|
|
disk->dataBuffer[43 + index++] = 0x20;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (index = 0; index < 11; index++)
|
|
{
|
|
disk->dataBuffer[index+43] = 0;
|
|
}
|
|
}
|
|
|
|
disk->dataBuffer[54] = 'F';
|
|
disk->dataBuffer[55] = 'A';
|
|
disk->dataBuffer[56] = 'T';
|
|
disk->dataBuffer[57] = '1';
|
|
disk->dataBuffer[59] = ' ';
|
|
disk->dataBuffer[60] = ' ';
|
|
disk->dataBuffer[61] = ' ';
|
|
|
|
}
|
|
else //FAT32
|
|
{
|
|
disk->dataBuffer[14] = 0x20; //Reserved sector count
|
|
disk->dataBuffer[15] = 0x00;
|
|
disk->firstFatSector = 0x20 + disk->firstPartitionSector;
|
|
|
|
disk->dataBuffer[16] = 0x02; //number of FATs
|
|
|
|
disk->dataBuffer[17] = 0x00; //Max number of root directory entries - 512 files allowed
|
|
disk->dataBuffer[18] = 0x00;
|
|
|
|
disk->dataBuffer[19] = 0x00; //total sectors
|
|
disk->dataBuffer[20] = 0x00;
|
|
|
|
disk->dataBuffer[21] = 0xF8; //Media Descriptor
|
|
|
|
disk->dataBuffer[22] = 0x00; //Sectors per FAT
|
|
disk->dataBuffer[23] = 0x00;
|
|
|
|
disk->dataBuffer[24] = 0x3F; //Sectors per track
|
|
disk->dataBuffer[25] = 0x00;
|
|
|
|
disk->dataBuffer[26] = 0xFF; //Number of heads
|
|
disk->dataBuffer[27] = 0x00;
|
|
|
|
// Hidden sectors = sectors between the MBR and the boot sector
|
|
disk->dataBuffer[28] = (uint8_t)(disk->firstPartitionSector & 0xFF);
|
|
disk->dataBuffer[29] = (uint8_t)((disk->firstPartitionSector / 0x100) & 0xFF);
|
|
disk->dataBuffer[30] = (uint8_t)((disk->firstPartitionSector / 0x10000) & 0xFF);
|
|
disk->dataBuffer[31] = (uint8_t)((disk->firstPartitionSector / 0x1000000) & 0xFF);
|
|
|
|
// Total Sectors = same as sectors in the partition from MBR
|
|
disk->dataBuffer[32] = (uint8_t)(sectorCount & 0xFF);
|
|
disk->dataBuffer[33] = (uint8_t)((sectorCount / 0x100) & 0xFF);
|
|
disk->dataBuffer[34] = (uint8_t)((sectorCount / 0x10000) & 0xFF);
|
|
disk->dataBuffer[35] = (uint8_t)((sectorCount / 0x1000000) & 0xFF);
|
|
|
|
disk->dataBuffer[36] = fatSize & 0xFF; //Sectors per FAT
|
|
disk->dataBuffer[37] = (fatSize >> 8) & 0xFF;
|
|
disk->dataBuffer[38] = (fatSize >> 16) & 0xFF;
|
|
disk->dataBuffer[39] = (fatSize >> 24) & 0xFF;
|
|
|
|
disk->dataBuffer[40] = 0x00; //Active FAT
|
|
disk->dataBuffer[41] = 0x00;
|
|
|
|
disk->dataBuffer[42] = 0x00; //File System version
|
|
disk->dataBuffer[43] = 0x00;
|
|
|
|
disk->dataBuffer[44] = 0x02; //First cluster of the root directory
|
|
disk->dataBuffer[45] = 0x00;
|
|
disk->dataBuffer[46] = 0x00;
|
|
disk->dataBuffer[47] = 0x00;
|
|
|
|
disk->dataBuffer[48] = 0x01; //FSInfo
|
|
disk->dataBuffer[49] = 0x00;
|
|
|
|
disk->dataBuffer[50] = 0x00; //Backup Boot Sector
|
|
disk->dataBuffer[51] = 0x00;
|
|
|
|
disk->dataBuffer[52] = 0x00; //Reserved for future expansion
|
|
disk->dataBuffer[53] = 0x00;
|
|
disk->dataBuffer[54] = 0x00;
|
|
disk->dataBuffer[55] = 0x00;
|
|
disk->dataBuffer[56] = 0x00;
|
|
disk->dataBuffer[57] = 0x00;
|
|
disk->dataBuffer[58] = 0x00;
|
|
disk->dataBuffer[59] = 0x00;
|
|
disk->dataBuffer[60] = 0x00;
|
|
disk->dataBuffer[61] = 0x00;
|
|
disk->dataBuffer[62] = 0x00;
|
|
disk->dataBuffer[63] = 0x00;
|
|
|
|
disk->dataBuffer[64] = 0x00; // Physical drive number
|
|
|
|
disk->dataBuffer[65] = 0x00; // Reserved (current head)
|
|
|
|
disk->dataBuffer[66] = 0x29; // Signature code
|
|
|
|
disk->dataBuffer[67] = (uint8_t)(serialNumber & 0xFF);
|
|
disk->dataBuffer[68] = (uint8_t)((serialNumber / 0x100) & 0xFF);
|
|
disk->dataBuffer[69] = (uint8_t)((serialNumber / 0x10000) & 0xFF);
|
|
disk->dataBuffer[70] = (uint8_t)((serialNumber / 0x1000000) & 0xFF);
|
|
|
|
// Volume ID
|
|
if (volumeId != NULL)
|
|
{
|
|
for (index = 0; (*(volumeId + index) != 0) && (index < 11); index++)
|
|
{
|
|
disk->dataBuffer[index + 71] = *(volumeId + index);
|
|
}
|
|
while (index < 11)
|
|
{
|
|
disk->dataBuffer[71 + index++] = 0x20;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
memset (disk->dataBuffer, 0x00, 11);
|
|
}
|
|
|
|
disk->dataBuffer[82] = 'F';
|
|
disk->dataBuffer[83] = 'A';
|
|
disk->dataBuffer[84] = 'T';
|
|
disk->dataBuffer[85] = '3';
|
|
disk->dataBuffer[86] = '2';
|
|
disk->dataBuffer[87] = ' ';
|
|
disk->dataBuffer[88] = ' ';
|
|
disk->dataBuffer[89] = ' ';
|
|
|
|
|
|
}
|
|
|
|
#ifdef __XC8__
|
|
// C18 can't reference a value greater than 256
|
|
// using an array name pointer
|
|
*(dataBufferPointer + 510) = 0x55;
|
|
*(dataBufferPointer + 511) = 0xAA;
|
|
#else
|
|
disk->dataBuffer[510] = 0x55;
|
|
disk->dataBuffer[511] = 0xAA;
|
|
#endif
|
|
|
|
disk->firstRootSector = disk->firstFatSector + (disk->fatCopyCount * disk->fatSectorCount);
|
|
|
|
if ((*config->funcSectorWrite) (mediaParameters, disk->firstPartitionSector, disk->dataBuffer, false) == false)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
break;
|
|
case FILEIO_FORMAT_ERASE:
|
|
tempDriveOwner = bufferStatusPtr->driveOwner;
|
|
bufferStatusPtr->driveOwner = disk;
|
|
if (FILEIO_LoadBootSector (disk) != FILEIO_ERROR_NONE)
|
|
{
|
|
bufferStatusPtr->driveOwner = tempDriveOwner;
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
bufferStatusPtr->driveOwner = tempDriveOwner;
|
|
break;
|
|
default:
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
|
|
// Erase the FAT
|
|
memset (disk->dataBuffer, 0x00, FILEIO_CONFIG_MEDIA_SECTOR_SIZE);
|
|
|
|
if (disk->type == FILEIO_FILE_SYSTEM_TYPE_FAT32)
|
|
{
|
|
disk->dataBuffer[0] = 0xF8; //BPB_Media byte value in its low 8 bits, and all other bits are set to 1
|
|
disk->dataBuffer[1] = 0xFF;
|
|
disk->dataBuffer[2] = 0xFF;
|
|
disk->dataBuffer[3] = 0xFF;
|
|
|
|
disk->dataBuffer[4] = 0x00; //Disk is clean and no read/write errors were encountered
|
|
disk->dataBuffer[5] = 0x00;
|
|
disk->dataBuffer[6] = 0x00;
|
|
disk->dataBuffer[7] = 0x0C;
|
|
|
|
disk->dataBuffer[8] = 0xFF; //Root Directory EOF
|
|
disk->dataBuffer[9] = 0xFF;
|
|
disk->dataBuffer[10] = 0xFF;
|
|
disk->dataBuffer[11] = 0xFF;
|
|
|
|
for (j = disk->fatCopyCount - 1; j != 0xFFFF; j--)
|
|
{
|
|
if ((*config->funcSectorWrite)(mediaParameters, disk->firstFatSector + (j * disk->fatSectorCount), disk->dataBuffer, false) == false)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
}
|
|
|
|
memset (disk->dataBuffer, 0x00, 12);
|
|
|
|
for (index = disk->firstFatSector + 1; index < (disk->firstFatSector + disk->fatSectorCount); index++)
|
|
{
|
|
for (j = disk->fatCopyCount - 1; j != 0xFFFF; j--)
|
|
{
|
|
if ((*config->funcSectorWrite)(mediaParameters, index + (j * disk->fatSectorCount), disk->dataBuffer, false) == false)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Erase the root directory
|
|
for (index = 1; index < disk->sectorsPerCluster; index++)
|
|
{
|
|
if ((*config->funcSectorWrite)(mediaParameters, disk->firstRootSector + index, disk->dataBuffer, false) == false)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
}
|
|
|
|
if (volumeId != NULL)
|
|
{
|
|
// Create a drive name entry in the root dir
|
|
index = 0;
|
|
while ((*(volumeId + index) != 0) && (index < 11))
|
|
{
|
|
disk->dataBuffer[index] = *(volumeId + index);
|
|
index++;
|
|
}
|
|
while (index < 11)
|
|
{
|
|
disk->dataBuffer[index++] = ' ';
|
|
}
|
|
disk->dataBuffer[11] = 0x08;
|
|
disk->dataBuffer[17] = 0x11;
|
|
disk->dataBuffer[19] = 0x11;
|
|
disk->dataBuffer[23] = 0x11;
|
|
|
|
if ((*config->funcSectorWrite)(mediaParameters, disk->firstRootSector, disk->dataBuffer, false) == false)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((*config->funcSectorWrite)(mediaParameters, disk->firstRootSector, disk->dataBuffer, false) == false)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
}
|
|
|
|
return FILEIO_RESULT_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
disk->dataBuffer[0] = 0xF8;
|
|
disk->dataBuffer[1] = 0xFF;
|
|
disk->dataBuffer[2] = 0xFF;
|
|
if (disk->type == FILEIO_FILE_SYSTEM_TYPE_FAT16)
|
|
{
|
|
disk->dataBuffer[3] = 0xFF;
|
|
}
|
|
|
|
for (j = disk->fatCopyCount - 1; j != 0xFFFF; j--)
|
|
{
|
|
if ((*config->funcSectorWrite)(mediaParameters, disk->firstFatSector + (j * disk->fatSectorCount), disk->dataBuffer, false) == false)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
}
|
|
|
|
memset (disk->dataBuffer, 0x00, 4);
|
|
|
|
for (index = disk->firstFatSector + 1; index < (disk->firstFatSector + disk->fatSectorCount); index++)
|
|
{
|
|
for (j = disk->fatCopyCount - 1; j != 0xFFFF; j--)
|
|
{
|
|
if ((*config->funcSectorWrite)(mediaParameters, index + (j * disk->fatSectorCount), disk->dataBuffer, false) == false)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Initialize the sector size
|
|
disk->sectorSize = FILEIO_CONFIG_MEDIA_SECTOR_SIZE;
|
|
|
|
// Erase the root directory
|
|
rootDirSectors = ((disk->rootDirectoryEntryCount * 32) + (disk->sectorSize - 1)) / disk->sectorSize;
|
|
|
|
for (index = 1; index < rootDirSectors; index++)
|
|
{
|
|
if ((*config->funcSectorWrite)(mediaParameters, disk->firstRootSector + index, disk->dataBuffer, false) == false)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
}
|
|
|
|
if (volumeId != NULL)
|
|
{
|
|
// Create a drive name entry in the root dir
|
|
index = 0;
|
|
while ((*(volumeId + index) != 0) && (index < 11))
|
|
{
|
|
disk->dataBuffer[index] = *(volumeId + index);
|
|
index++;
|
|
}
|
|
while (index < 11)
|
|
{
|
|
disk->dataBuffer[index++] = ' ';
|
|
}
|
|
disk->dataBuffer[11] = 0x08;
|
|
disk->dataBuffer[17] = 0x11;
|
|
disk->dataBuffer[19] = 0x11;
|
|
disk->dataBuffer[23] = 0x11;
|
|
|
|
if ((*config->funcSectorWrite)(mediaParameters, disk->firstRootSector, disk->dataBuffer, false) == false)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((*config->funcSectorWrite)(mediaParameters, disk->firstRootSector, disk->dataBuffer, false) == false)
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
}
|
|
|
|
return FILEIO_RESULT_SUCCESS;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if !defined (FILEIO_CONFIG_DRIVE_PROPERTIES_DISABLE)
|
|
void FILEIO_DrivePropertiesGet(FILEIO_DRIVE_PROPERTIES * properties, uint16_t driveId)
|
|
{
|
|
uint8_t i;
|
|
uint32_t value = 0x0;
|
|
FILEIO_DRIVE * drive;
|
|
|
|
drive = FILEIO_CharToDrive (driveId);
|
|
if (drive == NULL)
|
|
{
|
|
properties->properties_status = FILEIO_GET_PROPERTIES_DRIVE_NOT_MOUNTED;
|
|
return;
|
|
}
|
|
|
|
#if defined (FILEIO_CONFIG_MULTIPLE_BUFFER_MODE_DISABLE)
|
|
if (FILEIO_GetSingleBuffer (drive) != FILEIO_RESULT_SUCCESS)
|
|
{
|
|
properties->properties_status = FILEIO_GET_PROPERTIES_CACHE_ERROR;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if(properties->new_request == true)
|
|
{
|
|
properties->results.free_clusters = 0;
|
|
properties->new_request = false;
|
|
|
|
properties->properties_status = FILEIO_GET_PROPERTIES_STILL_WORKING;
|
|
|
|
properties->results.disk_format = drive->type;
|
|
properties->results.sector_size = drive->sectorSize;
|
|
properties->results.sectors_per_cluster = drive->sectorsPerCluster;
|
|
properties->results.total_clusters = drive->partitionClusterCount;
|
|
|
|
/* Settings based on FAT type */
|
|
switch (drive->type)
|
|
{
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT32:
|
|
properties->private.EndClusterLimit = FILEIO_CLUSTER_VALUE_FAT32_END;
|
|
properties->private.ClusterFailValue = FILEIO_CLUSTER_VALUE_FAT32_FAIL;
|
|
break;
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT16:
|
|
properties->private.EndClusterLimit = FILEIO_CLUSTER_VALUE_FAT16_END;
|
|
properties->private.ClusterFailValue = FILEIO_CLUSTER_VALUE_FAT16_FAIL;
|
|
break;
|
|
case FILEIO_FILE_SYSTEM_TYPE_FAT12:
|
|
properties->private.EndClusterLimit = FILEIO_CLUSTER_VALUE_FAT12_END;
|
|
properties->private.ClusterFailValue = FILEIO_CLUSTER_VALUE_FAT16_FAIL;
|
|
break;
|
|
}
|
|
|
|
properties->private.c = 2;
|
|
|
|
properties->private.curcls = properties->private.c;
|
|
FILEIO_FATRead(drive, properties->private.c);
|
|
}
|
|
|
|
if(properties->properties_status != FILEIO_GET_PROPERTIES_STILL_WORKING)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// sequentially scan through the FAT looking for an empty cluster
|
|
for(i=0;i<255;i++)
|
|
{
|
|
// look at its value
|
|
if ( (value = FILEIO_FATRead(drive, properties->private.c)) == properties->private.ClusterFailValue)
|
|
{
|
|
properties->properties_status = FILEIO_GET_PROPERTIES_CLUSTER_FAILURE;
|
|
return;
|
|
}
|
|
|
|
// check if empty cluster found
|
|
if (value == FILEIO_CLUSTER_VALUE_EMPTY)
|
|
{
|
|
properties->results.free_clusters++;
|
|
}
|
|
|
|
properties->private.c++; // check next cluster in FAT
|
|
// check if reached last cluster in FAT, re-start from top
|
|
if ((value == properties->private.EndClusterLimit) || (properties->private.c >= (properties->results.total_clusters + 2)))
|
|
properties->private.c = 2;
|
|
|
|
// check if full circle done, disk full
|
|
if ( properties->private.c == properties->private.curcls)
|
|
{
|
|
properties->properties_status = FILEIO_GET_PROPERTIES_NO_ERRORS;
|
|
return;
|
|
}
|
|
} // scanning for an empty cluster
|
|
|
|
properties->properties_status = FILEIO_GET_PROPERTIES_STILL_WORKING;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
void FILEIO_ShortFileNameConvert (char * newFileName, char * oldFileName)
|
|
{
|
|
uint8_t j = 0;
|
|
while (j < 8)
|
|
{
|
|
if (*oldFileName == 0x20)
|
|
{
|
|
break;
|
|
}
|
|
*newFileName++ = *oldFileName++;
|
|
j++;
|
|
}
|
|
|
|
while (j < 8)
|
|
{
|
|
oldFileName++;
|
|
j++;
|
|
}
|
|
|
|
if (*oldFileName != 0x20)
|
|
{
|
|
*newFileName++ = '.';
|
|
while (j < 11)
|
|
{
|
|
if (*oldFileName == 0x20)
|
|
{
|
|
break;
|
|
}
|
|
*newFileName++ = *oldFileName++;
|
|
j++;
|
|
}
|
|
}
|
|
*newFileName = 0;
|
|
}
|
|
|
|
#if defined (FILEIO_CONFIG_MULTIPLE_BUFFER_MODE_DISABLE)
|
|
int FILEIO_GetSingleBuffer (FILEIO_DRIVE * drive)
|
|
{
|
|
if (drive->bufferStatusPtr->driveOwner != drive)
|
|
{
|
|
if (drive != NULL)
|
|
{
|
|
if (!FILEIO_FlushBuffer(drive, FILEIO_BUFFER_FAT))
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
if (!FILEIO_FlushBuffer(drive, FILEIO_BUFFER_DATA))
|
|
{
|
|
return FILEIO_RESULT_FAILURE;
|
|
}
|
|
}
|
|
|
|
drive->bufferStatusPtr->driveOwner = drive;
|
|
drive->bufferStatusPtr->dataBufferCachedSector = 0xFFFFFFFF;
|
|
drive->bufferStatusPtr->fatBufferCachedSector = 0xFFFFFFFF;
|
|
}
|
|
|
|
return FILEIO_RESULT_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
FILEIO_ERROR_TYPE FILEIO_FindLongFileName (FILEIO_DIRECTORY * directory, FILEIO_OBJECT * filePtr, uint32_t * currentCluster, uint16_t * currentClusterOffset, uint16_t entryOffset, uint16_t attributes, FILEIO_SEARCH_TYPE mode)
|
|
{
|
|
FILEIO_ERROR_TYPE error = FILEIO_ERROR_NONE;
|
|
FILEIO_DIRECTORY_ENTRY * entry;
|
|
uint8_t checksum, i;
|
|
uint8_t * source;
|
|
uint32_t currentClusterTemp;
|
|
uint16_t currentClusterOffsetTemp;
|
|
|
|
if (*currentCluster == 0)
|
|
{
|
|
*currentCluster = directory->drive->firstRootCluster;
|
|
}
|
|
|
|
while(1)
|
|
{
|
|
do
|
|
{
|
|
entry = FILEIO_DirectoryEntryCache (directory, &error, currentCluster, currentClusterOffset, entryOffset);
|
|
if (error == FILEIO_ERROR_DONE)
|
|
{
|
|
break;
|
|
}
|
|
else if (error == FILEIO_ERROR_BAD_CACHE_READ)
|
|
{
|
|
directory->drive->error = FILEIO_ERROR_BAD_CACHE_READ;
|
|
return error;
|
|
}
|
|
|
|
entryOffset++;
|
|
} while (((entry->attributes == FILEIO_ATTRIBUTE_LONG_NAME) || (entry->attributes == FILEIO_ATTRIBUTE_VOLUME) || (((uint8_t)entry->name[0]) == FILEIO_DIRECTORY_ENTRY_DELETED)) && (entry->name[0] != FILEIO_DIRECTORY_ENTRY_EMPTY));
|
|
|
|
if ((error == FILEIO_ERROR_DONE) || (entry->name[0] == FILEIO_DIRECTORY_ENTRY_EMPTY))
|
|
{
|
|
return FILEIO_ERROR_DONE;
|
|
}
|
|
|
|
currentClusterTemp = *currentCluster;
|
|
currentClusterOffsetTemp = *currentClusterOffset;
|
|
|
|
// Valid file entry was found
|
|
if (((mode & FILEIO_SEARCH_ENTRY_ATTRIBUTES) != FILEIO_SEARCH_ENTRY_ATTRIBUTES) || ((entry->attributes & attributes) == entry->attributes))
|
|
{
|
|
checksum = 0;
|
|
source = (uint8_t *)entry->name;
|
|
|
|
for (i = 11; i != 0; i--)
|
|
{
|
|
checksum = ((checksum & 1) ? 0x80 : 0) + (checksum >> 1) + *source++;
|
|
}
|
|
|
|
if (FILEIO_LongFileNameCache(directory, entryOffset - 1, *currentCluster, checksum) == FILEIO_LFN_SUCCESS)
|
|
{
|
|
// File's attributes are valid or we aren't trying to match attributes
|
|
if (FILEIO_LongFileNameCompare (filePtr->lfnPtr, mode) == true)
|
|
{
|
|
// Recache the short file name entry, just in case the LFN entry spans a cluster boundary
|
|
entry = FILEIO_DirectoryEntryCache (directory, &error, ¤tClusterTemp, ¤tClusterOffsetTemp, entryOffset - 1);
|
|
|
|
// Found a match. Fill the result object with the file data
|
|
memcpy (filePtr->name, entry->name, FILEIO_FILE_NAME_LENGTH_8P3_NO_RADIX);
|
|
filePtr->disk = directory->drive;
|
|
filePtr->firstCluster = FILEIO_FullClusterNumberGet (entry);
|
|
filePtr->currentCluster = filePtr->firstCluster;
|
|
filePtr->currentSector = 0;
|
|
filePtr->currentOffset = 0;
|
|
filePtr->absoluteOffset = 0;
|
|
filePtr->size = entry->fileSize;
|
|
filePtr->attributes = entry->attributes;
|
|
if ((filePtr->attributes & FILEIO_ATTRIBUTE_DIRECTORY) == FILEIO_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
filePtr->timeMs = entry->createTimeMs;
|
|
filePtr->time = entry->createTime;
|
|
filePtr->date = entry->createDate;
|
|
}
|
|
else
|
|
{
|
|
filePtr->time = entry->writeTime;
|
|
filePtr->date = entry->writeDate;
|
|
}
|
|
filePtr->entry = --entryOffset;
|
|
filePtr->baseClusterDir = directory->cluster;
|
|
filePtr->currentClusterDir = directory->cluster;
|
|
|
|
return FILEIO_ERROR_NONE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FILEIO_LFN_ERROR FILEIO_LongFileNameCache (FILEIO_DIRECTORY * directory, uint16_t shortEntryOffset, uint32_t currentCluster, uint8_t checksum)
|
|
{
|
|
uint16_t i = 0;
|
|
FILEIO_DIRECTORY_ENTRY_LFN * lfnEntry;
|
|
uint16_t entryOffset;
|
|
FILEIO_ERROR_TYPE error = FILEIO_ERROR_NONE;
|
|
uint16_t currentClusterOffset = shortEntryOffset / FILEIO_DIRECTORY_ENTRIES_PER_SECTOR;
|
|
currentClusterOffset /= directory->drive->sectorsPerCluster;
|
|
|
|
if (shortEntryOffset == 0)
|
|
{
|
|
return FILEIO_LFN_NONE;
|
|
}
|
|
|
|
entryOffset = shortEntryOffset - 1;
|
|
|
|
lfnEntry = (FILEIO_DIRECTORY_ENTRY_LFN *)FILEIO_DirectoryEntryCache (directory, &error, ¤tCluster, ¤tClusterOffset, entryOffset);
|
|
|
|
if ((lfnEntry->attributes != FILEIO_ATTRIBUTE_LONG_NAME) || (lfnEntry->checksum != checksum))
|
|
{
|
|
return FILEIO_LFN_NONE;
|
|
}
|
|
|
|
while ((error == FILEIO_ERROR_NONE) && (lfnEntry->attributes == FILEIO_ATTRIBUTE_LONG_NAME) && (i < 256) && ((lfnEntry->sequenceNumber & 0x40) != 0x40) && (lfnEntry->checksum == checksum))
|
|
{
|
|
entryOffset--;
|
|
|
|
// Copy file data to lfnData array
|
|
|
|
memcpy (lfnBuffer + i, &lfnEntry->namePart1, 10);
|
|
i+= 5;
|
|
memcpy (lfnBuffer + i, &lfnEntry->namePart2, 12);
|
|
i += 6;
|
|
memcpy (lfnBuffer + i, &lfnEntry->namePart3, 4);
|
|
i += 2;
|
|
|
|
lfnEntry = (FILEIO_DIRECTORY_ENTRY_LFN *)FILEIO_DirectoryEntryCache (directory, &error, ¤tCluster, ¤tClusterOffset, entryOffset);
|
|
}
|
|
|
|
if ((error == FILEIO_ERROR_NONE) && (lfnEntry->attributes == FILEIO_ATTRIBUTE_LONG_NAME) && (i < 256) && (lfnEntry->checksum == checksum))
|
|
{
|
|
memcpy (lfnBuffer + i, &lfnEntry->namePart1, 10);
|
|
i+= 5;
|
|
memcpy (lfnBuffer + i, &lfnEntry->namePart2, 12);
|
|
i += 6;
|
|
memcpy (lfnBuffer + i, &lfnEntry->namePart3, 4);
|
|
i += 2;
|
|
}
|
|
else
|
|
{
|
|
if (error == FILEIO_ERROR_NONE)
|
|
{
|
|
error = FILEIO_ERROR_BAD_CACHE_READ;
|
|
}
|
|
}
|
|
|
|
if (error == FILEIO_ERROR_NONE)
|
|
{
|
|
*(lfnBuffer + i) = 0x0000;
|
|
return FILEIO_LFN_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
return FILEIO_LFN_FAILURE;
|
|
}
|
|
}
|
|
|
|
bool FILEIO_LongFileNameCompare (uint16_t * fileName, FILEIO_SEARCH_TYPE mode)
|
|
{
|
|
uint16_t nameLen;
|
|
|
|
nameLen = FILEIO_lfnlen (fileName);
|
|
|
|
if (nameLen != FILEIO_strlen16 (lfnBuffer))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ((mode & FILEIO_SEARCH_PARTIAL_STRING_SEARCH) == FILEIO_SEARCH_PARTIAL_STRING_SEARCH)
|
|
{
|
|
uint16_t character;
|
|
uint16_t i = 0;
|
|
|
|
while(nameLen-- != 0)
|
|
{
|
|
character = fileName[i];
|
|
if (character == '*')
|
|
break;
|
|
if (character != '?')
|
|
{
|
|
if (character != lfnBuffer[i])
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (FILEIO_memcmp16(fileName, lfnBuffer, nameLen) == 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
uint16_t FILEIO_strlen16 (uint16_t * name)
|
|
{
|
|
uint16_t i = 0;
|
|
|
|
while (*name++ != 0)
|
|
{
|
|
i++;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
uint16_t FILEIO_lfnlen (uint16_t * name)
|
|
{
|
|
uint16_t i = 0;
|
|
uint16_t c;
|
|
|
|
while (((c = *name++) != 0) && (c != FILEIO_CONFIG_DELIMITER))
|
|
{
|
|
i++;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
int FILEIO_memcmp16 (uint16_t * name1, uint16_t * name2, uint16_t len)
|
|
{
|
|
while (len--)
|
|
{
|
|
if (*name1 != *name2)
|
|
{
|
|
return (*name1 - *name2);
|
|
}
|
|
name1++;
|
|
name2++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint16_t ReadRam16bit (uint8_t * pBuffer, uint16_t index)
|
|
{
|
|
uint16_t res;
|
|
|
|
pBuffer += index + 1;
|
|
res = *pBuffer--;
|
|
res <<= 8;
|
|
res |= *pBuffer;
|
|
return res;
|
|
}
|
|
|
|
uint32_t ReadRam32bit(uint8_t * pBuffer, uint16_t index)
|
|
{
|
|
uint32_t res;
|
|
|
|
pBuffer += index + 3;
|
|
res = *pBuffer--;
|
|
res <<= 8;
|
|
res |= *pBuffer--;
|
|
res <<= 8;
|
|
res |= *pBuffer--;
|
|
res <<= 8;
|
|
res |= *pBuffer;
|
|
|
|
return res;
|
|
}
|