repo2/firmware/defunct/fat.c @ 461
46 | markw | /*! \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);
|
|||
}
|