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 }

Generated on Sun Oct 29 03:41:06 2006 for Procyon AVRlib by  doxygen 1.4.2