📄 ffs.c
字号:
/*
** PHILIPS ARM 2005 DESIGN CONTEST
** ENTRY AR1757
** FLASH CARD AUDIO PLAYER FOR HEAD END UNIT
**
** FFS.C: Stripped down and optimised FAT16 file system routines
**
// Based on the software from:
//
// Apple II 16-bit IDE/ATA interface software
// (c) 2001 stephane.guillard@steria.com
// Hardware and software design available at
// http:// s.guillard.free.fr
*/
//
// Conditional compilation directives
//
//
//#define CCD_DEBUG // Enable verbose behaviour
//#undef CCD_DEBUG
//#define CCD_STRICTCHECK // Enforce full checkings of IDE ide_status etc.
//
//
// ****************** I N C L U D E S ******************
//
//
#ifndef SIMULATION
#ifdef CCD_DEBUG
#define DEBUG
#endif
#include "FFs.h"
#include "mmc.h"
#include "types.h"
#include <LPC213x.H>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
static u8 sector[512];
//
// big-little endian swappers
//
inline u16 swap16(u16 x)
{
u8 *p=(u8 *)&x;
u8 t;
t=p[0];
p[0]=p[1];
p[1]=t;
return x;
}
inline u32 swap32(u32 x)
{
u8 *p=(u8 *)&x;
u8 t;
t=p[0];
p[0]=p[3];
p[3]=t;
t=p[1];
p[1]=p[2];
p[2]=t;
return x;
}
//
//
// ****************** C O N S T A N T S ******************
//
//
// General
#define FILE_USED FALSE
#define FILE_FREE TRUE
// IDE block size
#define BLOCKSIZE 512
// Max count of file handles
#define MAX_FILES 2
// End of cluster chain
#define CLUSTCHAIN_END ((u16) 0xFFF8)
//
//
// ****************** M A C R O S ******************
//
//
#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))
//
//
// ****************** G L O B A L S ******************
//
//
// Drive geometry
static u32 hd1_start_lba; // first lba of partiton
static u8 hd1_geom_secperclust; // sectors per cluster
static u16 hd1_geom_rd_size; // root directory size in sectors
static u32 hd1_geom_offfat; // 1st sector after 1st FAT
static u32 hd1_geom_clustsize; // u8s per cluster
// Main FS struct properties
static u32
lba_fat, // FAT 1st sector
lba_rd, // Root directory 1st sector
lba_data, // Partition data area 1st sector
lba_curdir, // Current directory 1st sector (initialized by dir_examine())
lba_tmpdir; // Current sector in current directory (used by dir_exnext())
static dirent *pde_cur, de_cur; // de_cur is a copy of the current dirent. pde_cur points at the original in secbuf
// Global file handle table
static file_handle __files[MAX_FILES];
// Sector cache
static u32 lba_incache = -1; // valid LBA starts at 1 so 0 is invalid cache
//
//
// ****************** E X P O R T E D P R O T O S ******************
//
//
//
//
// ****************** M I S C U T I L I T Y F U N C S ******************
//
//
static u16 lba2clust(u32 lba) // lba2clust() : find cluster number from LBA sector address
{
if (lba < lba_data) return 0; // 0 is error code since clust < 2 is error
return 2 + (u16) ((lba - lba_data) / hd1_geom_secperclust);
}
static u32 clust2lba(u16 clust) // clust2lba() : find LBA address of cluster's 1st sector
{
if (clust < 2) return 0; // 0 is error code since LBA < 1 is error
return lba_data + (u32) (clust - 2) * (u32) hd1_geom_secperclust;
}
//
//
// ****************** LV1 - S E C T O R I N T E R F A C E F U N C S ******************
//
//
#define secbuf(index) sector[(index)]
u16 inline peekw(u16 index)
{
u16 t;
t=sector[index+1];
t<<=8;
t|=sector[index];
return t;
}
inline u32 peekl(u16 index)
{
u8 *p;
u32 t;
p=sector+index;
t=p[3];
t<<=8;
t|=p[2];
t<<=8;
t|=p[1];
t<<=8;
t|=p[0];
return t;
}
// sec_read() : read a sector pointed by lba into secbuf
static void sec_read(u32 lba)
{
lba_incache = lba;
#ifdef CCD_DEBUG
mprintf("Read sector: %u\n\r",lba);
#endif
mmc_SectorRead(sector,lba);
}
#ifdef CCD_DEBUG
static void sec_dump(void) // Dump sector buffer on screen as HEX
{
u16 x;
for(x=0;x<512;x+=16)
{
mprintf("%04x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n\r",x,
secbuf(x),secbuf(x+1),secbuf(x+2),secbuf(x+3),secbuf(x+4),secbuf(x+5),secbuf(x+6), secbuf(x+7),
secbuf(x+8),secbuf(x+9),secbuf(x+10),secbuf(x+11),secbuf(x+12),secbuf(x+13),secbuf(x+14), secbuf(x+15));
}
}
static void dbg_dumpprm(void)
{
mprintf("Start LBA %u\n\r",hd1_start_lba);
}
#endif
static void sec_get(u32 *sa) // Read sector into sector buffer. CHS is recalculated from LBA.
{
// Simple cache mechanism : don't read an already present sector into buffer
if (lba_incache == *sa)
{
return;
}
// 2 - Read sector
sec_read(*sa);
}
//
//
// ****************** LV2 - H A R D D I S K I N T E R F A C E F U N C S ******************
//
//
bool hd_qry(void) // hd_qry() : inquiry disk properties and display them
{
u8 rc;
#ifdef CCD_DEBUG
mprintf("Query disk info\n\r");
#endif
rc=mmc_Initialise();
if(rc==TRUE)
{
return TRUE;
}
else
{
#ifdef CCD_DEBUG
mprintf("MMC not responding?\n\r");
#endif
return FALSE;
}
}
bool hd_mbr(void) // hd_mbr() : read disk Master Boot Record, check it and analyze partition entries
{
#ifdef CCD_DEBUG
u8 pnum;
u16 p_offset;
#else
#define p_offset 446
#endif
// Analyze MBR and find 1st partition boot sector (leave CHS of this BPB in IDE parameters for further read)
#ifdef CCD_DEBUG
mprintf("Chk MBR\n\r");
#endif
sec_read(0); // Read MBR sector
// Check signature MBR : offset 1FE=0x55 1FF=AA
if ((secbuf(0x1FE) != 0x55) || (secbuf(0x1FF) != 0xAA))
{
#ifdef CCD_DEBUG
mprintf("No\n\r");
#endif
return FALSE;
}
#ifdef CCD_DEBUG
for (pnum = 0 ; pnum < 4 ; ++pnum) // Scan the 4 partition entries of MBR (starts at secbuf + 446)
{
p_offset = 446 + 16 * pnum; // Offset of partition #pnum struct in MBR
// Display current part #, and skip if not defined (type == 0)
mprintf("Partition #%d ",pnum);
#endif
if (secbuf(p_offset + 4)) // If partition defined (type != 0)..
{
#ifdef CCD_DEBUG
// Display type
mprintf("type %d ", secbuf(p_offset + 4));
#endif
// Extract start LBA
hd1_start_lba = peekl(p_offset + 8);
#ifdef CCD_DEBUG
// Display active Y/N
mprintf("Active: %s",secbuf(p_offset) == 0x80 ? "Yes " : "No ");
// Output start CHS (note that start CHS is the boot sector of the partition)
mprintf("LBA %u\n\r",hd1_start_lba);
#endif
}
#ifdef CCD_DEBUG
else
mprintf("None\n\r");
}
p_offset = 446;
// extract start LBA
hd1_start_lba = peekl(p_offset + 8);
#endif
return (!(secbuf(446 /*p_offset*/ + 4) != 6 && secbuf(446 /*p_offset*/ + 4) != 4)); // Only return 0 if 1st partition type is FAT16
}
bool hd_bpb(void) // Analyze partition Boot Param Block to find out addresses of FAT and rootdir
{
u8 i;
sec_read(hd1_start_lba );
hd1_geom_rd_size = peekw(17) / 16;/* * 32 / BLOCKSIZE */ // Get root dir size
hd1_geom_secperclust = secbuf(13); // Get sector per cluster count
// Check if BPB looks OK
if (
#ifdef CCD_STRICTCHECK
(secbuf( 3) != 'M') // standard FAT BPB signature == "MSWIN4.1"
|| (secbuf( 4) != 'S')
|| (secbuf( 9) != '.')
|| (peekw( 11) != BLOCKSIZE) // MUST have BLOCKSIZE u8 sectors
#else
(secbuf(510) != 0x55)
|| (secbuf(511) != 0xaa)
|| (!(peekw( 14))) // Reserved MUST be >0
|| (secbuf( 21) != 0xF8) // Media MUST be 0xF8
|| (peekw( 11) != BLOCKSIZE) // MUST have BLOCKSIZE u8 sectors*/
#endif
)
{
#ifdef CCD_DEBUG
mprintf("Boot block not found\n\r");
#endif
return FALSE;
}
// Calculate LBA of FAT : reserved + hidden
// LBA of BPB is calculated from current values of IDE parameters (BPB is the last sector read in secbuf)
lba_fat = (u32) peekw( 14)
+ peekl( 28);
// Remember LBA of 1st sector after 1st FAT
hd1_geom_offfat = lba_fat + (u32) peekw( 22);
// Calculate LBA of root dir : FAT + numFAT * fatsize
// Store root dir into current dir
lba_curdir = lba_rd = lba_fat + (u32) secbuf(16) * (u32) peekw( 22) ;
// Calculate LBA of data area : after root dir
lba_data = lba_rd + (u32) hd1_geom_rd_size;
// Calculate cluster size in u8s (to speedup later calculation)
hd1_geom_clustsize = (u32) BLOCKSIZE * (u32) hd1_geom_secperclust;
#ifdef CCD_DEBUG
mprintf("Fat LBA =%u\n\r",lba_fat);
mprintf("Root LBA =%u\n\r",lba_rd);
mprintf("Data LBA =%u\n\r",lba_data);
mprintf("LFat LBA =%u\n\r",hd1_geom_offfat);
mprintf("Clustsize =%u\n\r",hd1_geom_clustsize);
#endif
// clear out file handles
for(i=0;i<MAX_FILES;i++)
__files[i].inuse=0;
return TRUE;
}
//
//
// ****************** LV3 - C L U S T E R I N T E R F A C E F U N C S ******************
//
//
static bool clust_getfat(u16 clust) // Read into secbuf the FAT sector containing clust.
// Return TRUE if Ok, FALSE if error
{
u32 sa;
// Find out FAT sector number containing cluster#
// If we are out of 1st FAT then clust is invalid
if ((sa = lba_fat + ((u32) (clust >> 8) /* * (u32) 2 / (u32) BLOCKSIZE*/)) // a cluster # is 2 u8s, a sector is BLOCKSIZE u8s
>= hd1_geom_offfat) return FALSE;
// Read in FAT sector
sec_get(&sa);
return TRUE;
}
static u16 clust_next(u16 clust) // Find next cluster in chain. Return CLUSTCHAIN_END if no next,
// and 0 if error
{
u16 next;
// Read in FAT sector containing clust
if (!(clust_getfat(clust))) return 0;
// Find next cluster entry from FAT
if ((next = peekw( (clust % (BLOCKSIZE / 2)) * 2)) >= CLUSTCHAIN_END) next = CLUSTCHAIN_END; // Above, CLUSTCHAIN_END (End Of cluster Chain)
return next;
}
static u32 clust_nextlba(u32 lba) // Find next sector in same cluster chain
{
u32 next = lba + 1;
u16 c1, c2;
// Calculate lba and next clusters
c1 = lba2clust(lba);
c2 = lba2clust(next);
// If sectors are in the same cluster, simply return next
if (c1 == c2) return next;
// If not, fetch next cluster in cluster chain and return
// 1st sector of this new cluster
if ((c2 = clust_next(c1)) < CLUSTCHAIN_END) return clust2lba(c2);
// Did not find next cluster
return 0;
}
//
//
// ****************** LV4 - F I L E T R E E I N T E R F A C E F U N C S ******************
//
//
// From Microsoft Extensible Firmware Initiative FAT32 File System Specification :
// Special notes about the first u8 (Name[0]) of a FAT directory entry :
// If Name[0] == 0xE5, then the directory entry is free (there is no file or directory name in this entry).
// If Name[0] == 0x00, then the directory entry is free (same as for 0xE5), and there are no allocated directory entries after this one (all of the Name[0] u8s in all of the entries after this one are also set to 0).
#define DE_FREE ((u8) 0xE5)
#define DE_FREE_LAST ((u8) 0x00)
#define DE_NONE ((u8) 0xFF)
bool dir_next(bool bfree) // dir_next() : return next dirent
// If bfree is FALSE then we look for an unused dirent.
// If bfree is TRUE then we look for an used dirent.
{
u8 c;
// Read current directory sector into buffer
sec_get(&lba_tmpdir);
do
{
// Go to next dirent
++pde_cur;
// Put 1st char of current dirent filename into c;
c = pde_cur -> Name[0];
// If we go outside secbuf, go to next sector of directory (if any)
if (((u32) pde_cur - (u32) sector) >= BLOCKSIZE)
{
c = (bfree ? 1 : DE_FREE); // Prepare to loop in there
if (lba_curdir == lba_rd) // We're in root dir so we merely go to next sector checking we're below hd1_geom_rd_size
{
if (lba_tmpdir - lba_curdir < hd1_geom_rd_size)
lba_tmpdir++;
else
c = (bfree ? DE_NONE : DE_FREE_LAST);
}
else // We're not in root dir so find next sector of current dir in its cluster chain
{
if (!(lba_tmpdir = clust_nextlba(lba_tmpdir)))
c = (bfree ? DE_NONE : DE_FREE_LAST);
}
if (c != (bfree ? DE_NONE : 0)) // We are on a new dir sector, read it in and prepare next scan
{
sec_get(&lba_tmpdir);
pde_cur = (dirent *) sector;
--pde_cur;
}
}
}
while ((bfree ? // skip (un)used entries
/* unused */ ((c != DE_FREE_LAST) && (c != DE_FREE) && (c != DE_NONE))
/* used */ : (c == DE_FREE)
));
// If we are on a (un)used entry then return OK
if ((bfree ? (c != DE_NONE) : (c != DE_FREE_LAST)))
{
memcpy(&de_cur,pde_cur,sizeof(de_cur));
return TRUE;
}
return FALSE;
}
bool dir_examine(bool bfree) // dir_examine() : start scan of current dir and return first (un)used dirent
{
// Set current directory sector to 1st sector of current dir
lba_tmpdir = lba_curdir;
// "Rewind back" so that dir_next starts scan at 1st dirent
pde_cur = (dirent *) sector;
--pde_cur;
// Find next dirent
return dir_next(bfree);
}
//
// Convert filename to padded 8.3 format
//
static char * __create_83_name(char *filename)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -