⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fat.c

📁 fat文件系统的源码 老外写的FAT32文件系统 还是有用的
💻 C
字号:
/*
  Copyright (C) 2001 Jesper Hansen <jesperh@telia.com>.

  Rewritten by:	Nikolai Vorontsov <nickviz@mail.be>

  Rewritten by:	Romuald Bialy (aka MIS) <romek_b@o2.pl>

  FAT16 support idea by Pelos adapted to this code by MIS.

  This file is part of the yampp system.

  This program 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.
*/


#include <io.h>
#include <string.h>
#include <progmem.h>
#include <interrupt.h>

#include "fat.h"
#include "mem.h"
#include "ata_if.h"
#include "uart.h"
#include "Constants.h"

// Functions declaration
#ifdef ENABLE_NAVL
#define CHK_LETTER if (dirlisting) CheckLetter 
static void CheckLetter(void);
#else
#define CHK_LETTER()
#endif

#define SECT_PER_CLUST	(CLUSTER_SIZE / BPS)
#define SECT_PER_BUFFER	(BUFFER_SIZE / BPS)

#ifdef FAT16
 #define FAT_MASK FAT16_MASK
 #define BPFE 2			// 2 bytes per fat entry for FAT16
#else
 #define FAT_MASK FAT32_MASK
 #define BPFE 4			// 4 bytes per fat entry for FAT32
#endif

// Global variables
u32  dwFileSize;		// Current file size
u08 nLastPosL = 0;		// Letter tabele last element number
u16 nLastPosD = 0;		// Directory tabele last element number
u16 LastEntry = -1;		// For directory change checking
u32 dwCurrentCluster;		// Current being read cluster


// Internal variables
static u32 FirstDataSectorD, FirstFATSector, FirstDirCuster;
static u08 nSectorsReaded;	// Amount of sectors alerady read in current cluster
static u32 dwStartSector;	// Current cluster's start sector

#define clust2sect(clust) (clust * SECT_PER_CLUST + FirstDataSectorD)


// FAT structures initialization
u08 init_fat(void)
{
	struct partrecord *pr;
#ifdef FAT16
	struct bpb50 *bpb;
#else
	struct bpb710 *bpb;
#endif
	u32 first_sec;

 	ATA_SW_Reset();
#ifdef CHS_ACCES_FIRST
//++++++++++++++++++++++++++++++++++++++++
// fix from Uli (Ulrich.Behrenbeck@Informatik-Werkstatt.de)
	ATA_ReadCHS0(1, 1, 1, 1, TMP_SECTOR);  
	// LBA bit NOT set !!! a raw erroneous CHS access
//++++++++++++++++++++++++++++++++++++++++
#else
	ATA_ReadLBA0(0,	1, TMP_SECTOR);
#endif
	// read partition table
	ATA_ReadLBA0(0,	1, TMP_SECTOR);
	
	// get the partition record
	pr = (struct partrecord *) ((struct partsector*)TMP_SECTOR)->psPart;

	// and find the first valid sector
	first_sec = pr->prStartLBA;

	// Read the Partition BootSector
	ATA_ReadLBA0(first_sec, 1, TMP_SECTOR);

	// get BIOS parameter block
#ifdef FAT16
	bpb = (struct bpb50*)((struct bootsector50*)TMP_SECTOR)->bsBPB;
#else
	bpb = (struct bpb710*)((struct bootsector710*)TMP_SECTOR)->bsBPB;
#endif
	// and setup some constants
	if (bpb->bpbSecPerClust != SECT_PER_CLUST)	// Recompile code for this disk
//		return false;
		return bpb->bpbSecPerClust;

	FirstFATSector	= bpb->bpbResSectors + first_sec;
#ifdef FAT16
	FirstDirCuster   = bpb->bpbResSectors + (bpb->bpbFATs * bpb->bpbFATsecs) + first_sec;
	FirstDataSectorD = bpb->bpbResSectors + (bpb->bpbFATs * bpb->bpbFATsecs) + (bpb->bpbRootDirEnts / 16) + first_sec;
	FirstDirCuster   = ((FirstDirCuster - FirstDataSectorD) / SECT_PER_CLUST) + 2;
#else
	FirstDataSectorD = bpb->bpbResSectors + (bpb->bpbFATs * bpb->bpbBigFATsecs) + first_sec; // This is FirstDataSector
	FirstDirCuster	= bpb->bpbRootClust;
#endif
	FirstDataSectorD -= (2 * SECT_PER_CLUST);	// FirstDataSector - 2 clusters (thanks Jasper for bug correction)
	*(LONG_NAME - 1) = 0xFF;			//  placed to help in .mp3 extension checking
	return 0;
}

