Project

General

Profile

/*! \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.
//
// ----------------------------------------------------------------------------
// 17.8.2008
// Bob!k & Raster, C.P.U.
// Original code was modified especially for the SDrive device.
// Some parts of code have been added, removed, rewrited or optimized due to
// lack of MCU AVR Atmega8 memory.
// ----------------------------------------------------------------------------
//
// This code is distributed under the GNU Public License
// which can be found at http://www.gnu.org/licenses/gpl.txt
//
//*****************************************************************************

#include <stdio.h>
#include <string.h>

#include "fat.h"
#include "mmc.h"
#include "spi.h"

void USART_SendString(char *buff);
void mmcReadCached(u32 sector);


#define FileNameBuffer atari_sector_buffer
extern unsigned char atari_sector_buffer[256];
extern unsigned char mmc_sector_buffer[512]; // one sector
extern struct GlobalSystemValues GS;
extern struct FileInfoStruct FileInfo; //< file information for last file accessed


unsigned long fatClustToSect(unsigned short clust)
{
u32 ret;
if(clust==MSDOSFSROOT)
{
debug("fatClustToSect:");
u32 res = (u32)((u32)(FirstDataSector) - (u32)(RootDirSectors));
plotnextnumber(res);
debug(" ");
plotnextnumber(FirstDataSector);
debug(" ");
plotnextnumber(RootDirSectors);
debug("\n");
return res;
}
ret = (u32)(clust-2);
ret *= (u32)SectorsPerCluster;
ret += (u32)FirstDataSector;
return ((unsigned long)ret);
}


//POZOR - DEBUG !!!
//prvne definovana promenna
u32 debug_endofvariables; //promenna co je v pameti jako posledni (za ni je uz jen stack)
//POZOR - DEBUG !!!

unsigned short last_dir_start_cluster;
unsigned char last_dir_valid;
unsigned short last_dir_entry;
unsigned long last_dir_sector;
unsigned char last_dir_sector_count;
unsigned short last_dir_cluster;
unsigned char last_dir_index;


unsigned char fatInit()
{
struct partrecord PartInfo;
struct bpb710 *bpb;

// Moved here since init vars are in ROM!
last_dir_entry=0x0;
last_dir_sector=0;
last_dir_sector_count=0;
last_dir_cluster=0;
last_dir_index=0;

// read partition table
// TODO.... error checking
debug("FAT INIT:");
mmcReadCached(0);
// map first partition record
// save partition information to global PartInfo
PartInfo = *((struct partrecord *) ((struct partsector *) (char*)mmc_sector_buffer)->psPart);

if(mmc_sector_buffer[0x36]=='F' && mmc_sector_buffer[0x37]=='A' && mmc_sector_buffer[0x38]=='T' && mmc_sector_buffer[0x39]=='1' && mmc_sector_buffer[0x3a]=='6')
{
PartInfo.prPartType = PART_TYPE_FAT16LBA; //0x04
PartInfo.prStartLBA = 0x00;
}
// Read the Partition BootSector
// **first sector of partition in PartInfo.prStartLBA
mmcReadCached(PartInfo.prStartLBA);
bpb = (struct bpb710 *) ((struct bootsector710 *) (char*)mmc_sector_buffer)->bsBPB;

// setup global disk constants
PartInfo.prStartLBA = 0x1e8; // FIXME
FirstDataSector = PartInfo.prStartLBA;

RootDirSectors = bpb->bpbRootDirEnts>>4; // /16 ; 512/16 = 32 (sector size / max entries in one sector = number of sectors)

debug("PartInto ");
plotnextnumber(PartInfo.prStartLBA);
debug(" ");
plotnextnumber(bpb->bpbRootDirEnts>>4);
debug("\n");

// bpbFATsecs is non-zero and is therefore valid
FirstDataSector += bpb->bpbResSectors + bpb->bpbFATs * bpb->bpbFATsecs + RootDirSectors;

SectorsPerCluster = bpb->bpbSecPerClust;
BytesPerSector = bpb->bpbBytesPerSec;
FirstFATSector = bpb->bpbResSectors + PartInfo.prStartLBA;

// set current directory to root (\)
FileInfo.vDisk.dir_cluster = MSDOSFSROOT; //RootDirStartCluster;

last_dir_start_cluster=0xffff;
//last_dir_valid=0; //<-- neni potreba, protoze na zacatku fatGetDirEntry se pri zjisteni
//ze je (FileInfo.vDisk.dir_cluster!=last_dir_start_cluster)
//vynuluje last_dir_valid=0;

if ( PartInfo.prPartType==PART_TYPE_DOSFAT16
|| PartInfo.prPartType==PART_TYPE_FAT16
|| PartInfo.prPartType==PART_TYPE_FAT16LBA
)
{
return 1; //je to ok
debug("OK\n");
}
else
{
return 0; //neni to zadny filesystem co umime
debug("FAIL\n");
}
}

//////////////////////////////////////////////////////////////

unsigned char fatGetDirEntry(unsigned short entry, unsigned char use_long_names)
{
unsigned long sector;
struct direntry *de = 0; // avoid compiler warning by initializing
struct winentry *we;
unsigned char haveLongNameEntry;
unsigned char gotEntry;
unsigned short b;
u08 index;
unsigned short entrycount = 0;
unsigned short actual_cluster = FileInfo.vDisk.dir_cluster;
unsigned char seccount=0;

haveLongNameEntry = 0;
gotEntry = 0;

if (FileInfo.vDisk.dir_cluster!=last_dir_start_cluster)
{
//zmenil se adresar, takze musi pracovat s nim
//a zneplatnit last_dir polozky
last_dir_start_cluster=FileInfo.vDisk.dir_cluster;
last_dir_valid=0;

debug("LastDirStartCluster:");
plotnextnumber(last_dir_start_cluster);
plotnextnumber(last_dir_valid);
debug("\n");
}

if ( !last_dir_valid
|| (entry<=last_dir_entry && (entry!=last_dir_entry || last_dir_valid!=1 || use_long_names!=0))
)
{
//musi zacit od zacatku
sector = fatClustToSect(FileInfo.vDisk.dir_cluster);
//index = 0;
debug("ReadFromBegin:");
plotnextnumber(sector);
debug("\n");
goto fat_read_from_begin;
}
else
{
//muze zacit od posledne pouzite
entrycount=last_dir_entry;
index = last_dir_index;
sector=last_dir_sector;
actual_cluster=last_dir_cluster;
seccount = last_dir_sector_count;
debug("ReadFromLastEntry:");
plotnextnumber(sector);
debug("\n");
goto fat_read_from_last_entry;
}

while(1)
{
debug("Index:");
plotnextnumber(index);
debug("\n");
if(index==16) // time for next sector ?
{
if ( actual_cluster==MSDOSFSROOT )
{
//v MSDOSFSROOT se nerozlisuji clustery
//protoze MSDOSFSROOT ma sektory stale dal za sebou bez clusterovani
if (seccount>=RootDirSectors) return 0; //prekrocil maximalni pocet polozek v MSDOSFSROOT
}
else //MUSI BYT!!! (aby neporovnaval seccount je-li actual_cluster==MSDOSFSROOT)
if( seccount>=SectorsPerCluster )
{
//next cluster
//pri prechodu pres pocet sektoru v clusteru
actual_cluster = nextCluster(actual_cluster);
sector=fatClustToSect(actual_cluster);
seccount=0;
}
fat_read_from_begin:
index = 0;
seccount++; //ted bude nacitat dalsi sektor (musi ho zapocitat)
fat_read_from_last_entry:
mmcReadCached( sector++ ); //prave ho nacetl
de = (struct direntry *) (char*)mmc_sector_buffer;
de+=index;
}
// check the status of this directory entry slot
if(de->deName[0] == 0x00) //SLOT_EMPTY
{
// slot is empty and this is the end of directory
gotEntry = 0;
break;
}
else if(de->deName[0] == 0xE5) //SLOT_DELETED
{
// this is an empty slot
// do nothing and move to the next one
}
else
{
// this is a valid and occupied entry
// is it a part of a long file/dir name?
if( de->deAttributes==ATTR_LONG_FILENAME )
{
// we have a long name entry
// cast this directory entry as a "windows" (LFN: LongFileName) entry
u08 i;
unsigned char *fnbPtr;

//pokud nechceme longname, NESMI je vubec kompletovat
//a preskoci na dalsi polozku
//( takze ani haveLongNameEntry nikdy nebude nastaveno na 1)
//Jinak by totiz preplacaval dalsi kusy atari_sector_bufferu 12-255 ! BACHA!!!
if (!use_long_names) goto fat_next_dir_entry;

we = (struct winentry *) de;
b = WIN_ENTRY_CHARS*( (unsigned short)((we->weCnt-1) & 0x0f)); // index into string
fnbPtr = &FileNameBuffer[b];

for (i=0;i<5;i++) *fnbPtr++ = we->wePart1[i*2]; // copy first part
for (i=0;i<6;i++) *fnbPtr++ = we->wePart2[i*2]; // second part
for (i=0;i<2;i++) *fnbPtr++ = we->wePart3[i*2]; // and third part

/*
{
//unsigned char * sptr;
//sptr=we->wePart1;
//do { *fnbPtr++ = *sptr++; sptr++; } while (sptr<(we->wePart1+10)); // copy first part
//^-- pouziti jen tohoto prvniho a druhe dva pres normalni for(..) uspori 10 bajtu,
//pri pouziti i dalsich dvou timto stylem uz se to ale zase prodlouzi - nechaaaaaapu!
//sptr=we->wePart2;
//do { *fnbPtr++ = *sptr++; sptr++; } while (sptr<(we->wePart2+12)); // second part
//sptr=we->wePart3;
//do { *fnbPtr++ = *sptr++; sptr++; } while (sptr<(we->wePart3+4)); // and third part
}
*/

