mirror of
https://github.com/arduino/Arduino.git
synced 2024-11-30 11:24:12 +01:00
365 lines
10 KiB
C
Executable File
365 lines
10 KiB
C
Executable File
/*! \file fat.c \brief FAT16/32 file system driver. */
|
|
//*****************************************************************************
|
|
//
|
|
// File Name : 'fat.c'
|
|
// Title : FAT16/32 file system driver
|
|
// Author : Pascal Stang
|
|
// Date : 11/07/2000
|
|
// Revised : 12/12/2000
|
|
// Version : 0.3
|
|
// Target MCU : ATmega103 (should work for Atmel AVR Series)
|
|
// Editor Tabs : 4
|
|
//
|
|
// This code is based in part on work done by Jesper Hansen for his
|
|
// YAMPP MP3 player project.
|
|
//
|
|
// NOTE: This code is currently below version 1.0, and therefore is considered
|
|
// to be lacking in some functionality or documentation, or may not be fully
|
|
// tested. Nonetheless, you can expect most functions to work.
|
|
//
|
|
// This code is distributed under the GNU Public License
|
|
// which can be found at http://www.gnu.org/licenses/gpl.txt
|
|
//
|
|
//*****************************************************************************
|
|
|
|
|
|
#include <avr/io.h>
|
|
#include <avr/pgmspace.h>
|
|
#include <string.h>
|
|
|
|
#include "ata.h"
|
|
#include "rprintf.h"
|
|
|
|
#include "fat.h"
|
|
#include "fatconf.h"
|
|
|
|
// globals
|
|
unsigned char *SectorBuffer = (unsigned char *) SECTOR_BUFFER1_ADDR;
|
|
unsigned char *LongNameBuffer = (unsigned char *) LONGNAME_BUFFER_ADDR;
|
|
unsigned char *DirNameBuffer = (unsigned char *) DIRNAME_BUFFER_ADDR;
|
|
|
|
struct partrecord PartInfo;
|
|
unsigned char Fat32Enabled;
|
|
unsigned long FirstDataSector;
|
|
unsigned int BytesPerSector;
|
|
unsigned int SectorsPerCluster;
|
|
unsigned long FirstFATSector;
|
|
unsigned long FirstDirSector;
|
|
unsigned long FileSize;
|
|
unsigned long FatInCache = 0;
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
|
|
unsigned long fatClustToSect(unsigned long clust)
|
|
{
|
|
return ((clust-2) * SectorsPerCluster) + FirstDataSector;
|
|
}
|
|
|
|
unsigned int fatClusterSize(void)
|
|
{
|
|
// return the number of sectors in a disk cluster
|
|
return SectorsPerCluster;
|
|
}
|
|
|
|
unsigned char fatInit( unsigned char device)
|
|
{
|
|
//struct partrecord *pr;
|
|
struct bpb710 *bpb;
|
|
|
|
// read partition table
|
|
// TODO.... error checking
|
|
ataReadSectors(DRIVE0, 0, 1, SectorBuffer);
|
|
// map first partition record
|
|
// save partition information to global PartInfo
|
|
PartInfo = *((struct partrecord *) ((struct partsector *) SectorBuffer)->psPart);
|
|
// PartInfo = *pr;
|
|
|
|
// Read the Partition BootSector
|
|
// **first sector of partition in PartInfo.prStartLBA
|
|
ataReadSectors( DRIVE0, PartInfo.prStartLBA, 1, SectorBuffer );
|
|
bpb = (struct bpb710 *) ((struct bootsector710 *) SectorBuffer)->bsBPB;
|
|
|
|
// setup global disk constants
|
|
FirstDataSector = PartInfo.prStartLBA;
|
|
if(bpb->bpbFATsecs)
|
|
{
|
|
// bpbFATsecs is non-zero and is therefore valid
|
|
FirstDataSector += bpb->bpbResSectors + bpb->bpbFATs * bpb->bpbFATsecs;
|
|
}
|
|
else
|
|
{
|
|
// bpbFATsecs is zero, real value is in bpbBigFATsecs
|
|
FirstDataSector += bpb->bpbResSectors + bpb->bpbFATs * bpb->bpbBigFATsecs;
|
|
}
|
|
SectorsPerCluster = bpb->bpbSecPerClust;
|
|
BytesPerSector = bpb->bpbBytesPerSec;
|
|
FirstFATSector = bpb->bpbResSectors + PartInfo.prStartLBA;
|
|
|
|
switch (PartInfo.prPartType)
|
|
{
|
|
case PART_TYPE_DOSFAT16:
|
|
case PART_TYPE_FAT16:
|
|
case PART_TYPE_FAT16LBA:
|
|
// first directory cluster is 2 by default (clusters range 2->big)
|
|
FirstDirSector = CLUST_FIRST;
|
|
// push data sector pointer to end of root directory area
|
|
//FirstDataSector += (bpb->bpbRootDirEnts)/DIRENTRIES_PER_SECTOR;
|
|
Fat32Enabled = FALSE;
|
|
break;
|
|
case PART_TYPE_FAT32LBA:
|
|
case PART_TYPE_FAT32:
|
|
// bpbRootClust field exists in FAT32 bpb710, but not in lesser bpb's
|
|
FirstDirSector = bpb->bpbRootClust;
|
|
// push data sector pointer to end of root directory area
|
|
// need this? FirstDataSector += (bpb->bpbRootDirEnts)/DIRENTRIES_PER_SECTOR;
|
|
Fat32Enabled = TRUE;
|
|
break;
|
|
default:
|
|
rprintfProgStrM("Found: No Partition!\r\n");
|
|
//return 1;
|
|
break;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG_FAT
|
|
switch (PartInfo.prPartType)
|
|
{
|
|
case PART_TYPE_DOSFAT16:
|
|
rprintfProgStrM("Found: DOSFAT 16\r\n");
|
|
break;
|
|
case PART_TYPE_FAT16:
|
|
rprintfProgStrM("Found: FAT16\r\n");
|
|
break;
|
|
case PART_TYPE_FAT16LBA:
|
|
rprintfProgStrM("Found: FAT16 LBA\r\n");
|
|
break;
|
|
case PART_TYPE_FAT32LBA:
|
|
rprintfProgStrM("Found: FAT32 LBA\r\n");
|
|
break;
|
|
case PART_TYPE_FAT32:
|
|
rprintfProgStrM("Found: FAT32\r\n");
|
|
//return 1;
|
|
break;
|
|
default:
|
|
rprintfProgStrM("Found: No Partition!\r\n");
|
|
//return 1;
|
|
break;
|
|
}
|
|
|
|
rprintfProgStrM("First sector : "); rprintfu32(PartInfo.prStartLBA); rprintfCRLF();
|
|
rprintfProgStrM("Size : "); rprintfu32(PartInfo.prSize); rprintfCRLF();
|
|
rprintfProgStrM("bytes/sector : "); rprintfu16(bpb->bpbBytesPerSec); rprintfCRLF();
|
|
rprintfProgStrM("sectors/cluster : "); rprintfu08(bpb->bpbSecPerClust); rprintfCRLF();
|
|
rprintfProgStrM("reserved sectors: "); rprintfu16(bpb->bpbResSectors); rprintfCRLF();
|
|
rprintfProgStrM("FatSectors : "); rprintfu16(bpb->bpbFATsecs); rprintfCRLF();
|
|
rprintfProgStrM("BigFatSectors : "); rprintfu32(bpb->bpbBigFATsecs); rprintfCRLF();
|
|
rprintfProgStrM("Number of Fats : "); rprintfu08(bpb->bpbFATs); rprintfCRLF();
|
|
rprintfProgStrM("First Fat Sector: "); rprintfu32(FirstFATSector); rprintfCRLF();
|
|
rprintfProgStrM("First Data Sect : "); rprintfu32(FirstDataSector); rprintfCRLF();
|
|
rprintfProgStrM("First Dir Clust : "); rprintfu32(FirstDirSector); rprintfCRLF();
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
unsigned int baseentry = 0;
|
|
unsigned int entrycount = 0;
|
|
|
|
|
|
unsigned long fatGetDirEntry(unsigned int entry, unsigned int count)
|
|
{
|
|
unsigned long sector;
|
|
struct direntry *de = 0; // avoid compiler warning by initializing
|
|
struct winentry *we;
|
|
unsigned int hasBuffer;
|
|
unsigned int b;
|
|
int i,index;
|
|
char *p;
|
|
|
|
if(count == 0)
|
|
{
|
|
entrycount = 0;
|
|
DirNameBuffer = 0;
|
|
}
|
|
|
|
// read dir data
|
|
sector = fatClustToSect(FirstDirSector);
|
|
|
|
hasBuffer = 0;
|
|
|
|
index = 16; // crank it up
|
|
do
|
|
{
|
|
if(index == 16) // time for next sector ?
|
|
{
|
|
ataReadSectors( DRIVE0, sector++, 1, SectorBuffer);
|
|
de = (struct direntry *) SectorBuffer;
|
|
index = 0;
|
|
}
|
|
|
|
if(*de->deName != 0xE5)
|
|
{
|
|
// if not a deleted entry
|
|
if(de->deAttributes == ATTR_LONG_FILENAME)
|
|
{
|
|
// we have a long name entry
|
|
we = (struct winentry *) de;
|
|
b = 13 *( (we->weCnt-1) & 0x0f); // index into string
|
|
p = &LongNameBuffer[b];
|
|
for (i=0;i<5;i++) *p++ = we->wePart1[i*2]; // copy first part
|
|
for (i=0;i<6;i++) *p++ = we->wePart2[i*2]; // second part
|
|
for (i=0;i<2;i++) *p++ = we->wePart3[i*2]; // and third part
|
|
if (we->weCnt & 0x40) *p = 0; // in case dirnamelength is multiple of 13
|
|
if ((we->weCnt & 0x0f) == 1) hasBuffer = 1; // mark that we have a long entry
|
|
}
|
|
else
|
|
{
|
|
// we have a short name entry
|
|
// check if this is the end of a multi-part long name entry
|
|
if(hasBuffer)
|
|
{
|
|
// a long entry name has been collected
|
|
// is it a directory ?
|
|
if(de->deAttributes == ATTR_DIRECTORY)
|
|
{
|
|
unsigned long save = FirstDirSector;
|
|
unsigned int save2 = baseentry;
|
|
unsigned long rval;
|
|
|
|
strcpy(DirNameBuffer,LongNameBuffer);
|
|
strcat(DirNameBuffer,"/");
|
|
|
|
// rprintfStr(LongNameBuffer); rprintfProgStrM("/"); //EOL();
|
|
|
|
// call recursively
|
|
FirstDirSector = ((unsigned long)de->deHighClust << 16) + de->deStartCluster;
|
|
rval = fatGetDirEntry(entry,1);
|
|
FirstDirSector = save;
|
|
baseentry = save2;
|
|
if (rval)
|
|
return rval;
|
|
else
|
|
{
|
|
// reload original sector
|
|
ataReadSectors( DRIVE0, sector-1, 1, SectorBuffer);
|
|
entrycount--; // decrement entry counter
|
|
*DirNameBuffer = 0;
|
|
}
|
|
}
|
|
else // normal file entry
|
|
if(entrycount == entry)
|
|
break;
|
|
hasBuffer = 0; // clear buffer
|
|
entrycount++; // increment entry counter
|
|
}
|
|
// else ignore short_name_only entries
|
|
}
|
|
}
|
|
de++;
|
|
index++;
|
|
} while (*de->deName || index == 16); // 0 in de->deName[0] if no more entries
|
|
|
|
if (hasBuffer == 0) // end of entries
|
|
return 0;
|
|
|
|
FileSize = de->deFileSize;
|
|
return (unsigned long) ((unsigned long)de->deHighClust << 16) + de->deStartCluster;
|
|
}
|
|
|
|
|
|
// return the size of the last directory entry
|
|
unsigned long fatGetFilesize(void)
|
|
{
|
|
return FileSize;
|
|
}
|
|
|
|
|
|
// return the long name of the last directory entry
|
|
char* fatGetFilename(void)
|
|
{
|
|
return LongNameBuffer;
|
|
}
|
|
|
|
|
|
// return the directory of the last directory entry
|
|
char* fatGetDirname(void)
|
|
{
|
|
return DirNameBuffer;
|
|
}
|
|
|
|
|
|
// load a clusterfull of data
|
|
void fatLoadCluster(unsigned long cluster, unsigned char *buffer)
|
|
{
|
|
register unsigned char i;
|
|
// read cluster
|
|
//while ( ataReadSectors( DRIVE0, clust2sect(cluster), SectorsPerCluster, buffer) != 0);
|
|
for(i=0; i<SectorsPerCluster; i++)
|
|
{
|
|
// ataReadSectors( DRIVE0, clust2sect(cluster)+i, 1, buffer+(i<<9) );
|
|
// temporary fix for wierd misaligned cluster problem
|
|
// (only when using FAT16?)
|
|
ataReadSectors( DRIVE0, fatClustToSect(cluster+8)+i, 1, buffer+(i<<9) );
|
|
}
|
|
}
|
|
|
|
|
|
// find next cluster in the FAT chain
|
|
unsigned long fatNextCluster(unsigned long cluster)
|
|
{
|
|
unsigned long nextCluster;
|
|
unsigned long fatMask;
|
|
unsigned long fatOffset;
|
|
unsigned long sector;
|
|
unsigned int offset;
|
|
|
|
// get fat offset in bytes
|
|
if(Fat32Enabled)
|
|
{
|
|
// four FAT bytes (32 bits) for every cluster
|
|
fatOffset = cluster << 2;
|
|
// set the FAT bit mask
|
|
fatMask = FAT32_MASK;
|
|
}
|
|
else
|
|
{
|
|
// two FAT bytes (16 bits) for every cluster
|
|
fatOffset = cluster << 1;
|
|
// set the FAT bit mask
|
|
fatMask = FAT16_MASK;
|
|
}
|
|
|
|
// calculate the FAT sector that we're interested in
|
|
sector = FirstFATSector + (fatOffset / BytesPerSector);
|
|
// calculate offset of the our entry within that FAT sector
|
|
offset = fatOffset % BytesPerSector;
|
|
|
|
// if we don't already have this FAT chunk loaded, go get it
|
|
if (sector != FatInCache)
|
|
{
|
|
// read sector of FAT table
|
|
while (ataReadSectors( DRIVE0, sector, 1, (unsigned char*)FAT_CACHE_ADDR) != 0);
|
|
FatInCache = sector;
|
|
}
|
|
|
|
// read the nextCluster value
|
|
nextCluster = (*((unsigned long*) &((char*)FAT_CACHE_ADDR)[offset])) & fatMask;
|
|
|
|
// check to see if we're at the end of the chain
|
|
if (nextCluster == (CLUST_EOFE & fatMask))
|
|
nextCluster = 0;
|
|
|
|
#ifdef DEBUG_FAT
|
|
rprintfProgStrM(">");
|
|
rprintfu32(nextCluster);
|
|
rprintfCRLF();
|
|
#endif
|
|
|
|
return nextCluster;
|
|
}
|