static u16 wCurEntry;		// Couner of current dir entry during get_de() 					
static u08 dirlisting = 0;	// Mode of diectory browsing 
static u08 *src, *dst;		// Temp vars, do not need to be in stack


// This function seeks for filename number entry, in folder started by
//  dwCurrentCluster. It returns pointer to the found structure or NULL pointer
// Always use SetStartCluster() before call of this function 
struct direntry *get_de(u16 entry)
{
	struct direntry *de = 0;
    u16 index;

	*LONG_NAME = 0;					// Clear the longname buffer

	index = 16 * SECT_PER_BUFFER;			// Amount of 8.3 dir entries per buffer
	do						//  crunk it to force to load buffer
	{
		if (index == 16 * SECT_PER_BUFFER)		// Time for a new buffer ?
		{
			if (CheckAndAdvanceCluster())
				ReadBuffer(BUFFER1P);
			else
				return 0;			// We've reached last cluster in this folder

			de = (struct direntry *)BUFFER1P;
			index = 0;
		}	
		if (*de->deName != 0xE5)	// if not a deleted entry
		{
			// if this is a long name entry
			if (de->deAttributes == ATTR_LONG_FILENAME)
			{
				struct winentry *we = (struct winentry *) de;
				register u08 i = 13 * ((we->weCnt - 1) & 0x0f);	// index into string
				dst = LONG_NAME + i;
				for (i = 0; i < 5 * 2; i += 2)	// copy first part
					*dst++ = we->wePart1[i];
				for (i = 0; i < 6 * 2; i += 2)	// second part
					*dst++ = we->wePart2[i];
				for (i = 0; i < 2 * 2; i += 2)	// and third part
					*dst++ = we->wePart3[i];
				if (we->weCnt & 0x40) *dst = 0;	// in case dirnamelength
												// is multiple of 13
			}
			else
			if ((de->deAttributes & // Normal file entry (including directories)
				(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME)) == 0  &&
				*de->deName != '.')	// Ignore the dot names
			{
				if (*LONG_NAME == 0)			// If the longname buffer is empty, copy 
				{					//  the short name in there
					LONG_NAME[8] = 0;
					src = memccpy(LONG_NAME, de->deName, ' ',8);	// Copy name up to occurence
					if (src)					// of first space of 8 chars long
						*--src = 0;				// if space found clear it
					if(*de->deExtension != ' ')			// if any extension found
					{
						strcat(LONG_NAME,".");			// add a "."
						strncat(LONG_NAME,de->deExtension,3);	// add 3 chars of extension
					}

				} // *LONG_NAME == 0

				if (de->deAttributes == ATTR_DIRECTORY)	// Is it a directory ?
				{
					struct direntry *subde;
					u32 dwPrevCluster;
					u08 nPrevSector;
					strcpy(DIR_NAME,LONG_NAME);
#if (LCD_LINES == 2)
					strcat(DIR_NAME," / ");
#else
					if(strlen(DIR_NAME) > LCD_LINE_LENGTH)
						strcat(DIR_NAME," >> ");
#endif
					dwPrevCluster = dwCurrentCluster;	// Save info for back-step
					nPrevSector = nSectorsReaded;

					union u32convert Data;			// Set new folder entry
					Data.words.high = de->deHighClust;
					Data.words.low = de->deStartCluster;
					SetStartCluster(Data.value);
					if ((dirlisting) && (nLastPosD < MAX_DIRS))		// fill DirsTabele
					{ 							// in dirlisting mode
						DirsTable[nLastPosD].song_num = wCurEntry;	// first file in dir for dir navigation
						DirsTable[nLastPosD++].dir_clust = dwCurrentCluster; // dir cluster number for fastload
//  		UART_Printfu16(wCurEntry); UART_SendByte(' '); UART_Printfu32(dwCurrentCluster); EOL();  // Debug
					}
					subde = get_de(entry);
					if (subde)
						return subde;
					else			// Restore original settings 	
					{	
						SetStartCluster(dwPrevCluster);
						nSectorsReaded = nPrevSector - SECT_PER_BUFFER;
						ReadBuffer(BUFFER1P);
						*DIR_NAME = 0;
					}
				}
				else // de->deAttributes != ATTR_DIRECTORY
				{
					// Check for ".mp3" extension, and if it exist,
					// then cut it off, else skip file
					src = LONG_NAME;
					while (*src) src++;			// start checking from the end 
					if (*--src == '3'  &&  
						(*--src == 'p'  ||  *src == 'P')  &&
						(*--src == 'm'  ||  *src == 'M') &&	*--src == '.')
					{
						*src = 0;				// cut it
						if (dirlisting == DIRLIST_VERBOSE)	
						{					// print the name
							UART_Puts(DIR_NAME);
							UART_Puts_p(PSTR(" / "));
							UART_Putsln(LONG_NAME);
						}
						// If this is the entry we're looking for, exit
						if (wCurEntry == entry  &&  !dirlisting)
							return de;
						CHK_LETTER();			// Always skipped if dirlisting == 0
						wCurEntry++;			// Increment entry counter		
					}
					*LONG_NAME = 0;
				} // if de->deAttributes == ATTR_DIRECTORY
			} // if de->deAttributes == normal file
		} // if not a deleted entry
		de++;		// point to next directory entry
		index++;	// increment index count within this buffer
	} while (index == (16 * SECT_PER_BUFFER) || *de->deName); 
					// 0 in de->deName[0] if no more entries
	return 0;
}