if (we->weCnt & WIN_LAST) *fnbPtr = 0; // in case dirnamelength is multiple of 13, add termination
if ((we->weCnt & 0x0f) == 1) haveLongNameEntry = 1; // flag that we have a complete long name entry set
}
else
{
// we have a short name entry
// check if this is the short name entry corresponding
// to the end of a multi-part long name entry
if(haveLongNameEntry)
{
// a long entry name has been collected
if(entrycount == entry)
{
// desired entry has been found, break out
gotEntry = 2;
break;
}
// otherwise
haveLongNameEntry = 0; // clear long name flag
entrycount++; // increment entry counter
}
else
{
// entry is a short name (8.3 format) without a
// corresponding multi-part long name entry

//Zcela vynecha adresar "." a disk label
if( (de->deName[0]=='.' && de->deName[1]==' ' && (de->deAttributes & ATTR_DIRECTORY) ) //je to "." adresar
|| (de->deAttributes & ATTR_VOLUME) ) goto fat_next_dir_entry;

if(entrycount == entry)
{
/*
fnbPtr = &FileNameBuffer[string_offset];
for (i=0;i<8;i++) *fnbPtr++ = de->deName[i]; // copy name
for (i=0;i<3;i++) *fnbPtr++ = de->deExtension[i]; // copy extension
*fnbPtr = 0; // null-terminate
*/
u08 i;
unsigned char *dptr;

dptr = &FileNameBuffer;
for (i=0;i<11;i++) *dptr++ = de->deName[i]; // copy name+ext
*dptr=0; //ukonceni za nazvem

// desired entry has been found, break out
//pokud chtel longname, tak to neni a proto upravi 8+3 jmeno na nazev.ext
//aby to melo formatovani jako longname
if (use_long_names)
{
//upravi 'NAME EXT' na 'NAME.EXT'
//(vyhazi mezery a prida tecku)
//krome nazvu '. ', a '.. ', tam jen vyhaze mezery
unsigned char *sptr;
//EXT => .EXT (posune vcetne 0x00 za koncem extenderu)
dptr=(FileNameBuffer+12);
i=4; do { sptr=dptr-1; *dptr=*sptr; dptr=sptr; i--; } while(i>0);
if (FileNameBuffer[0]!='.') *dptr='.'; //jen pro jine nez '.' a '..'
//NAME .EXT => NAME.EXT
sptr=dptr=&FileNameBuffer;
do
{
if ((*sptr)!=' ') *dptr++=*sptr;
sptr++;
} while (*sptr);
*dptr=0; //ukonceni
}
//
gotEntry = 1;
break;
}
// otherwise
entrycount++; // increment entry counter
}
}
}
fat_next_dir_entry:
// next directory entry
de++;
// next index
index++;
}
// we have a file/dir to return
// store file/dir starting cluster (start of data)
FileInfo.vDisk.start_cluster = (unsigned short) (de->deStartCluster);
// fileindex
FileInfo.vDisk.file_index = entry; //fileindex teto polozky
// store file/dir size (note: size field for subdirectory entries is always zero)
FileInfo.vDisk.size = de->deFileSize;
// store file/dir attributes
FileInfo.Attr = de->deAttributes;
// store file/dir last update time
FileInfo.Time = (unsigned short) (de->deMTime[0] | de->deMTime[1]<<8);
// store file/dir last update date
FileInfo.Date = (unsigned short) (de->deMDate[0] | de->deMDate[1]<<8);

