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

📄 fat.c

📁 Yet Another MP3 Player source code. It can be usefull as reference source for any project.
💻 C
字号:
/*
  Copyright (C) 2001 Jesper Hansen <jesperh@telia.com>.

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

  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"

// Functions declaration
static bool ReadNextCluster(void);
#ifdef ENABLE_NAV
#define CHK_LETTER if (dirlisting) CheckLetter 
static void CheckLetter(void);
u08 nLastPos = 0;
#else
#define CHK_LETTER()
#endif

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

// Global variables
u32  dwFileSize;					// Current file size
bool bBuf1Empty	= true;				// Empty flags for buffers
bool bBuf2Empty	= true;

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

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


// FAT structures initialization
bool init_fat(u08 device)
{
	struct partrecord *pr;
	struct bpb710 *bpb;
	struct fsinfo *fsi;
	u32 first_sec;

//++++++++++++++++++++++++++++++++++++++++
// fix from Uli (Ulrich.Behrenbeck@Informatik-Werkstatt.de)
 	ATA_SW_Reset();
    ATA_ReadCHS0(1, 1, 1, 1, TMP_SECTOR);  
	// LBA bit NOT set !!! a raw erroneous CHS access
//++++++++++++++++++++++++++++++++++++++++

	// 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
	bpb = (struct bpb710 *)((struct bootsector710*)TMP_SECTOR)->bsBPB;

	// and setup some constants
	FirstDataSectorD = (-2 * SECT_PER_CLUST) + 
					   bpb->bpbResSectors + 
					   bpb->bpbFATs * bpb->bpbBigFATsecs +
					   first_sec; // This is FirstDataSector - 2 clusters
	if (bpb->bpbSecPerClust != SECT_PER_CLUST)	// Recompile code for this disk
		return false;

	FirstFATSector	= bpb->bpbResSectors + first_sec;
	FirstDirCuster	= bpb->bpbRootClust;

	*(u16*)(LONG_NAME - 2) = 0xFFFF;			// This are special 3 bytes, 
	*(LONG_NAME - 3) = 0xFF;					//  placed to help in .mp3
												//  extension checking

    ATA_ReadLBA0(first_sec + 1, 1, TMP_SECTOR);	// MSInfo sector
	fsi = (struct fsinfo*)TMP_SECTOR;
	// check for signature precense
	// if (fsi->sig1 == 0x41615252  &&  fsi->sig2 == 0x61417272)
	FirstFreeCluster = fsi->fsinfree;

	return true;
}

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(BUF1);
			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
					register u08 i;
					dst = LONG_NAME;
					for (i = 0; i < 8; i++)		// Copy first part
						if (de->deName[i] != ' ')// Do not copy spaces
							*dst++ = de->deName[i];
						else
							break;
					*dst++ = '.';
					for (i = 0; i < 3; i++)		// Copy second part
						if (de->deExtension[i] != ' ')
							*dst++ = (de->deExtension[i]);
						else
							break;
					*dst = 0;
				} // *LONG_NAME == 0

				if (de->deAttributes == ATTR_DIRECTORY)	// Is it a directory ?
				{
					struct direntry *subde;
					u32 dwPrevCluster;
					u08 nPrevSector;

					src = LONG_NAME;			// Copy name buffer to
					dst = DIR_NAME;				//  directory buffer
					while (*src) *dst++ = *src++;
					*dst++ = '\\'; *dst = '\0';	// Add slash

					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);
					subde = get_de(entry);
					if (subde)
						return subde;
					else			// Restore original settings 	
					{	
						SetStartCluster(dwPrevCluster);
						nSectorsReaded = nPrevSector - SECT_PER_BUFFER;
						ReadBuffer(BUF1);
						*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_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)
{
#ifdef ENABLE_NAV
	register u08 i;
	union u32convert conv;
	conv.words.low  = eeprom_rw(EEPROM_FREECLUST1);
	conv.words.high = eeprom_rw(EEPROM_FREECLUST2);
#endif
	*DIR_NAME = 0;
	wCurEntry = eeprom_rw(EEPROM_MAXSONGS);

	if (wCurEntry == 0 || wCurEntry == -1 || 
		mode == DIRLIST_VERBOSE 
#ifdef ENABLE_NAV
		|| FirstFreeCluster != conv.value
#endif
		)
	{
		wCurEntry = 0;
		dirlisting = mode;
		SetStartCluster(FirstDirCuster);
		get_de(-1);							// scan through all
		eeprom_ww(EEPROM_MAXSONGS, wCurEntry);
#ifdef ENABLE_NAV
		eeprom_wb(EEPROM_MAXLETTERS, nLastPos);
		for (i = 0; i < nLastPos; i++)
		{
			eeprom_ww(EEPROM_ORDER + i * sizeof(LetterTable[0]),
													LetterTable[i].song_num);
			eeprom_wb(EEPROM_ORDER + i * sizeof(LetterTable[0]) + 2, 
													LetterTable[i].letter);
		}
		conv.value = FirstFreeCluster;
		eeprom_ww(EEPROM_FREECLUST1, conv.words.low);
		eeprom_ww(EEPROM_FREECLUST2, conv.words.high);
	}
	else				// read saved letter table back
	{
		nLastPos = eeprom_rb(EEPROM_MAXLETTERS);
		if (nLastPos > MAX_LETTERS)
			nLastPos = MAX_LETTERS;
		for (i = 0; i < nLastPos; i++)
		{
			LetterTable[i].song_num = eeprom_rw(EEPROM_ORDER +  
												i * sizeof(LetterTable[0]));
			LetterTable[i].letter = eeprom_rb(EEPROM_ORDER + 
												i * sizeof(LetterTable[0]) + 2);
		}
#endif
	}
	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)
{
	*DIR_NAME = 0;
	wCurEntry = 0;
	SetStartCluster(FirstDirCuster);
	struct direntry *de = get_de(entry);

	if (de)
	{
		dwFileSize = de->deFileSize;
		union u32convert Data;
		Data.words.high = de->deHighClust;
		Data.words.low = de->deStartCluster;
		SetStartCluster(Data.value);
		return true;
	}
	return false;
}


// This function initializes variables to read new file (dir)
void SetStartCluster(u32 dwStartCluster)
{
	dwCurrentCluster = dwStartCluster;
	dwStartSector  = clust2sect(dwStartCluster);
	nSectorsReaded = 0;
}


// 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 > FAT32_MASK)
		return NO_DATA;
	return ReadNextCluster();
}


// This function reads next cluster in chain defined by dwCurrentCluster
// 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
u08 ReadNextCluster(void)
{
static u32 dwFatInCacheSector = 0;
	u08 nRes = DATA_EXIST;
	u32 dwFatSector = FirstFATSector + dwCurrentCluster / (BPS / 4);
//	u08 nFatOffset  = ((u08)dwCurrentCluster) % (BPS / 4);
	u08 nFatOffset  = ((u08)dwCurrentCluster) & (BPS / 4 - 1);
	if (dwFatSector != dwFatInCacheSector)
	{
	    while (ATA_ReadLBA0(dwFatSector, 1, FAT_CACHE))
	    	ATA_SW_Reset();
		dwFatInCacheSector = dwFatSector;
		nRes = FAT_READ;
	}
	nSectorsReaded = 0;
	dwCurrentCluster =					// Next cluster item in a chain
				*((u32*)FAT_CACHE + nFatOffset);
	dwStartSector = clust2sect(dwCurrentCluster);
	return (dwCurrentCluster <= FAT32_MASK) ? nRes : NO_DATA;
}


// This function reads BUFFER_SIZE of data from current cluster's position to 
//  the one of the buffers BUF1 or BUF2
void ReadBuffer(u08 nBuf)
{
	register u08* pBuf;
	if (nBuf == BUF1)
	{
		pBuf = BUFFER1P;
		bBuf1Empty = false;
	}
	else
	{
		pBuf = BUFFER2P;
		bBuf2Empty = false;
	}
	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("Err chk ");
		UART_Printfu16(crc);
		UART_Printfu16(calc_crc);
	}
	return crc == calc_crc;
}

#endif // ENABLE_CRC


#ifdef ENABLE_NAV

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[nLastPos].song_num = wCurEntry;
		LetterTable[nLastPos++].letter = sym;
	}
}

#endif

⌨️ 快捷键说明

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