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