extern u08* unsigned2str(u16 track, u08 *str);

//
// do a directory listing 
// either just scan to find # of files,
// or print dir/filename on each
//
u16 dirlist(u08 mode)
{
	*DIR_NAME = 0;
	wCurEntry = 0;
	dirlisting = mode;
	nLastPosL = 0;
	nLastPosD = 0;
 	SetStartCluster(FirstDirCuster);
	get_de(-1);							// scan through all
	dirlisting = 0;
	return wCurEntry;
}


// This function sets information about MP3 file number 'entry'
// Returns true in file was found and false otherwise
bool get_dir_entry(u16 entry)
{
u16 i = nLastPosD;

#ifdef FAST_LOAD
	while ((i--) && DirsTable[i].song_num > entry);

	if (LastEntry==i)					// check for dir change
	{							// song in the same dir
		wCurEntry = DirsTable[i].song_num;		// set current entry at first file in dir
		SetStartCluster(DirsTable[i].dir_clust);	// set current dir cluster position
#if (LCD_LINES == 2)
		dst = strchr(DIR_NAME,'/');			// check for slash in directory name
		if(dst)						// if slash found
			*(dst+2) = 0;				// cut file name from dir string
		else						// slash not found, it's root directory
			*DIR_NAME = 0;				// clear old name
#endif
	}
	else	
#endif // FAST_LOAD
	{							// song in different dir
		wCurEntry = 0;
		SetStartCluster(FirstDirCuster);		// search from root directory
		*DIR_NAME = 0;					// clear dir name
		LastEntry = i;					// save actual dir number for directory change check
	}
	struct direntry *de = get_de(entry);
	if (de)
	{
		dwFileSize = de->deFileSize / 32;
		union u32convert Data;
		Data.words.high = de->deHighClust;
		Data.words.low = de->deStartCluster;
		SetStartCluster(Data.value);
		return true;
	}
	LastEntry = -1;						// Something wrong
	return false;
}


// This function initializes variables to read new file (dir)
void SetStartCluster(u32 dwStartCluster)
{
	dwCurrentCluster = dwStartCluster;
	dwStartSector  = clust2sect(dwStartCluster);
	nSectorsReaded = 0;
#ifdef FREW_SEARCH
	wBackStepPos = 0;
	Rev_Table[wBackStepPos++] = dwCurrentCluster;
#endif
}


