Main Page | Modules | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

# fat.c

Go to the documentation of this file.

    00001 /*! \file fat.c \brief FAT16/32 file system driver. */
    00002 //*****************************************************************************
    00003 //
    00004 // File Name    : 'fat.c'
    00005 // Title        : FAT16/32 file system driver
    00006 // Author       : Pascal Stang
    00007 // Date         : 11/07/2000
    00008 // Revised      : 12/12/2000
    00009 // Version      : 0.3
    00010 // Target MCU   : ATmega103 (should work for Atmel AVR Series)
    00011 // Editor Tabs  : 4
    00012 //
    00013 // This code is based in part on work done by Jesper Hansen for his
    00014 //      YAMPP MP3 player project.
    00015 //
    00016 // NOTE: This code is currently below version 1.0, and therefore is considered
    00017 // to be lacking in some functionality or documentation, or may not be fully
    00018 // tested.  Nonetheless, you can expect most functions to work.
    00019 //
    00020 // This code is distributed under the GNU Public License
    00021 //      which can be found at http://www.gnu.org/licenses/gpl.txt
    00022 //
    00023 //*****************************************************************************
    00024
    00025
    00026 #include <avr/io.h>
    00027 #include <avr/pgmspace.h>
    00028 #include <string.h>
    00029
    00030 #include "ata.h"
    00031 #include "rprintf.h"
    00032 #include "debug.h"
    00033
    00034 #include "fat.h"
    00035 #include "fatconf.h"
    00036
    00037 // globals
    00038 // buffers
    00039 unsigned char *SectorBuffer  =      (unsigned char *) FAT_SECTOR_BUFFER_ADDR;
    00040 unsigned char *FileNameBuffer =     (unsigned char *) FAT_FILENAME_BUFFER_ADDR;
    00041 unsigned char *PathNameBuffer =     (unsigned char *) FAT_PATHNAME_BUFFER_ADDR;
    00042
    00043 // filesystem constants/metrics
    00044 struct partrecord PartInfo;
    00045 unsigned char Fat32Enabled;
    00046 unsigned long FirstDataSector;
    00047 unsigned short BytesPerSector;
    00048 unsigned short SectorsPerCluster;
    00049 unsigned long FirstFATSector;
    00050 unsigned long RootDirStartCluster;
    00051
    00052 // operating variables
    00053 unsigned long CurrentDirStartCluster;   //< current directory starting cluster
    00054 struct FileInfoStruct FileInfo;         //< file information for last file accessed
    00055 unsigned long FatInCache = 0;
    00056
    00057
    00058 /*************************************************************************/
    00059 /*************************************************************************/
    00060
    00061
    00062 unsigned long fatClustToSect(unsigned long clust)
    00063 {
    00064     return ((clust-2) * SectorsPerCluster) + FirstDataSector;
    00065 }
    00066
    00067 unsigned int fatClusterSize(void)
    00068 {
    00069     // return the number of sectors in a disk cluster
    00070     return SectorsPerCluster;
    00071 }
    00072
    00073 unsigned char fatInit( unsigned char device)
    00074 {
    00075     //struct partrecord *pr;
    00076     struct bpb710 *bpb;
    00077
    00078     // read partition table
    00079     // TODO.... error checking
    00080     ataReadSectors(DRIVE0, 0, 1, SectorBuffer);
    00081     // map first partition record
    00082     // save partition information to global PartInfo
    00083     PartInfo = *((struct partrecord *) ((struct partsector *) SectorBuffer)->psPart);
    00084 //  PartInfo = *pr;
    00085
    00086     // Read the Partition BootSector
    00087     // **first sector of partition in PartInfo.prStartLBA
    00088     ataReadSectors( DRIVE0, PartInfo.prStartLBA, 1, SectorBuffer );
    00089     bpb = (struct bpb710 *) ((struct bootsector710 *) SectorBuffer)->bsBPB;
    00090
    00091     // setup global disk constants
    00092     FirstDataSector = PartInfo.prStartLBA;
    00093     if(bpb->bpbFATsecs)
    00094     {
    00095         // bpbFATsecs is non-zero and is therefore valid
    00096         FirstDataSector += bpb->bpbResSectors + bpb->bpbFATs * bpb->bpbFATsecs;
    00097     }
    00098     else
    00099     {
    00100         // bpbFATsecs is zero, real value is in bpbBigFATsecs
    00101         FirstDataSector += bpb->bpbResSectors + bpb->bpbFATs * bpb->bpbBigFATsecs;
    00102     }
    00103     SectorsPerCluster   = bpb->bpbSecPerClust;
    00104     BytesPerSector      = bpb->bpbBytesPerSec;
    00105     FirstFATSector      = bpb->bpbResSectors + PartInfo.prStartLBA;
    00106
    00107     switch (PartInfo.prPartType)
    00108     {
    00109         case PART_TYPE_DOSFAT16:
    00110         case PART_TYPE_FAT16:
    00111         case PART_TYPE_FAT16LBA:
    00112             // first directory cluster is 2 by default (clusters range 2->big)
    00113             RootDirStartCluster = CLUST_FIRST;
    00114             // push data sector pointer to end of root directory area
    00115             //FirstDataSector += (bpb->bpbRootDirEnts)/DIRENTRIES_PER_SECTOR;
    00116             Fat32Enabled = FALSE;
    00117             break;
    00118         case PART_TYPE_FAT32LBA:
    00119         case PART_TYPE_FAT32:
    00120             // bpbRootClust field exists in FAT32 bpb710, but not in lesser bpb's
    00121             RootDirStartCluster = bpb->bpbRootClust;
    00122             // push data sector pointer to end of root directory area
    00123             // need this? FirstDataSector += (bpb->bpbRootDirEnts)/DIRENTRIES_PER_SECTOR;
    00124             Fat32Enabled = TRUE;
    00125             break;
    00126         default:
    00127             rprintfProgStrM("Found: No Partition!\r\n");
    00128             //return 1;
    00129             break;
    00130     }
    00131
    00132     // set current directory to root (\)
    00133     CurrentDirStartCluster = RootDirStartCluster;
    00134     PathNameBuffer[0] = '\\';
    00135     PathNameBuffer[1] = 0;
    00136
    00137
    00138     // do debug
    00139 #ifdef DEBUG_FAT
    00140     switch (PartInfo.prPartType)
    00141     {
    00142         case PART_TYPE_DOSFAT16:
    00143                 rprintfProgStrM("Found: DOSFAT 16\r\n");
    00144                 break;
    00145         case PART_TYPE_FAT16:
    00146                 rprintfProgStrM("Found: FAT16\r\n");
    00147                 break;
    00148         case PART_TYPE_FAT16LBA:
    00149                 rprintfProgStrM("Found: FAT16 LBA\r\n");
    00150                 break;
    00151         case PART_TYPE_FAT32LBA:
    00152                 rprintfProgStrM("Found: FAT32 LBA\r\n");
    00153                 break;
    00154         case PART_TYPE_FAT32:
    00155                 rprintfProgStrM("Found: FAT32\r\n");
    00156                 //return 1;
    00157                 break;
    00158         default:
    00159                 rprintfProgStrM("Found: No Partition!\r\n");
    00160                 //return 1;
    00161                 break;
    00162     }
    00163
    00164     rprintfProgStrM("First sector    : ");  rprintfu32(PartInfo.prStartLBA);    rprintfCRLF();
    00165     rprintfProgStrM("Size            : ");  rprintfu32(PartInfo.prSize);        rprintfCRLF();
    00166     rprintfProgStrM("bytes/sector    : ");  rprintfu16(bpb->bpbBytesPerSec);    rprintfCRLF();
    00167     rprintfProgStrM("sectors/cluster : ");  rprintfu08(bpb->bpbSecPerClust);    rprintfCRLF();
    00168     rprintfProgStrM("reserved sectors: ");  rprintfu16(bpb->bpbResSectors);     rprintfCRLF();
    00169     rprintfProgStrM("FatSectors      : ");  rprintfu16(bpb->bpbFATsecs);        rprintfCRLF();
    00170     rprintfProgStrM("BigFatSectors   : ");  rprintfu32(bpb->bpbBigFATsecs);     rprintfCRLF();
    00171     rprintfProgStrM("Number of Fats  : ");  rprintfu08(bpb->bpbFATs);           rprintfCRLF();
    00172     rprintfProgStrM("First Fat Sector: ");  rprintfu32(FirstFATSector);         rprintfCRLF();
    00173     rprintfProgStrM("First Data Sect : ");  rprintfu32(FirstDataSector);        rprintfCRLF();
    00174     rprintfProgStrM("RootDirStartClus: ");  rprintfu32(RootDirStartCluster);    rprintfCRLF();
    00175 #endif
    00176
    00177     return 0;
    00178 }
    00179
    00180 //////////////////////////////////////////////////////////////
    00181
    00182 unsigned char fatGetDirEntry(unsigned short entry)
    00183 {
    00184     unsigned long sector;
    00185     struct direntry *de = 0;    // avoid compiler warning by initializing
    00186     struct winentry *we;
    00187     unsigned char haveLongNameEntry;
    00188     unsigned char gotEntry;
    00189     unsigned short b;
    00190     int i,index;
    00191     char *fnbPtr;
    00192     unsigned short entrycount = 0;
    00193
    00194     // read dir data
    00195     sector = fatClustToSect(CurrentDirStartCluster);
    00196
    00197     haveLongNameEntry = 0;
    00198     gotEntry = 0;
    00199
    00200     index = 16; // crank it up
    00201
    00202     //while(entrycount < entry)
    00203     while(1)
    00204     {
    00205         if(index == 16) // time for next sector ?
    00206         {
    00207             ataReadSectors( DRIVE0, sector++, 1, SectorBuffer);
    00208             de = (struct direntry *) SectorBuffer;
    00209             index = 0;
    00210         }
    00211
    00212         // check the status of this directory entry slot
    00213         if(de->deName[0] == 0x00)
    00214         {
    00215             // slot is empty and this is the end of directory
    00216             gotEntry = 0;
    00217             break;
    00218         }
    00219         else if(de->deName[0] == 0xE5)
    00220         {
    00221             // this is an empty slot
    00222             // do nothing and move to the next one
    00223         }
    00224         else
    00225         {
    00226             // this is a valid and occupied entry
    00227             // is it a part of a long file/dir name?
    00228             if(de->deAttributes == ATTR_LONG_FILENAME)
    00229             {
    00230                 // we have a long name entry
    00231                 // cast this directory entry as a "windows" (LFN: LongFileName) entry
    00232                 we = (struct winentry *) de;
    00233
    00234                 b = WIN_ENTRY_CHARS*( (we->weCnt-1) & 0x0f);        // index into string
    00235                 fnbPtr = &FileNameBuffer[b];
    00236                 for (i=0;i<5;i++)   *fnbPtr++ = we->wePart1[i*2];   // copy first part
    00237                 for (i=0;i<6;i++)   *fnbPtr++ = we->wePart2[i*2];   // second part
    00238                 for (i=0;i<2;i++)   *fnbPtr++ = we->wePart3[i*2];   // and third part
    00239                 if (we->weCnt & WIN_LAST) *fnbPtr = 0;              // in case dirnamelength is multiple of 13, add termination
    00240                 if ((we->weCnt & 0x0f) == 1) haveLongNameEntry = 1; // flag that we have a complete long name entry set
    00241             }
    00242             else
    00243             {
    00244                 // we have a short name entry
    00245
    00246                 // check if this is the short name entry corresponding
    00247                 // to the end of a multi-part long name entry
    00248                 if(haveLongNameEntry)
    00249                 {
    00250                     // a long entry name has been collected
    00251                     if(entrycount == entry)
    00252                     {
    00253                         // desired entry has been found, break out
    00254                         gotEntry = 1;
    00255                         break;
    00256                     }
    00257                     // otherwise
    00258                     haveLongNameEntry = 0;  // clear long name flag
    00259                     entrycount++;           // increment entry counter
    00260                 }
    00261                 else
    00262                 {
    00263                     // entry is a short name (8.3 format) without a
    00264                     // corresponding multi-part long name entry
    00265                     fnbPtr = FileNameBuffer;
    00266                     for (i=0;i<8;i++)   *fnbPtr++ = de->deName[i];      // copy name
    00267                     *fnbPtr++ = '.';                                    // insert '.'
    00268                     for (i=0;i<3;i++)   *fnbPtr++ = de->deExtension[i]; // copy extension
    00269                     *fnbPtr = 0;                                        // null-terminate
    00270
    00271                     if(entrycount == entry)
    00272                     {
    00273                         // desired entry has been found, break out
    00274                         gotEntry = 1;
    00275                         break;
    00276                     }
    00277                     // otherwise
    00278                     entrycount++;           // increment entry counter
    00279                 }
    00280             }
    00281         }
    00282         // next directory entry
    00283         de++;
    00284         // next index
    00285         index++;
    00286     }
    00287
    00288     // we have a file/dir to return
    00289     // store file/dir starting cluster (start of data)
    00290     FileInfo.StartCluster = (unsigned long) ((unsigned long)de->deHighClust << 16) + de->deStartCluster;
    00291     // store file/dir size
    00292     // (note: size field for subdirectory entries is always zero)
    00293     FileInfo.Size = de->deFileSize;
    00294     // store file/dir attributes
    00295     FileInfo.Attr = de->deAttributes;
    00296     // store file/dir creation time
    00297     FileInfo.CreateTime = de->deCTime[0] | de->deCTime[1]<<8;
    00298     // store file/dir creation date
    00299     FileInfo.CreateTime = de->deCDate[0] | de->deCDate[1]<<8;
    00300
    00301     return gotEntry;
    00302 }
    00303
    00304 // change directory into
    00305 unsigned char fatChangeDirectory(unsigned short entry)
    00306 {
    00307     // get the requested directory entry
    00308     if( fatGetDirEntry(entry) )
    00309     {
    00310         // make sure the entry is a directory
    00311         if(FileInfo.Attr & ATTR_DIRECTORY)
    00312         {
    00313             // change directories into this directory
    00314             // check to see if we are changing back to root directory
    00315             if(FileInfo.StartCluster)
    00316             {
    00317                 // standard change directory
    00318                 CurrentDirStartCluster = FileInfo.StartCluster;
    00319             }
    00320             else
    00321             {
    00322                 // if startCluster pointer is zero,
    00323                 // a change to the root directory is intended
    00324                 // change directory to root
    00325                 CurrentDirStartCluster = RootDirStartCluster;
    00326             }
    00327             // TODO: handle pathname properly for going up a directory
    00328             // set path string
    00329             strcat(PathNameBuffer, FileNameBuffer);
    00330             strcat(PathNameBuffer, "\\");
    00331             // return success
    00332             return TRUE;
    00333         }
    00334         else
    00335         {
    00336             // not a directory, cannot CD into a file!
    00337             return FALSE;
    00338         }
    00339     }
    00340     else
    00341     {
    00342         // not a valid entry, cannot CD!
    00343         return FALSE;
    00344     }
    00345 }
    00346
    00347 void fatPrintDirEntry(void)
    00348 {
    00349     // print a formatted dir-style output for most recent file
    00350     // print date
    00351     rprintfNum(10, 2, FALSE, '0', (FileInfo.CreateDate&DD_MONTH_MASK)>>DD_MONTH_SHIFT );    // month
    00352     rprintfChar('/');
    00353     rprintfNum(10, 2, FALSE, '0', (FileInfo.CreateDate&DD_DAY_MASK)>>DD_DAY_SHIFT );        // day
    00354     rprintfChar('/');
    00355     rprintfNum(10, 4, FALSE, '0', (FileInfo.CreateDate&DD_YEAR_MASK)>>DD_YEAR_SHIFT );  // year
    00356     rprintfChar(' ');
    00357
    00358     // print time
    00359     rprintfNum(10, 2, FALSE, '0', (FileInfo.CreateTime&DT_HOURS_MASK)>>DT_HOURS_SHIFT );    // month
    00360     rprintfChar(':');
    00361     rprintfNum(10, 2, FALSE, '0', (FileInfo.CreateTime&DT_MINUTES_MASK)>>DT_MINUTES_SHIFT );        // day
    00362     rprintfChar(':');
    00363     rprintfNum(10, 2, FALSE, '0', 2*(FileInfo.CreateTime&DT_2SECONDS_MASK)>>DT_2SECONDS_SHIFT );    // seconds
    00364     rprintfChar(' ');
    00365
    00366     // print attributes
    00367     if(FileInfo.Attr & ATTR_VOLUME)     rprintfChar('V'); else rprintfChar('-');
    00368     if(FileInfo.Attr & ATTR_DIRECTORY)  rprintfChar('D'); else rprintfChar('-');
    00369     if(FileInfo.Attr & ATTR_READONLY)   rprintfChar('R'); else rprintfChar('-');
    00370     if(FileInfo.Attr & ATTR_HIDDEN)     rprintfChar('H'); else rprintfChar('-');
    00371     if(FileInfo.Attr & ATTR_SYSTEM)     rprintfChar('S'); else rprintfChar('-');
    00372     if(FileInfo.Attr & ATTR_ARCHIVE)    rprintfChar('A'); else rprintfChar('-');
    00373     rprintfChar(' ');
    00374
    00375     // print filesize
    00376     rprintfNum(10, 8, FALSE, ' ', FileInfo.Size);   // filesize
    00377     rprintfChar(' ');
    00378
    00379     // print filename
    00380     rprintfStr(FileNameBuffer);
    00381 }
    00382
    00383 void fatDumpDirSlot(unsigned short slot)
    00384 {
    00385     unsigned long sector;
    00386     // load correct sector
    00387     sector = fatClustToSect(CurrentDirStartCluster);
    00388     sector += slot/DIRENTRIES_PER_SECTOR;
    00389     // print the entry as a hex table
    00390     debugPrintHexTable(32, SectorBuffer+(slot<<5) );
    00391 }
    00392
    00393 struct FileInfoStruct* fatGetFileInfo(void)
    00394 {
    00395     return &FileInfo;
    00396 }
    00397
    00398 // return the size of the last directory entry
    00399 unsigned long fatGetFilesize(void)
    00400 {
    00401     return FileInfo.Size;
    00402 }
    00403
    00404 // return the long name of the last directory entry
    00405 char* fatGetFilename(void)
    00406 {
    00407     return FileNameBuffer;
    00408 }
    00409
    00410 // return the directory of the last directory entry
    00411 char* fatGetDirname(void)
    00412 {
    00413     return PathNameBuffer;
    00414 }
    00415
    00416 // load a clusterfull of data
    00417 void fatLoadCluster(unsigned long cluster, unsigned char *buffer)
    00418 {
    00419     register unsigned char i;
    00420     // read cluster
    00421     //while ( ataReadSectors( DRIVE0, clust2sect(cluster), SectorsPerCluster, buffer) != 0);
    00422     for(i=0; i<SectorsPerCluster; i++)
    00423     {
    00424         ataReadSectors( DRIVE0, fatClustToSect(cluster)+i, 1, buffer+(i<<9) );
    00425         // temporary fix for wierd misaligned cluster problem
    00426         // (only when using FAT16?)
    00427 //      ataReadSectors( DRIVE0, fatClustToSect(cluster+8)+i, 1, buffer+(i<<9) );
    00428     }
    00429 }
    00430
    00431
    00432 // find next cluster in the FAT chain
    00433 unsigned long fatNextCluster(unsigned long cluster)
    00434 {
    00435     unsigned long nextCluster;
    00436     unsigned long fatMask;
    00437     unsigned long fatOffset;
    00438     unsigned long sector;
    00439     unsigned int offset;
    00440
    00441     // get fat offset in bytes
    00442     if(Fat32Enabled)
    00443     {
    00444         // four FAT bytes (32 bits) for every cluster
    00445         fatOffset = cluster << 2;
    00446         // set the FAT bit mask
    00447         fatMask = FAT32_MASK;
    00448     }
    00449     else
    00450     {
    00451         // two FAT bytes (16 bits) for every cluster
    00452         fatOffset = cluster << 1;
    00453         // set the FAT bit mask
    00454         fatMask = FAT16_MASK;
    00455     }
    00456
    00457     // calculate the FAT sector that we're interested in
    00458     sector = FirstFATSector + (fatOffset / BytesPerSector);
    00459     // calculate offset of the our entry within that FAT sector
    00460     offset = fatOffset % BytesPerSector;
    00461
    00462     // if we don't already have this FAT chunk loaded, go get it
    00463     if (sector != FatInCache)
    00464     {
    00465         // read sector of FAT table
    00466         while (ataReadSectors( DRIVE0, sector, 1, (unsigned char*)FAT_CACHE_ADDR) != 0);
    00467         FatInCache = sector;
    00468     }
    00469
    00470     // read the nextCluster value
    00471     nextCluster = (*((unsigned long*) &((char*)FAT_CACHE_ADDR)[offset])) & fatMask;
    00472
    00473     // check to see if we're at the end of the chain
    00474     if (nextCluster == (CLUST_EOFE & fatMask))
    00475         nextCluster = 0;
    00476
    00477 #ifdef DEBUG_FAT
    00478     rprintfProgStrM(">");
    00479     rprintfu32(nextCluster);
    00480     rprintfCRLF();
    00481 #endif
    00482
    00483     return nextCluster;
    00484 }