if(gotEntry)
{
debug(" gotEntry ");
last_dir_entry = entrycount;
last_dir_index = index;
last_dir_sector = sector-1; //protoze ihned po nacteni se posune
last_dir_cluster = actual_cluster;
last_dir_sector_count = seccount; //skace se az za inkrementaci seccount, takze tady se 1 neodecita!
last_dir_valid=gotEntry;
}
else
{
debug(" not gotEntry ");
}

return gotEntry;
}


unsigned short nextCluster(unsigned short cluster)
{
unsigned short nextCluster;
unsigned long fatOffset;
unsigned long sector;
unsigned long offset;

// two FAT bytes (16 bits) for every cluster
fatOffset = ((u32)cluster) << 1;

// calculate the FAT sector that we're interested in
sector = FirstFATSector + (fatOffset / ((u32)BytesPerSector));
// calculate offset of the our entry within that FAT sector
offset = fatOffset % ((u32)BytesPerSector);

// if we don't already have this FAT chunk loaded, go get it
// read sector of FAT table
mmcReadCached( sector );

// read the nextCluster value
nextCluster = (*((unsigned short*) &((char*)mmc_sector_buffer)[offset])) & FAT16_MASK;

// check to see if we're at the end of the chain
if (nextCluster == (CLUST_EOFE & FAT16_MASK))
nextCluster = 0;

return (nextCluster&0xFFFF);
}
(2-2/9)