// This function checks whether current cluster has beed read and advances
// cluster number if need.
// Returns:
//   DATA_EXIST	= 1	if current cluster isn't read completely or FAT sector 
//					 was in cache
//   FAT_READ	= 2	if data exist and new FAT sector was read into cache
//   NO_DATA	= 0	if EOF has been reached
u08 CheckAndAdvanceCluster(void)
{
	if (nSectorsReaded <= (CLUSTER_SIZE - BUFFER_SIZE) / BPS)
		return DATA_EXIST;			// Current cluster not read completely yet
	if (dwCurrentCluster >= FAT_MASK)
		return NO_DATA;
	return ReadNextCluster();
}


// Returns:
//   DATA_EXIST	= 1	if FAT sector was in cache
//   FAT_READ	= 2	if data exist and new FAT sector was read into cache
//   NO_DATA	= 0	if EOF has been reached
// This function reads next cluster in chain defined by dwCurrentCluster
u08 ReadNextCluster(void)
{
static u32 dwFatInCacheSector = 0;
#ifdef FREW_SEARCH
static u08 NumClust = 0;
#endif
	u08 nRes = DATA_EXIST;
	u32 dwFatSector = FirstFATSector + dwCurrentCluster / (BPS / BPFE);
	u08 nFatOffset  = ((u08)dwCurrentCluster) & (BPS / BPFE - 1);
	if (dwFatSector != dwFatInCacheSector)
	{
	    while (ATA_ReadLBA0(dwFatSector, 1, FAT_CACHE))
	    	ATA_SW_Reset();
	    dwFatInCacheSector = dwFatSector;
	    nRes = FAT_READ;
	}
	nSectorsReaded = 0;
#ifdef FAT16
	dwCurrentCluster = (*((u16*)FAT_CACHE + nFatOffset)) & FAT_MASK;	// Next cluster item in a chain
#else
	dwCurrentCluster = *((u32*)FAT_CACHE + nFatOffset);			// Next cluster item in a chain
#endif
	dwStartSector = clust2sect(dwCurrentCluster);
#ifdef FREW_SEARCH
	NumClust++;
	if ((NumClust == 0x18000 / CLUSTER_SIZE) && (wBackStepPos < MAX_REV_STEPS))
	{
		NumClust = 0;
		Rev_Table[wBackStepPos++] = dwCurrentCluster;
	}
#endif
	return (dwCurrentCluster < FAT_MASK) ? nRes : NO_DATA;
}


// This function reads BUFFER_SIZE of data from current cluster's position to one of bufers
void ReadBuffer(u08 *pBuf)
{
	while (ATA_ReadLBA0(dwStartSector + nSectorsReaded, BUFFER_SIZE / BPS, pBuf))
		ATA_SW_Reset();
	nSectorsReaded += BUFFER_SIZE / BPS;
}


#ifdef ENABLE_CRC

bool CheckBuffer(u08* buf)
{
	u16 crc, calc_crc;
	if (buf < (u08*)BUFFERSTART)
		return true;
	crc = crcs[((u16)buf - BUFFERSTART) / 0x1000];
	calc_crc = CalculateCRC(buf, 8 * 512);
	if (crc != calc_crc)
	{
		UART_Puts_p(PSTR("Err chk "));
		UART_Printfu16(crc);
		UART_Printfu16(calc_crc);
	}
	return crc == calc_crc;
}

#endif // ENABLE_CRC


#ifdef ENABLE_NAVL

static void CheckLetter(void)
{
	static u08 prev_letter = 'A' - 1;
	register u08 sym = *LONG_NAME;
	if (sym == 0)
		return;
	if (sym >= 'a'  &&  sym <= 'z')		// low case
		sym -= 'a' - 'A';
	if (sym < 'A')	sym = 'A';			// normalize
	if (sym > 'Z')	sym = 'Z';
	if (sym > prev_letter)				// new letter started
	{
		prev_letter = sym;
		LetterTable[nLastPosL].song_num = wCurEntry;
		LetterTable[nLastPosL++].letter = sym;
	}
}

#endif

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -