diff options
Diffstat (limited to 'cfe/cfe/main/cfe_fatfs.c')
-rw-r--r-- | cfe/cfe/main/cfe_fatfs.c | 1983 |
1 files changed, 1983 insertions, 0 deletions
diff --git a/cfe/cfe/main/cfe_fatfs.c b/cfe/cfe/main/cfe_fatfs.c new file mode 100644 index 0000000..f2fa49f --- /dev/null +++ b/cfe/cfe/main/cfe_fatfs.c @@ -0,0 +1,1983 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * FAT file system File: cfe_fatfs.c + * + * This module knows how to read files from a FAT formatted + * file system (12 or 16 bit fats only for now) + * + * Eventually, we'll also include support for the FAT Translation + * Layer (FTL) on PCMCIA flash file systems. + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +#include "lib_types.h" +#include "lib_string.h" +#include "lib_queue.h" +#include "lib_malloc.h" +#include "lib_printf.h" + +#include "cfe_error.h" +#include "cfe_fileops.h" +#include "cfe_iocb.h" +#include "cfe_devfuncs.h" + +#include "cfe_loader.h" + +#include "cfe.h" + + +/* ********************************************************************* + * Constants + ********************************************************************* */ + +#define SECTORSIZE 512 +#define DIRENTRYSIZE 32 +#define DIRPERSECTOR (SECTORSIZE/DIRENTRYSIZE) + +/*#define _FATFS_DEBUG_*/ + +/* + * Bios Parameter Block offsets and values + */ + +#define BPB_JMPINSTR 0x00 +#define BPB_JMPINSTR_VALUE 0xEB +#define BPB_JMPINSTR_VALUE2 0xE9 +#define BPB_SEAL 0x1FE +#define BPB_SEAL_VALUE 0xAA55 + +#define BPB_BYTESPERSECTOR 0x0B +#define BPB_SECTORSPERCLUSTER 0x0D +#define BPB_RESERVEDSECTORS 0x0E +#define BPB_NUMFATS 0x10 +#define BPB_MAXROOTDIR 0x11 +#define BPB_TOTALSECTORS 0x13 +#define BPB_SECTORSPERFAT 0x16 +#define BPB_SECTORSPERTRACK 0x18 +#define BPB_NUMHEADS 0x1A +#define BPB_HIDDENSECTORS 0x1C +#define BPB_SYSTEMID 54 +#define BPB_MEDIADESCRIPTOR 21 +#define BPB_SIGNATURE 38 +#define BPB_SIGNATURE_VALUE1 0x28 +#define BPB_SIGNATURE_VALUE2 0x29 + +/* + * Partition types + */ + +#define PARTTYPE_EMPTY 0 +#define PARTTYPE_FAT12 1 +#define PARTTYPE_FAT16 4 +#define PARTTYPE_FAT16BIG 6 +#define PARTTYPE_FAT32 0x0B + +/* + * Partition table offsets + */ +#define PTABLE_STATUS 0 +#define PTABLE_STARTHEAD 1 +#define PTABLE_STARTSECCYL 2 /* 2 bytes */ +#define PTABLE_TYPE 4 +#define PTABLE_ENDHEAD 5 +#define PTABLE_ENDSECCYL 6 /* 2 bytes */ +#define PTABLE_BOOTSECTOR 8 /* 4 bytes */ +#define PTABLE_NUMSECTORS 12 /* 4 bytes */ + +#define PTABLE_SIZE 16 +#define PTABLE_COUNT 4 +#define PTABLE_OFFSET (512-2-(PTABLE_COUNT*PTABLE_SIZE)) + +#define PTABLE_STATUS_ACTIVE 0x80 + +/* + * Directory attributes + */ + +#define ATTRIB_NORMAL 0x00 +#define ATTRIB_READONLY 0x01 +#define ATTRIB_HIDDEN 0x02 +#define ATTRIB_SYSTEM 0x04 +#define ATTRIB_LABEL 0x08 +#define ATTRIB_DIR 0x10 +#define ATTRIB_ARCHIVE 0x20 + +#define ATTRIB_LFN 0x0F + +/* + * Macros to read fields in directory & BPB entries + */ + +#define READWORD(buffer,x) (((unsigned int) (buffer)[(x)]) | \ + (((unsigned int) (buffer)[(x)+1]) << 8)) + +#define READWORD32(buffer,x) (READWORD(buffer,(x)) | (READWORD(buffer,(x)+2) << 16)) + +#define READBYTE(buffer,x) ((unsigned int) (buffer)[(x)]) + +/* + * Directory entry offsets and values + */ + +#define DIR_CHECKSUM 13 +#define DIR_FILELENGTH 28 +#define DIR_STARTCLUSTER 26 +#define DIR_ATTRIB 11 +#define DIR_NAMEOFFSET 0 +#define DIR_NAMELEN 8 +#define DIR_EXTOFFSET 8 +#define DIR_EXTLEN 3 + +#define DIRENTRY_CHECKSUM(e) READBYTE(e,DIR_CHECKSUM) +#define DIRENTRY_FILELENGTH(e) READWORD32(e,DIR_FILELENGTH) +#define DIRENTRY_STARTCLUSTER(e) READWORD(e,DIR_STARTCLUSTER) +#define DIRENTRY_ATTRIB(e) READBYTE(e,DIR_ATTRIB) + +#define DIRENTRY_LAST 0 +#define DIRENTRY_DELETED 0xE5 +#define DIRENTRY_PARENTDIR 0x2E + +#define DIRENTRY_LFNIDX(e) READBYTE(e,0) +#define LFNIDX_MASK 0x1F +#define LFNIDX_END 0x40 +#define LFNIDX_MAX 20 + +/* ********************************************************************* + * Types + ********************************************************************* */ + +/* + * Internalized BPB + */ + +typedef struct bpb_s { + unsigned int bpb_bytespersector; + unsigned int bpb_sectorspercluster; + unsigned int bpb_reservedsectors; + unsigned int bpb_numfats; + unsigned int bpb_maxrootdir; + unsigned int bpb_totalsectors; + unsigned int bpb_sectorsperfat; + unsigned int bpb_sectorspertrack; + unsigned int bpb_numheads; + unsigned int bpb_hiddensectors; +} bpb_t; + +/* + * FAT Filesystem descriptor - contains working information + * about an "open" file system + */ + +typedef struct fatfs_s { + int fat_fh; + int fat_refcnt; + bpb_t fat_bpb; + int fat_twelvebit; + int fat_partstart; + uint8_t fat_dirsector[SECTORSIZE]; + int fat_dirsecnum; + uint8_t fat_fatsector[SECTORSIZE]; + int fat_fatsecnum; +} fatfs_t; + +/* + * FAT Chain - describes a series of FAT entries + */ + +typedef struct fatchain_s { + int fat_start; + uint16_t *fat_entries; + int fat_count; +} fatchain_t; + +/* + * FAT File descriptor - contains working information + * about an open file (including the filesystem info) + */ + +typedef struct fatfile_s { + fatfs_t *ff_fat; + int ff_filelength; + fatchain_t ff_chain; + int ff_curpos; + int ff_cursector; + uint8_t ff_sector[SECTORSIZE]; +} fatfile_t; + +/* ********************************************************************* + * Prototypes + ********************************************************************* */ + +static int fatfs_fileop_xinit(void **fsctx,void *filename); +static int fatfs_fileop_pinit(void **fsctx,void *filename); +static int fatfs_fileop_open(void **ref,void *fsctx,char *filename,int mode); +static int fatfs_fileop_read(void *ref,uint8_t *buf,int len); +static int fatfs_fileop_write(void *ref,uint8_t *buf,int len); +static int fatfs_fileop_seek(void *ref,int offset,int how); +static void fatfs_fileop_close(void *ref); +static void fatfs_fileop_uninit(void *fsctx); + +static int fatfs_check_for_partition_table(fatfs_t *fatfs); + +/* ********************************************************************* + * FAT fileio dispatch table + ********************************************************************* */ + +/* + * Raw FAT (no partition table) - used only on floppies + */ + +const fileio_dispatch_t fatfs_fileops = { + "rfat", + LOADFLG_NOBB, + fatfs_fileop_xinit, + fatfs_fileop_open, + fatfs_fileop_read, + fatfs_fileop_write, + fatfs_fileop_seek, + fatfs_fileop_close, + fatfs_fileop_uninit +}; + +/* + * Partitioned FAT - used on Zip disks, removable hard disks, + * hard disks, flash cards, etc. + */ + +const fileio_dispatch_t pfatfs_fileops = { + "fat", + LOADFLG_NOBB, + fatfs_fileop_pinit, + fatfs_fileop_open, + fatfs_fileop_read, + fatfs_fileop_write, + fatfs_fileop_seek, + fatfs_fileop_close, + fatfs_fileop_uninit +}; + + +/* ********************************************************************* + * fat_readsector(fatfs,sector,numsec,buffer) + * + * Read one or more sectors from the disk into memory + * + * Input parameters: + * fatfs - fat filesystem descriptor + * sector - sector number + * numsec - number of sectors to read + * buffer - buffer to read sectors into + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int fat_readsector(fatfs_t *fatfs,int sector,int numsec,uint8_t *buffer) +{ + int res; + + res = cfe_readblk(fatfs->fat_fh,(sector+fatfs->fat_partstart)*SECTORSIZE, + buffer,numsec*SECTORSIZE); + + if (res != numsec*SECTORSIZE) return CFE_ERR_IOERR; + + return 0; +} + + +/* ********************************************************************* + * fat_dumpbpb(bpb) + * + * Debug function; display fields in a BPB + * + * Input parameters: + * bpb - BIOS parameter block structure + * + * Return value: + * nothing + ********************************************************************* */ + +#ifdef _FATFS_DEBUG_ +static void fat_dumpbpb(bpb_t *bpb) +{ + xprintf("Bytes per sector %d\n",bpb->bpb_bytespersector); + xprintf("Sectors per cluster %d\n",bpb->bpb_sectorspercluster); + xprintf("Reserved sectors %d\n",bpb->bpb_reservedsectors); + xprintf("Number of FATs %d\n",bpb->bpb_numfats); + xprintf("Root dir entries %d\n",bpb->bpb_maxrootdir); + xprintf("Total sectors %d\n",bpb->bpb_totalsectors); + xprintf("Sectors per FAT %d\n",bpb->bpb_sectorsperfat); + xprintf("Sectors per track %d\n",bpb->bpb_sectorspertrack); + xprintf("Number of heads %d\n",bpb->bpb_numheads); + xprintf("Hidden sectors %d\n",bpb->bpb_hiddensectors); +} +#endif + +/* ********************************************************************* + * fat_findpart(fatfs) + * + * For partitioned disks, locate the active partition + * and set "fat_partstart" accordingly + * + * Input parameters: + * fatfs - FAT filesystem descriptor + * + * Return value: + * 0 if we found a valid partition table; else error code + ********************************************************************* */ + +static int fat_findpart(fatfs_t *fatfs) +{ + uint8_t buffer[SECTORSIZE]; + uint8_t *part; + int res; + int idx; + + fatfs->fat_partstart = 0; /* make sure we get real boot sector */ + res = fat_readsector(fatfs,0,1,buffer); + if (res < 0) return res; + + /* + * Normally you're supposed to check for a JMP instruction. + * At least that's what many people do. Flash MBRs don't start + * with JMP instructions, so just look for the seal. + * + * + * if (READBYTE(buffer,BPB_JMPINSTR) != BPB_JMPINSTR_VALUE) { + * return CFE_ERR_BADFILESYS; + * } + */ + + /* + * Check the seal at the end of th sector + */ + + if (READWORD(buffer,BPB_SEAL) != BPB_SEAL_VALUE) return CFE_ERR_BADFILESYS; + + /* + * Look for an active FAT partition. The partition we want must + * be the active one. We do not deal with extended partitions + * here. Hey, this is supposed to be boot code! + */ + + part = &buffer[PTABLE_OFFSET]; + for (idx = 0; idx < PTABLE_COUNT; idx++) { + if ((part[PTABLE_STATUS] == PTABLE_STATUS_ACTIVE) && + ((part[PTABLE_TYPE] == PARTTYPE_FAT12) || + (part[PTABLE_TYPE] == PARTTYPE_FAT16) || + (part[PTABLE_TYPE] == PARTTYPE_FAT16BIG))) { + break; + } + + part += PTABLE_SIZE; + } + + if (idx == PTABLE_COUNT) return CFE_ERR_BADFILESYS; + + /* + * The info we want is really just the pointer to the + * boot (BPB) sector. Get that and we'll use it for an + * offset into the disk later. + */ + + fatfs->fat_partstart = READWORD32(part,PTABLE_BOOTSECTOR); + + return 0; +} + + +/* ********************************************************************* + * fat_readbpb(fatfs) + * + * Read and internalize the BIOS Parameter Block + * + * Input parameters: + * fatfs - FAT filesystem descriptor + * + * Return value: + * 0 if ok + * else error code (usually, BPB is not valid) + ********************************************************************* */ + +static int fat_readbpb(fatfs_t *fatfs) +{ + uint8_t buffer[SECTORSIZE]; + int res; + + res = fat_readsector(fatfs,0,1,buffer); + if (res < 0) return res; + + if (READBYTE(buffer,BPB_JMPINSTR) != BPB_JMPINSTR_VALUE) return CFE_ERR_BADFILESYS; + if (READWORD(buffer,BPB_SEAL) != BPB_SEAL_VALUE) return CFE_ERR_BADFILESYS; + + fatfs->fat_bpb.bpb_bytespersector = READWORD(buffer,BPB_BYTESPERSECTOR); + fatfs->fat_bpb.bpb_sectorspercluster = READBYTE(buffer,BPB_SECTORSPERCLUSTER); + fatfs->fat_bpb.bpb_reservedsectors = READWORD(buffer,BPB_RESERVEDSECTORS); + fatfs->fat_bpb.bpb_numfats = READBYTE(buffer,BPB_NUMFATS); + fatfs->fat_bpb.bpb_maxrootdir = READWORD(buffer,BPB_MAXROOTDIR); + fatfs->fat_bpb.bpb_totalsectors = READWORD(buffer,BPB_TOTALSECTORS); + fatfs->fat_bpb.bpb_sectorsperfat = READWORD(buffer,BPB_SECTORSPERFAT); + fatfs->fat_bpb.bpb_sectorspertrack = READWORD(buffer,BPB_SECTORSPERTRACK); + fatfs->fat_bpb.bpb_numheads = READWORD(buffer,BPB_NUMHEADS); + fatfs->fat_bpb.bpb_hiddensectors = READWORD(buffer,BPB_HIDDENSECTORS); + + fatfs->fat_twelvebit = 1; + if (memcmp(&buffer[0x36],"FAT16 ",8) == 0) fatfs->fat_twelvebit = 0; + + if (fatfs->fat_bpb.bpb_bytespersector != SECTORSIZE) return CFE_ERR_BADFILESYS; + if (fatfs->fat_bpb.bpb_numfats > 2) return CFE_ERR_BADFILESYS; + + /* + * XXX sanity check other fields + */ + +#ifdef _FATFS_DEBUG_ + fat_dumpbpb(&(fatfs->fat_bpb)); +#endif + + return 0; +} + + + +/* ********************************************************************* + * fat_getentry(fatfs,entry) + * + * Read a FAT entry. This is more involved than you'd think, + * since we have to deal with 12 and 16 (and someday 32) bit FATs, + * and the nasty case where a 12-bit FAT entry crosses a sector + * boundary. + * + * Input parameters: + * fatfs - FAT filesystem descriptor + * entry - index of FAT entry + * + * Return value: + * FAT entry, or <0 if an error occured + ********************************************************************* */ + +static int fat_getfatentry(fatfs_t *fatfs,int entry) +{ + int fatsect; + int byteoffset; + int fatstart; + int fatoffset; + uint8_t b1,b2,b3; + int res; + + fatstart = fatfs->fat_bpb.bpb_reservedsectors; + + if (fatfs->fat_twelvebit) { + int odd; + odd = entry & 1; + byteoffset = ((entry & ~1) * 3) / 2; + fatsect = byteoffset / SECTORSIZE; + fatoffset = byteoffset % SECTORSIZE; + + if (fatfs->fat_fatsecnum != fatsect) { + res = fat_readsector(fatfs,fatsect+fatstart,1,fatfs->fat_fatsector); + if (res < 0) { + return res; + } + fatfs->fat_fatsecnum = fatsect; + } + + b1 = fatfs->fat_fatsector[fatoffset]; + + if ((fatoffset+1) >= SECTORSIZE) { + res = fat_readsector(fatfs,fatsect+1+fatstart,1,fatfs->fat_fatsector); + if (res < 0) { + return res; + } + fatfs->fat_fatsecnum = fatsect+1; + fatoffset -= SECTORSIZE; + } + + b2 = fatfs->fat_fatsector[fatoffset+1]; + + if ((fatoffset+2) >= SECTORSIZE) { + res = fat_readsector(fatfs,fatsect+1+fatstart,1,fatfs->fat_fatsector); + if (res < 0) { + return res; + } + fatfs->fat_fatsecnum = fatsect+1; + fatoffset -= SECTORSIZE; + } + + b3 = fatfs->fat_fatsector[fatoffset+2]; + + if (odd) { + return ((unsigned int) b3 << 4) + ((unsigned int) (b2 & 0xF0) >> 4); + } + else { + return ((unsigned int) (b2 & 0x0F) << 8) + ((unsigned int) b1); + } + + } + else { + byteoffset = entry * 2; + fatsect = byteoffset / SECTORSIZE; + fatoffset = byteoffset % SECTORSIZE; + + if (fatfs->fat_fatsecnum != fatsect) { + res = fat_readsector(fatfs,fatsect+fatstart,1,fatfs->fat_fatsector); + if (res < 0) { + return res; + } + fatfs->fat_fatsecnum = fatsect; + } + + b1 = fatfs->fat_fatsector[fatoffset]; + b2 = fatfs->fat_fatsector[fatoffset+1]; + return ((unsigned int) b1) + (((unsigned int) b2) << 8); + } +} + +/* ********************************************************************* + * fat_getrootdirentry(fatfs,entryidx,entry) + * + * Read a root directory entry. The FAT12/16 root directory + * is a contiguous group of sectors, whose size is specified in + * the BPB. This routine just digs out an entry from there + * + * Input parameters: + * fatfs - FAT filesystem descriptor + * entryidx - 0-based entry index to read + * entry - pointer to directory entry (32 bytes) + * + * Return value: + * 0 if ok + * <0 if error occured + ********************************************************************* */ + +static int fat_getrootdirentry(fatfs_t *fatfs,int entryidx,uint8_t *entry) +{ + int rootdirstart; + int rootdirsize; + int dirsecnum; + int res; + + if (entryidx >= fatfs->fat_bpb.bpb_maxrootdir) { + memset(entry,0,DIRENTRYSIZE); + return CFE_ERR_INV_PARAM; + } + + rootdirstart = fatfs->fat_bpb.bpb_reservedsectors + + fatfs->fat_bpb.bpb_numfats * fatfs->fat_bpb.bpb_sectorsperfat; + + rootdirsize = fatfs->fat_bpb.bpb_maxrootdir / DIRENTRYSIZE; + + dirsecnum = rootdirstart + entryidx / DIRPERSECTOR; + + if (fatfs->fat_dirsecnum != dirsecnum) { + res = fat_readsector(fatfs,dirsecnum,1,fatfs->fat_dirsector); + if (res < 0) { + return res; + } + fatfs->fat_dirsecnum = dirsecnum; + } + + memcpy(entry,&(fatfs->fat_dirsector[(entryidx % DIRPERSECTOR)*DIRENTRYSIZE]), + DIRENTRYSIZE); + + return 0; +} + +/* ********************************************************************* + * fat_checksumname(name) + * + * Calculate the "long filename" checksum for a given short name. + * All LFN directory entries associated with the short name are + * given the same checksum byte, to help keep the long name + * consistent. + * + * Input parameters: + * name - pointer to 32-byte directory entry + * + * Return value: + * checksum value + ********************************************************************* */ + +static uint8_t fat_checksumname(uint8_t *name) +{ + uint8_t sum = 0; + uint8_t newbit; + int idx; + + for (idx = 0; idx < 11; idx++) { + newbit = (sum & 1) ? 0x80 : 0x00; + sum >>= 1; + sum |= newbit; + sum += name[idx]; + } + + return sum; +} + +#ifdef _FATFS_DEBUG_ +void fat_dumpdirentry(uint8_t *entry); +void fat_dumpdirentry(uint8_t *entry) +{ + uint8_t name[32]; + int idx; + + if (entry[11] != ATTRIB_LFN) { + memcpy(name,entry,11); + name[11] = 0; + xprintf("%s %02X %04X %d\n", + name,DIRENTRY_ATTRIB(entry), + DIRENTRY_STARTCLUSTER(entry), + DIRENTRY_FILELENGTH(entry)); + } + else { + for (idx = 0; idx < 5; idx++) { + name[idx] = entry[(idx*2)+1]; + } + for (idx = 0; idx < 6; idx++) { + name[idx+5] = entry[(idx*2)+14]; + } + for (idx = 0; idx < 2; idx++) { + name[idx+11] = entry[(idx*2)+28]; + } + name[13] = '\0'; + xprintf("%02X: %s %04X cksum %02X\n",entry[0], + name,READWORD(entry,0x1A),entry[13]); + } +} +#endif + + +/* ********************************************************************* + * fat_walkfatchain(fat,start,arg,func) + * + * Walk a FAT chain, calling a callback routine for each entry + * we find along the way. + * + * Input parameters: + * fat - FAT filesystem descriptor + * start - starting FAT entry (from the directory, usually) + * arg - argument to pass to callback routine + * func - function to call for each FAT entry + * + * Return value: + * 0 if all elements processed + * <0 if error occured + * >0 if callback routine returned a nonzero value + ********************************************************************* */ + +static int fat_walkfatchain(fatfs_t *fat,int start, + void *arg, + int (*func)(fatfs_t *fat, + int e, + int prev_e, + void *arg)) +{ + int prev_e = 0; + int ending_e; + int e; + int res = 0; + + e = start; + + /* Note: ending FAT entry can be 0x(F)FF8..0x(F)FFF. We assume that the + 'getfatentry' call won't return values above that. */ + if (fat->fat_twelvebit) { + ending_e = 0xFF8; + } + else { + ending_e = 0xFFF8; + } + + while (e < ending_e) { + res = (*func)(fat,e,prev_e,arg); + if (res) break; + prev_e = e; + e = fat_getfatentry(fat,e); + if (e < 0) return e; + } + + return res; +} + +/* ********************************************************************* + * fat_getwalkfunc(fat,e,prev_e,arg) + * + * Callback routien to collect all of the FAT entries into + * a FAT chain descriptor + * + * Input parameters: + * fat - FAT filesystem descriptor + * e - current entry + * prev_e - previous entry (0 if first entry) + * arg - argument passed to fat_walkfatchain + * + * Return value: + * 0 to keep walking + * else value to return from fat_walkfatchain + ********************************************************************* */ + +static int fat_getwalkfunc(fatfs_t *fat,int e, + int prev_e,void *arg) +{ + fatchain_t *chain = arg; + + if (chain->fat_entries) { + chain->fat_entries[chain->fat_count] = (uint16_t) e; + } + + chain->fat_count++; + + return 0; +} + +/* ********************************************************************* + * fat_getchain(fat,start,chain) + * + * Walk an entire FAT chain and remember the chain in a + * FAT chain descriptor + * + * Input parameters: + * fat - FAT filesystem descriptor + * start - starting FAT entry + * chain - chain descriptor + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int fat_getchain(fatfs_t *fat,int start,fatchain_t *chain) +{ + int res; + + chain->fat_entries = NULL; + chain->fat_count = 0; + chain->fat_start = start; + + /* + * walk once to count the entries. + * + * For regular files, you probably don't have to do this + * since you can predict exactly how many FAT entries + * there are given the file length. + */ + + res = fat_walkfatchain(fat,start,chain,fat_getwalkfunc); + if (res < 0) return res; + + /* + * allocate space for the entries. Include one extra + * slot for the first entry, since the first entry + * does not actually appear in the FAT (the fat is + * only the 'next' pointers). + */ + + if (chain->fat_count == 0) return 0; + chain->fat_entries = KMALLOC((chain->fat_count+1)*sizeof(uint16_t),0); + chain->fat_count = 0; + + /* + * walk again to collect entries + */ + res = fat_walkfatchain(fat,start,chain,fat_getwalkfunc); + if (res < 0) return res; + + return chain->fat_count; +} + + +/* ********************************************************************* + * fat_freechain(chain) + * + * Free memory associated with a FAT chain + * + * Input parameters: + * chain - chain descriptor + * + * Return value: + * nothing + ********************************************************************* */ + +static void fat_freechain(fatchain_t *chain) +{ + if (chain->fat_entries) { + KFREE(chain->fat_entries); + chain->fat_entries = NULL; + } + chain->fat_count = 0; +} + +/* ********************************************************************* + * fat_clusteridx(fat,chain,idx) + * + * Index into a FAT chain and return the nth cluster number + * from the chain + * + * Input parameters: + * fat - fat filesystem descriptor + * chain - chain descriptor + * idx - index into FAT chain + * + * Return value: + * FAT entry at the nth index, or + * <0 if an error occured + ********************************************************************* */ +static int fat_clusteridx(fatfs_t *fat,fatchain_t *chain,int idx) +{ + if (idx >= chain->fat_count) return CFE_ERR_INV_PARAM; /* error! */ + return (int) (unsigned int) chain->fat_entries[idx]; +} + +/* ********************************************************************* + * fat_sectoridx(fat,chain,idx) + * + * Return the sector nunber of the nth sector in a given + * FAT chain. This routine knows how to translate cluster + * numbers into sector numbers. + * + * Input parameters: + * fat - FAT filesystem descriptor + * chain - FAT chain + * idx - index of which sector to find + * + * Return value: + * sector number + * <0 if an error occured + ********************************************************************* */ +static int fat_sectoridx(fatfs_t *fat,fatchain_t *chain,int idx) +{ + int clusteridx; + int sectoridx; + int sector; + int fatentry; + + clusteridx = idx / fat->fat_bpb.bpb_sectorspercluster; + sectoridx = idx % fat->fat_bpb.bpb_sectorspercluster; + + fatentry = fat_clusteridx(fat,chain,clusteridx); + + if (fatentry < 0) xprintf("ran off end of fat chain!\n"); + if (fatentry < 2) xprintf("fat entries should be >= 2\n"); + + sector = fat->fat_bpb.bpb_reservedsectors + + (fat->fat_bpb.bpb_maxrootdir * DIRENTRYSIZE)/SECTORSIZE + + (fat->fat_bpb.bpb_numfats * fat->fat_bpb.bpb_sectorsperfat) + + (fatentry - 2) * fat->fat_bpb.bpb_sectorspercluster + + sectoridx; + + return sector; +} + +/* ********************************************************************* + * fat_getsubdirentry(fat,chain,idx,direntry) + * + * This routine is similar to fat_getrootdirentry except it + * works on a subdirectory. FAT subdirectories are like files + * containing directory entries, so we use the "get nth sector + * in chain" routines to walk the chains of sectors reading directory + * entries. + * + * Input parameters: + * fat - FAT filesystem descriptor + * chain - FAT chain + * idx - index of entry to read + * direntry - place to put directory entry we read + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int fat_getsubdirentry(fatfs_t *fat,fatchain_t *chain, + int idx,uint8_t *direntry) +{ + int sector; + int res; + + sector = fat_sectoridx(fat,chain,idx/DIRPERSECTOR); + + if (sector < 0) return sector; + + if (fat->fat_dirsecnum != sector) { + res = fat_readsector(fat,sector,1,fat->fat_dirsector); + if (res < 0) { + return res; + } + fat->fat_dirsecnum = sector; + } + + memcpy(direntry,&(fat->fat_dirsector[(idx % DIRPERSECTOR)*DIRENTRYSIZE]), + DIRENTRYSIZE); + + return 0; +} + +/* ********************************************************************* + * fat_getshortname(direntry,name) + * + * Read the short filename from a directory entry, converting + * it into its classic 8.3 form + * + * Input parameters: + * direntry - directory entry + * name - place to put 8.3 name + * + * Return value: + * nothing + ********************************************************************* */ + +static void fat_getshortname(uint8_t *direntry,char *name) +{ + int idx; + + /* + * Collect the base file name + */ + + for (idx = DIR_NAMEOFFSET; idx < (DIR_NAMEOFFSET+DIR_NAMELEN); idx++) { + if (direntry[idx] == ' ') break; + *name++ = direntry[idx]; + } + + /* + * Put in the dot for the extension only if there + * is an extension. + */ + + if (direntry[DIR_EXTOFFSET] != ' ') *name++ = '.'; + + /* + * Collect the extension + */ + + for (idx = DIR_EXTOFFSET; idx < (DIR_EXTOFFSET+DIR_EXTLEN); idx++) { + if (direntry[idx] == ' ') break; + *name++ = direntry[idx]; + } + + *name = '\0'; +} + + +/* ********************************************************************* + * fat_getlongname(fat,chain,diridx,shortentry,longname) + * + * Look backwards in the directory to locate the long file name + * that corresponds to the short file name passed in 'shortentry' + * + * Input parameters: + * fat - FAT filesystem descriptor + * chain - chain describing current directory, or NULL + * if the current directory is the root directory + * diridx - index of the short file name + * shortentry - points to the short directory entry + * longname - buffer to receive long file name (up to 261 chars) + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int fat_getlongname(fatfs_t *fat,fatchain_t *chain,int diridx, + uint8_t *shortentry,char *longname) +{ + int lfnidx = 1; + uint8_t checksum; + uint8_t direntry[DIRENTRYSIZE]; + int idx; + char *lfnptr; + int badlfn = 0; + + *longname = '\0'; + + /* + * idx is the entry # of the short name + */ + + checksum = fat_checksumname(shortentry); + + /* + * Start working backwards from current entry + * and collect pieces of the lfn + */ + + lfnptr = longname; + diridx--; + + while (diridx > 0) { + + /* + * Read previous entry + */ + + if (chain) { + fat_getsubdirentry(fat,chain,diridx,direntry); + } + else { + fat_getrootdirentry(fat,diridx,direntry); + } + + /* + * Checksum must match, it must have the right entry index, + * and it must have the LFN attribute + */ + + if (DIRENTRY_CHECKSUM(direntry) != checksum) { + badlfn = 1; + break; + } + if ((DIRENTRY_LFNIDX(direntry) & LFNIDX_MASK) != lfnidx) { + badlfn = 1; + break; + } + + if (DIRENTRY_ATTRIB(direntry) != ATTRIB_LFN) { + badlfn = 1; + break; + } + + /* + * Collect the chars from the filename. Note we + * don't deal well with real unicode chars here. + */ + + for (idx = 0; idx < 5; idx++) { + *lfnptr++ = direntry[(idx*2)+1]; + } + for (idx = 0; idx < 6; idx++) { + *lfnptr++ = direntry[(idx*2)+14]; + } + for (idx = 0; idx < 2; idx++) { + *lfnptr++ = direntry[(idx*2)+28]; + } + + /* + * Don't go too far + */ + + if (DIRENTRY_LFNIDX(direntry) & LFNIDX_END) break; + lfnidx++; + if (lfnidx > LFNIDX_MAX) { + badlfn = 1; + break; + } + + diridx--; + } + + /* + * Null terminate the lfn + */ + + *lfnptr = 0; + + if (badlfn) { + longname[0] = 0; + return CFE_ERR_FILENOTFOUND; + } + + return 0; +} + + +/* ********************************************************************* + * fat_scandir(fat,chain,name,direntry) + * + * Scan a single directory looking for a file name + * + * Input parameters: + * fat - FAT filesystem descriptor + * chain - FAT chain for directory or NULL for root directory + * name - name of file to look for (short or long name) + * direntry - place to put directory entry if we find one + * + * Return value: + * 1 if name was found + * 0 if name was not found + * else <0 is error code + ********************************************************************* */ + + +static int fat_scandir(fatfs_t *fat,fatchain_t *chain, + char *name,uint8_t *direntry) +{ + int idx; + int count; + char shortname[16]; + char longname[280]; + + /* + * Get directory size + */ + + if (chain) { + count = (chain->fat_count * fat->fat_bpb.bpb_sectorspercluster) * DIRPERSECTOR; + } + else { + count = (int) fat->fat_bpb.bpb_maxrootdir; + } + + /* + * Scan whole directory + */ + + for (idx = 0; idx < count; idx++) { + + /* + * Get entry by root or chain depending... + */ + + if (chain) { + fat_getsubdirentry(fat,chain,idx,direntry); + } + else { + fat_getrootdirentry(fat,idx,direntry); + } + + /* + * Ignore stuff we don't want to see + */ + + if (direntry[0] == DIRENTRY_LAST) break; /* stop if at end of dir */ + if (direntry[0] == DIRENTRY_DELETED) continue; /* skip deleted entries */ + if (direntry[0] == DIRENTRY_PARENTDIR) continue; /* skip ./.. entries */ + + if (DIRENTRY_ATTRIB(direntry) == ATTRIB_LFN) continue; /* skip LFNs */ + if (DIRENTRY_ATTRIB(direntry) & ATTRIB_LABEL) continue; /* skip volume labels */ + + /* + * Get actual file names from directory + */ + + fat_getshortname(direntry,shortname); + fat_getlongname(fat,chain,idx,direntry,longname); + + + if (name) { + if (strcmpi(name,shortname) == 0) return 1; + if (longname[0] && (strcmpi(name,longname) == 0)) return 1; + } + else { + xprintf("%-30s",longname[0] ? longname : shortname); +// xprintf(" Clus=%04X",DIRENTRY_STARTCLUSTER(direntry)); +// xprintf(" Attrib=%02X",DIRENTRY_ATTRIB(direntry)); +// xprintf(" Size=%d",DIRENTRY_FILELENGTH(direntry)); + xprintf("%d",DIRENTRY_FILELENGTH(direntry)); + xprintf("\n"); + } + } + + return 0; +} + +/* ********************************************************************* + * fat_findfile(fat,name,direntry) + * + * Locate a directory entry given a complete path name + * + * Input parameters: + * fat - FAT filesystem descriptor + * name - name of file to locate (forward or reverse slashses ok) + * direntry - place to put directory entry we find + * + * Return value: + * 0 if file not found + * 1 if file was found + * <0 if error occurs + ********************************************************************* */ + +static int fat_findfile(fatfs_t *fat,char *name,uint8_t *direntry) +{ + char *namecopy; + char *namepart; + char *ptr; + fatchain_t chain; + int res; + int e; + + /* + * Copy the name, we're going to hack it up + */ + + namecopy = strdup(name); + + /* + * Chew off the first piece up to the first slash. Remove + * a leading slash if it is there. + */ + + ptr = namecopy; + + if ((*ptr == '/') || (*ptr == '\\')) ptr++; + + namepart = ptr; + while (*ptr && (*ptr != '/') && (*ptr != '\\')) ptr++; + if (*ptr) *ptr++ = '\0'; + + /* + * Scan the root directory looking for the first piece + */ + + res = fat_scandir(fat,NULL,namepart,direntry); + if (res == 0) { + KFREE(namecopy); + return 0; /* file not found */ + } + + + /* + * Start scanning subdirectories until we run out + * of directory components. + */ + + namepart = ptr; + while (*ptr && (*ptr != '/') && (*ptr != '\\')) ptr++; + if (*ptr) *ptr++ = '\0'; + if (!*namepart) namepart = NULL; + + + while (namepart) { + + /* + * Scan the subdirectory + */ + + e = DIRENTRY_STARTCLUSTER(direntry); + memset(&chain,0,sizeof(chain)); + fat_getchain(fat,e,&chain); + res = fat_scandir(fat,&chain,namepart,direntry); + if (res == 0) { + break; + } + fat_freechain(&chain); + + /* + * Advance to the next piece + */ + + namepart = ptr; + while (*ptr && (*ptr != '/') && (*ptr != '\\')) ptr++; + if (*ptr) *ptr++ = '\0'; + if (!*namepart) namepart = NULL; + + /* + * If there's more to go and we hit something that + * is not a directory, stop here. + */ + + if (namepart && !(DIRENTRY_ATTRIB(direntry) & ATTRIB_DIR)) { + res = 0; + } + } + + KFREE(namecopy); + + /* + * The last piece we enumerate has to be a file. + */ + + if ((res > 0) && + (DIRENTRY_ATTRIB(direntry) & ATTRIB_DIR)) { + return 0; + } + + return res; +} + + +/* ********************************************************************* + * fat_init(fat,name) + * + * Create the filesystem descriptor and attach to the hardware + * device. + * + * Input parameters: + * fat - filesystem descriptor + * name - hardware device name + * part - true to look for partition tables + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int fat_init(fatfs_t *fat,char *name,int part) +{ + int res; + + memset(fat,0,sizeof(fatfs_t)); + fat->fat_dirsecnum = -1; + fat->fat_fatsecnum = -1; + + fat->fat_fh = cfe_open(name); + + if (fat->fat_fh < 0) return fat->fat_fh; + + res = fatfs_check_for_partition_table(fat); + /* If we were able to figure it out, use that as the default */ + if (res >= 0) part = res; + + if (part) { + res = fat_findpart(fat); + if (res < 0) { + cfe_close(fat->fat_fh); + fat->fat_fh = -1; + return res; + } + } + + res = fat_readbpb(fat); + if (res != 0) { + cfe_close(fat->fat_fh); + fat->fat_fh = -1; + return res; + } + + return 0; +} + +/* ********************************************************************* + * fat_uninit(fat) + * + * Uninit the filesystem descriptor and release any resources + * we allocated. + * + * Input parameters: + * fat - filesystem descriptor + * + * Return value: + * nothing + ********************************************************************* */ + +static void fat_uninit(fatfs_t *fat) +{ + if (fat->fat_fh >= 0) cfe_close(fat->fat_fh); + fat->fat_fh = -1; +} + +int fatfs_fileop_dir(void *fsctx); +int fatfs_fileop_dir(void *fsctx) +{ + fatfs_t *fatfs = fsctx; + uint8_t direntry[32]; + + fat_scandir(fatfs,NULL,NULL,direntry); + + return 0; +} + +/* ********************************************************************* + * fatfs_fileop_init(fsctx,devname) + * + * Create a FAT filesystem context and open the associated + * block device. + * + * Input parameters: + * fsctx - file system context (return pointer) + * devname - device name to open + * part - true to look for a partition table + * + * Return value: + * 0 if ok, else error + ********************************************************************* */ + +static int fatfs_fileop_init(void **fsctx,char *devname,int part) +{ + int res; + fatfs_t *fatfs; + + /* + * Allocate a file system context + */ + + fatfs = (fatfs_t *) KMALLOC(sizeof(fatfs_t),0); + if (!fatfs) return CFE_ERR_NOMEM; + + /* + * Open a handle to the underlying device + */ + + res = fat_init(fatfs,devname,part); + if (res != 0) { + KFREE(fatfs); + return res; + } + + *fsctx = fatfs; + + return 0; +} + +/* ********************************************************************* + * fatfs_check_for_partition_table(fatfs) + * + * This routine attempts to determine if the disk contains a + * partition table or if it contains a standard MS-DOS boot recod. + * We try to find both, and return what we find, or an error + * if it is still unclear. + * + * Input parameters: + * fatfs - fat filesystem context + * + * Return value: + * 0 if no partition table + * 1 if partition table + * <0 = error occured, could not tell or I/O error + ********************************************************************* */ + +static int fatfs_check_for_partition_table(fatfs_t *fatfs) +{ + int res; + uint8_t buffer[SECTORSIZE]; + uint8_t *part; + int idx; + int foundit = 0; + + /* + * Read sector 0 + */ + + fatfs->fat_partstart = 0; + res = fat_readsector(fatfs,0,1,buffer); + if (res < 0) return res; + + /* + * Check the seal at the end of th sector. Both + * boot sector and MBR should contain this seal. + */ + if (READWORD(buffer,BPB_SEAL) != BPB_SEAL_VALUE) { + res = CFE_ERR_BADFILESYS; + return res; + } + + /* + * See Microsoft Knowledgebase article # Q140418, it contains + * a good description of the boot sector format. + * + * If the extended information is present, and SystemID is "FAT" + * and the "bytes per sector" is 512, assume it's a regular boot block + */ + + if (((buffer[BPB_SIGNATURE] == BPB_SIGNATURE_VALUE1) || + (buffer[BPB_SIGNATURE] == BPB_SIGNATURE_VALUE2)) && + (memcmp(&buffer[BPB_SYSTEMID],"FAT",3) == 0) && + (READWORD(buffer,BPB_BYTESPERSECTOR) == 512)) { + /* Not partitioned */ + res = 0; + return res; + } + + /* If no extended information is present, check a few other key values. */ + + if ((READWORD(buffer,BPB_BYTESPERSECTOR) == 512) && + (READWORD(buffer,BPB_RESERVEDSECTORS) >= 1) && + ((READWORD(buffer,BPB_MEDIADESCRIPTOR) & 0xF0) == 0xF0)) { + res = 0; + return res; + } + + /* + * If we're still confused, look for a partition table with a valid FAT + * partition on it. We might not detect a partition table that has + * only non-FAT partitions on it, like a disk with all Linux partitions, + * but that is fine here in the FATFS module, since we only want to + * find FAT partitions anyway. + */ + part = &buffer[PTABLE_OFFSET]; + for (idx = 0; idx < PTABLE_COUNT; idx++) { + + if (((part[PTABLE_STATUS] == PTABLE_STATUS_ACTIVE) || + (part[PTABLE_STATUS] == 0x00)) && + ((part[PTABLE_TYPE] == PARTTYPE_FAT12) || + (part[PTABLE_TYPE] == PARTTYPE_FAT16) || + (part[PTABLE_TYPE] == PARTTYPE_FAT16BIG))) { + foundit = 1; + res = 1; /*Partition table present*/ + break; + } + part += PTABLE_SIZE; + } + + /* + * If at this point we did not find what we were looking for, + * return an error. + */ + if (foundit) { + res = 1; /*Partition table is present.*/ + } + else { + /*Error! We can't decide if partition table exists or not*/ + res = CFE_ERR_BADFILESYS; + } + + return res; +} + +static int fatfs_fileop_xinit(void **fsctx,void *dev) +{ + char *devname = (char *) dev; + + return fatfs_fileop_init(fsctx,devname,0); +} + +static int fatfs_fileop_pinit(void **fsctx,void *dev) +{ + char *devname = (char *) dev; + + return fatfs_fileop_init(fsctx,devname,1); +} + + + +/* ********************************************************************* + * fatfs_fileop_open(ref,name) + * + * Open a file on the FAT device. + * + * Input parameters: + * ref - place to store pointer to fileinfo + * fsctx - filesystem context + * name - name of file to open + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int fatfs_fileop_open(void **ref,void *fsctx,char *name,int mode) +{ + int res; + uint8_t direntry[DIRENTRYSIZE]; + fatfile_t *ff; + fatfs_t *fatfs; + + if (mode != FILE_MODE_READ) return CFE_ERR_UNSUPPORTED; + + fatfs = (fatfs_t *) fsctx; + + ff = (fatfile_t *) KMALLOC(sizeof(fatfile_t),0); + if (ff == NULL) return CFE_ERR_NOMEM; + + memset(ff,0,sizeof(fatfile_t)); + + ff->ff_fat = fatfs; + + res = fat_findfile(ff->ff_fat,name,direntry); + if (res <= 0) { + return CFE_ERR_FILENOTFOUND; /* not found */ + } + + /* + * Okay, the file was found. Enumerate the FAT chain + * associated with this file. + */ + + ff->ff_filelength = DIRENTRY_FILELENGTH(direntry); + + ff->ff_curpos = 0; + ff->ff_cursector = -1; + + res = fat_getchain(ff->ff_fat, + DIRENTRY_STARTCLUSTER(direntry), + &(ff->ff_chain)); + + if (res < 0) { + KFREE(ff); + return res; + } + + /* + * Return the file handle + */ + + + fatfs->fat_refcnt++; + *ref = (void *) ff; + return 0; +} + + +/* ********************************************************************* + * fatfs_fileop_close(ref) + * + * Close the file. + * + * Input parameters: + * ref - pointer to open file information + * + * Return value: + * nothing + ********************************************************************* */ + +static void fatfs_fileop_close(void *ref) +{ + fatfile_t *file = (fatfile_t *) ref; + fatfs_t *fatctx = file->ff_fat; + + fatctx->fat_refcnt--; + + fat_freechain(&(file->ff_chain)); + KFREE(file); +} + + +/* ********************************************************************* + * fatfs_fileop_uninit(ref) + * + * Uninitialize the file system. + * + * Input parameters: + * fsctx - filesystem context + * + * Return value: + * nothing + ********************************************************************* */ +static void fatfs_fileop_uninit(void *fsctx) +{ + fatfs_t *fatctx = (fatfs_t *) fsctx; + + if (fatctx->fat_refcnt) { + xprintf("fatfs_fileop_unint: warning: refcnt should be zero\n"); + } + + fat_uninit(fatctx); + + KFREE(fatctx); +} + + +/* ********************************************************************* + * fatfs_fileop_seek(ref,offset,how) + * + * Move the file pointer within the file + * + * Input parameters: + * ref - pointer to open file information + * offset - new file location or distance to move + * how - method for moving + * + * Return value: + * new file offset + * <0 if error occured + ********************************************************************* */ + +static int fatfs_fileop_seek(void *ref,int offset,int how) +{ + fatfile_t *file = (fatfile_t *) ref; + + switch (how) { + case FILE_SEEK_BEGINNING: + file->ff_curpos = offset; + break; + case FILE_SEEK_CURRENT: + file->ff_curpos += offset; + break; + default: + break; + } + + if (file->ff_curpos >= file->ff_filelength) { + file->ff_curpos = file->ff_filelength; + } + + return file->ff_curpos; +} + + +/* ********************************************************************* + * fatfs_fileop_read(ref,buf,len) + * + * Read data from the file. + * + * Input parameters: + * ref - pointer to open file information + * buf - buffer to read data into + * len - number of bytes to read + * + * Return value: + * number of bytes read + * <0 if error occured + * 0 means eof + ********************************************************************* */ + +static int fatfs_fileop_read(void *ref,uint8_t *buf,int len) +{ + fatfile_t *file = (fatfile_t *) ref; + int amtcopy; + int ttlcopy = 0; + int offset; + int sector; + int secidx; + int origpos; + int res; + uint8_t temp_buf[SECTORSIZE]; + + /* + * Remember orig position in case we have an error + */ + + origpos = file->ff_curpos; + + /* + * bounds check the length based on the file length + */ + + if ((file->ff_curpos + len) > file->ff_filelength) { + len = file->ff_filelength - file->ff_curpos; + } + + res = 0; + + /* + * while ther is still data to be transferred + */ + + + while (len) { + + /* + * Calculate the sector offset and index in the sector + */ + + offset = file->ff_curpos % SECTORSIZE; + secidx = file->ff_curpos / SECTORSIZE; + + sector = fat_sectoridx(file->ff_fat,&(file->ff_chain),secidx); + + if (sector < 0) { + xprintf("should not happen, sector = -1!\n"); + return sector; + } + + /* + * first transfer up to the sector boundary + */ + + amtcopy = len; + if (amtcopy > (SECTORSIZE-offset)) { + amtcopy = (SECTORSIZE-offset); + } + + /* + * If transferring exactly a sector, on a sector + * boundary, read the data directly into the user buffer + * + * Extra credit: See if we can transfer more than one + * sector at a time, by determining if we can read a run of + * contiguous sectors (very likely) + * + * Otherwise: read into the sector buffer and + * transfer the data to user memory. + */ + + if ((offset == 0) && (amtcopy == SECTORSIZE)) { + res = fat_readsector(file->ff_fat,sector,1,temp_buf); + if (res < 0) { + xprintf("I/O error!\n"); + break; + } + memcpy(buf,temp_buf,amtcopy); + } + else { + if (file->ff_cursector != sector) { + res = fat_readsector(file->ff_fat,sector,1,file->ff_sector); + if (res < 0) { + break; + } + file->ff_cursector = sector; + } + memcpy(buf,&(file->ff_sector[offset]),amtcopy); + } + + /* + * Adjust/update all our pointers. + */ + + buf += amtcopy; + file->ff_curpos += amtcopy; + ttlcopy += amtcopy; + len -= amtcopy; + + /* + * see if we ran off the end of the file. Should not + * be necessary. + */ + + if (file->ff_curpos >= file->ff_filelength) { + /* should not be necessary */ + break; + } + } + + /* + * If an error occured, get out now. + */ + + if (res < 0) { + file->ff_curpos = origpos; + return res; + } + + return ttlcopy; + +} + +static int fatfs_fileop_write(void *ref,uint8_t *buf,int len) +{ + return CFE_ERR_UNSUPPORTED; +} + + +#if 0 + +void main(int argc,char *argv[]) +{ + fatfs_t fat; + int idx; + unsigned int e; + uint8_t direntry[DIRENTRYSIZE]; + fatchain_t chain; + int res; + + + fat_init(&fat,"floppy.raw"); + + + if (fat_readbpb(&fat) == 0) { + fat_dumpbpb(&fat.fat_bpb); + } + +#if 0 + for (idx = 0; idx < (int) fat.fat_bpb.bpb_maxrootdir; idx++) { + fat_getrootdirentry(&fat,idx,direntry); + if (direntry[0] == 0) break; + if (direntry[0] == 0xE5) continue; + xprintf("%3d: ",idx); + fat_dumpdirentry(direntry); + } +#endif + + fat_scandir(&fat,NULL,NULL,direntry); + + for (e = 0x150; e < 0x160; e++) { + xprintf("Entry %03X is %03X\n",e,fat_getfatentry(&fat,e)); + } + +#if 0 + e = 0x36E; + while (e != 0xFFF) { + e = fat_getfatentry(&fat,e); + xprintf("%03X ",e); + } +#endif + + + xprintf("\n\n"); + e = 0x36E; + memset(&chain,0,sizeof(chain)); + fat_getchain(&fat,e,&chain); + fat_scandir(&fat,&chain,NULL,direntry); + fat_freechain(&chain); + + xprintf("\n\n"); + e = 0x36F; + memset(&chain,0,sizeof(chain)); + fat_getchain(&fat,e,&chain); + fat_scandir(&fat,&chain,NULL,direntry); + fat_freechain(&chain); + + xprintf("\n\n"); + e = 0x370; + memset(&chain,0,sizeof(chain)); + fat_getchain(&fat,e,&chain); + fat_scandir(&fat,&chain,NULL,direntry); + fat_freechain(&chain); + + xprintf("\n\n"); + + res = fat_findfile(&fat,argc > 1 ? argv[1] : "/usr/local/include/ansidecl.h",direntry); + xprintf("res = %d\n",res); + + if (res) fat_dumpdirentry(direntry); + + close(fat.fat_fh); +} + +void main(int argc,char *argv[]) +{ + void *ref; + int res; + char buffer[257]; + int total = 0; + +// res = fatfs_fileop_open(&ref,"floppy.raw:/usr/local/include/bfdlink.h"); + res = fatfs_fileop_open(&ref,"floppy.raw:/idedrv.h"); + + if (res != 0) { + xprintf("Could not open file: %d\n",res); + exit(1); + } + + for (;;) { + res = fatfs_fileop_read(ref,buffer,39); +// xprintf("read returned %d\n",res); + if (res <= 0) break; + + if (res > 0) { + total += res; + buffer[res] = 0; + xprintf("%s",buffer); + } + } + + if (res < 0) xprintf("error! \n"); + + xprintf("[total %d]\n",total); + + fatfs_fileop_close(ref); + + exit(0); + +} + +#endif |