📄 fat.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 + -