📄 fat.c
字号:
//++
//fat.c - MP3 Player Compact Flash Card functions
//
// Copyright (C) 2005 by Spare Time Gizmos. All rights reserved.
//
// This file is part of the Spare Time Gizmos' MP3 Player firmware.
//
// This firmware is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 59 Temple
// Place, Suite 330, Boston, MA 02111-1307 USA.
//
//DESCRIPTION:
// This module knows just enough about FAT file systems to a) mount the first
// partition on the CompactFlash card, b)list all the files in the root directory,
// and b) find and read the data in an individual file. Other than that it's
// pretty limited. It understands ONLY FAT16 file systems - neither FAT12 nor
// FAT32 are supported. It also only knows how to read files; there's nothing to
// create files nor allocate free disk space for writing them! This code does,
// however, have enough sanity checking to reject Compact Flash cards which don't
// meet our requirements.
//
//REFERENCES:
// http://www.compuphase.com/mbr_fat.htm
// http://support.microsoft.com/kb/q140418/
// http://encyclopedia.thefreedictionary.com/FAT16
// http://www.cse.scu.edu/~tschwarz/coen152_05/Lectures/FAT.html
//
//REVISION HISTORY:
// dd-mmm-yy who description
// 20-May-05 RLA New file.
// 20-Jul-05 RLA Change NextFATFile() and PreviousFATFile() so that they
// loop back when they reach the end/beginning of the root
// directory. This way they just repeat the same sequence
// of files over and over again - just what we want an MP3
// player to do.
// 12-Oct-05 RLA Change a lot of things that were parameters (e.g. volume
// label, FAT type, etc) into global static variables to
// make life easier for SDCC.
// 13-Oct-05 RLA Semi-major changes to a) remove all statically allocated
// disk buffers - instead, buffers are pulled from the free
// pool as needed - and b) to allow the directory sector
// buffer to be freed while an MP3 is actually playing. This
// gives us an extra buffer for MP3 data, which makes it
// possible to run on an 89C664 MCU with only 2K RAM.
// 18-Oct-05 RLA Add basic support for MBRs
// 19-Oct-05 RLA Make the current FAT buffer statically allocated in XDATA.
// Since it's permanently allocated there's no real reason
// to use a disk buffer, and doing so wastes a BCB entry and
// lots of IDATA space that SDCC can't afford (it works great
// with C51, though!).
// Move ID3 tag stuff to id3.c
// A "return TRUE" was accidentally left out of StartDirScan()...
// Add STACKSLOCS hack for SDCC (see slocs.h for more info)...
// 20-Oct-05 RLA Don't crash if we find an actual LFN longer than MAX_LFN_LENGTH
// Fix a little endian problem with GetFileLFN()
//--
// Include files...
#include <stdio.h> // needed so DBGOUT(()) can find printf!
#include <string.h> // strcpy(), etc...
#include "standard.h" // standard types - BYTE, WORD, BOOL, etc
#include "cfcard.h" // Compact Flash card I/O functions
#include "buffer.h" // Buffer management functions
#include "fat.h" // declarations for this module
#include "id3.h" // ID3 v1.1 definitions
// Public members...
// In the original C51 version, these items were usally automatic variables in another
// module (generally player.c) and passed as parameters to routines in this module. SDCC,
// however, isn't all that swift at dealing with parameters and local variables, so they
// were converted to globals in the interest of simplifying the generated code. Sadly,
// the code quality suffers somewhat as a result, but I guess that's why Keil can charge
// the big bucks...
PUBLIC XDATA char g_szVolumeLabel[VOLUME_LABEL_LENGTH]; // GetVolumeData()
PUBLIC XDATA char g_szSystemType[SYSTEM_TYPE_LENGTH]; // InitializeFAT()
PUBLIC XDATA WORD g_wTotalMP3Files; // GetVolumeData()
PUBLIC XDATA char g_szLongFileName[MAX_LFN_LENGTH]; // GetFileLFN()
// Volume data...
PRIVATE XDATA LONG m_lVolFATOffset; // offset (in sectors) of the first FAT
PRIVATE XDATA LONG m_lVolRootOffset; // " " " " " " " root directory
PRIVATE XDATA LONG m_lVolDataOffset; // " " " " " " " file data area
PRIVATE XDATA LONG m_lVolFATSize; // size of each FAT (in sectors)
PRIVATE XDATA LONG m_lVolRootSize; // size of the root directory (in sectors!)
PRIVATE XDATA BYTE m_bVolClusterSize; // sectors per cluster on this device
// Current directory sector status...
PRIVATE DATA BYTE m_bCurDirEntry; // offset of the current directory entry
PRIVATE DATA LONG m_lCurDirSector; // sector currently in directory buffer
// The next member points to the buffer used to read directory sectors. This can
// be, and often is, NULL, indicating that there is no buffer allocated. Especially
// while we're playing, the directory buffer is released to free up more memory for
// MP3 file data. Calling ReadDirSector() will allocate another buffer...
PRIVATE DATA PIBCB m_pCurDirBCB; // BCB of current directory sector
// Current FAT sector status...
PRIVATE IDATA LONG m_lCurFATSector; // LBN of the current sector (in m_wCurFAT)...
PRIVATE XDATA CLUSTER m_awCurFAT[CLUSTERS_PER_SECTOR];
// Current file status...
PRIVATE IDATA WORD m_wCurFileCluster; // current cluster we're reading
PRIVATE IDATA int m_nRelFileSector; // relative sector within the cluster
PRIVATE IDATA LONG m_lCurFileSector; // absolute sector (Cluster + RelSector)
PRIVATE IDATA LONG m_lFileBytesLeft; // bytes remaining before EOF
// This is a temporary directory entry which NextMP3File() and PrevMP3File() use
// to cache the current file data while we're playing an MP3 file...
PRIVATE XDATA DIRECTORY_ENTRY m_CurDirEntCache;
// This macro generates a pointer (of type PXDIRENT) to file #n in the current
// directory sector, m_pCurDirBCB. The index n can safely range from 0 to
// DIRECTORY_ENTRIES_PER_SECTOR-1. It's just a metter of caste ...
#define GetDirEntry(n) ( (PXDIRENT) (m_pCurDirBCB->pbBuffer + (n)*sizeof(DIRECTORY_ENTRY)) )
// This macro is essentially the same, except that it always returns the
// current directory entry, as determined by m_bCurDirEntry...
#define GetCurDirEntry() GetDirEntry(m_bCurDirEntry)
//++
// This debugging routine will dump out the contents of a sector buffer in
// both hexadecimal and ASCII. There's not much too it!
//--
#if 0
PRIVATE void DumpSector (PXBYTE pxBuffer)
{
WORD wOffset, i; char ch;
for (wOffset = 0; wOffset < IDE_SECTOR_SIZE; wOffset += 16) {
printf("%03X/ ", wOffset);
for (i = 0; i < 16; ++i)
printf(" %02bx", pxBuffer[wOffset+i]);
printf(" ");
for (i = 0; i < 16; ++i) {
ch = pxBuffer[wOffset+i] & 0x7F;
if ((ch < ' ') || (ch >= 0x7F)) ch = '.';
printf("%c", ch);
}
printf("\n");
}
}
#endif
//++
// Show one entry from the partition table in the MBR. For debugging only!
//--
#ifdef SHOWPARTITION
PRIVATE void ShowPartition (BYTE bPart, MASTER_BOOT_RECORD XDATA *pxMBR)
{
DBGOUT(("Partition #%bd - Flags = 0x%02bX, Type = 0x%02bX\n",
bPart, pxMBR->aPartitions[bPart].bFlags, pxMBR->aPartitions[bPart].bType));
DBGOUT(("\tStartCHS = 0x%02bX %02bX %02bX, EndCHS = 0x%02bX %02bX %02bX\n",
pxMBR->aPartitions[bPart].abStartCHS[0], pxMBR->aPartitions[bPart].abStartCHS[1],
pxMBR->aPartitions[bPart].abStartCHS[2], pxMBR->aPartitions[bPart].abEndCHS[0],
pxMBR->aPartitions[bPart].abEndCHS[1], pxMBR->aPartitions[bPart].abEndCHS[2]));
DBGOUT(("\tOffset (LBN) = %ld, Size (sectors) = %ld\n",
LSWAP(pxMBR->aPartitions[bPart].lOffset), LSWAP(pxMBR->aPartitions[bPart].lSize)));
DBGOUT(("Master boot record signature = 0x%04X\n", WSWAP(pxMBR->wSignature)));
}
#endif
//++
// This debugging routine will print the contents of the boot sector.
// It's invaluable for figuring out what's wrong...
//--
#ifdef SHOWBOOTSECTOR
PRIVATE void ShowBootSector (BOOT_SECTOR XDATA *pxVBS)
{
BPB XDATA *pxBPB = &(pxVBS->bpb);
printf("Volume Boot Sector\n");
printf("\tabJump = 0x%02bX %02bX %02bX \n", pxVBS->abJump[0], pxVBS->abJump[1], pxVBS->abJump[2]);
printf("\tszOEMName[8] = \"%8s\"\n", pxVBS->szOEMName);
printf("\twBootSignature = 0x%04X\n", WSWAP(pxVBS->wBootSignature));
printf("BIOS Parameter Block\n");
printf("\twBytesPerSector = %d\n", WSWAP(pxBPB->wBytesPerSector));
printf("\tbSectorsPerCluster = %bd\n", pxBPB->bSectorsPerCluster);
printf("\twReservedSectors = %d\n", WSWAP(pxBPB->wReservedSectors));
printf("\tbNumberFATs = %bd\n", pxBPB->bNumberFATs);
printf("\twRootEntries = %d\n", WSWAP(pxBPB->wRootEntries));
printf("\twSmallSectors = %d\n", WSWAP(pxBPB->wSmallSectors));
printf("\tbMediaDescriptor = 0x%02bX\n", pxBPB->bMediaDescriptor);
printf("\twSectorsPerFAT = %d\n", WSWAP(pxBPB->wSectorsPerFAT));
printf("\twSectorsPerTrack = %d\n", WSWAP(pxBPB->wSectorsPerTrack));
printf("\twTotalHeads = %d\n", WSWAP(pxBPB->wTotalHeads));
printf("\tlHiddenSectors = %ld\n", LSWAP(pxBPB->lHiddenSectors));
printf("\tlLargeSectors = %ld\n", LSWAP(pxBPB->lLargeSectors));
printf("\tbPhysicalDrive = %bd\n", pxBPB->bPhysicalDrive);
printf("\tbCurrentHead = %bd\n", pxBPB->bCurrentHead);
printf("\tbBootSignature = 0x%02bX\n", pxBPB->bBootSignature);
printf("\tlVolumeID = 0x%08lX\n", LSWAP(pxBPB->lVolumeID));
printf("\tszVolumeLabel[11] = \"%11.11s\"\n", &(pxBPB->szVolumeLabel));
printf("\tszSystemID[8] = \"%8.8s\"\n", &(pxBPB->szSystemID));
}
#endif
//++
// This routine will read sector zero from the flash card and attempt
// to determine if it contains a valid master boot record. If an MBR
// is present, then this code uses the partition table in the MBR to
// find and return the first sector of the first partition on the drive.
// If no MBR is found, then it returns zero.
//
// The value returned thus tells us where to go to look for the volume
// boot sector (VBS), which is the next step in reading the file system.
// Notice that it's no accident that zero is returned if no MBR is found,
// because in the case of flash cards that have been formatted without
// any MBR (i.e. they're formatted as if they were a big floppy disk) then
// sector zero is where we should look next for the VBS.
//
// BTW, I know of no single, definitive, test to determine whether a volume
// has an MBR or not. Unfortunately the 0xAA55 signature that's stored at the
// end of the MBR just happens to be the same signarture, and stored in the
// same place, that you'd find in a VBS. Worse, most of the MBR is usually
// filled with zeros, and the partition table is the only part which holds any
// meaningful data. That doesn't give us a lot to go on!
//--
PRIVATE LONG ReadMBR (void) STACKSLOCS
{
PIBCB pBCB; MASTER_BOOT_RECORD XDATA *pxMBR; BYTE i;
// Allocate a buffer and read the master boot record. Note that although
// the VBS can move around, the MBR is ALWAYS in first sector on the disk!
ALLOCATE_BUFFER(pBCB);
if (pBCB == NULL) return 0L;
//DBGOUT(("ReadMBR: reading master boot record from LBN 0\n"));
if (!ReadSector(0L, pBCB->pbBuffer)) goto FAIL;
pxMBR = (MASTER_BOOT_RECORD XDATA *) pBCB->pbBuffer;
// If the signature isn't valid, then this is neither a master boot record
// nor a volume boot sector. There isn't much we can do except give up!
if (WSWAP(pxMBR->wSignature) != 0xAA55) goto FAIL;
// Go thru all the partition table entries until we find one that's usable.
for (i = 0; i < MAX_PARTITIONS; ++i) {
#ifdef SHOWPARTITION
ShowPartition(i, pxMBR);
#endif
// Check the system type to see if this partition is a DOS FAT16 volume.
// Unfortunately there are several types which are valid!
if ( (pxMBR->aPartitions[i].bType != 0x04) // FAT16 for partitions <= 32Mb
&& (pxMBR->aPartitions[i].bType != 0x06) // FAT16 for partitions > 32Mb
&& (pxMBR->aPartitions[i].bType != 0x0E) ) // BIGDOS FAT16 partition
continue;
// For the partition to be valid, the flags have to be either 0x00 (normal
// partition) or 0x80 (a bootable partition). The latter isn't likely, but
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -