1/*  *********************************************************************
2    *  Broadcom Common Firmware Environment (CFE)
3    *
4    *  FAT file system				File: cfe_fatfs.c
5    *
6    *  This module knows how to read files from a FAT formatted
7    *  file system (12 or 16 bit fats only for now)
8    *
9    *  Eventually, we'll also include support for the FAT Translation
10    *  Layer (FTL) on PCMCIA flash file systems.
11    *
12    *  Author:  Mitch Lichtenberg
13    *
14    *********************************************************************
15    *
16    *  Copyright 2000,2001,2002,2003
17    *  Broadcom Corporation. All rights reserved.
18    *
19    *  This software is furnished under license and may be used and
20    *  copied only in accordance with the following terms and
21    *  conditions.  Subject to these conditions, you may download,
22    *  copy, install, use, modify and distribute modified or unmodified
23    *  copies of this software in source and/or binary form.  No title
24    *  or ownership is transferred hereby.
25    *
26    *  1) Any source code used, modified or distributed must reproduce
27    *     and retain this copyright notice and list of conditions
28    *     as they appear in the source file.
29    *
30    *  2) No right is granted to use any trade name, trademark, or
31    *     logo of Broadcom Corporation.  The "Broadcom Corporation"
32    *     name may not be used to endorse or promote products derived
33    *     from this software without the prior written permission of
34    *     Broadcom Corporation.
35    *
36    *  3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
37    *     IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
38    *     WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
39    *     PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
40    *     SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
41    *     PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
42    *     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
43    *     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
44    *     GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
45    *     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
46    *     OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
47    *     TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
48    *     THE POSSIBILITY OF SUCH DAMAGE.
49    ********************************************************************* */
50
51#include "cfe.h"
52
53#include "cfe_fileops.h"
54
55#include "cfe_loader.h"
56
57
58/*  *********************************************************************
59    *  Constants
60    ********************************************************************* */
61
62#define SECTORSIZE 		512
63#define DIRENTRYSIZE		32
64#define DIRPERSECTOR		(SECTORSIZE/DIRENTRYSIZE)
65
66/*#define _FATFS_DEBUG_*/
67
68/*
69 * Bios Parameter Block offsets and values
70 */
71
72#define BPB_JMPINSTR		0x00
73#define BPB_JMPINSTR_VALUE	0xEB
74#define BPB_JMPINSTR_VALUE2	0xE9
75#define BPB_SEAL		0x1FE
76#define BPB_SEAL_VALUE		0xAA55
77
78#define BPB_BYTESPERSECTOR	0x0B
79#define BPB_SECTORSPERCLUSTER	0x0D
80#define BPB_RESERVEDSECTORS	0x0E
81#define BPB_NUMFATS		0x10
82#define BPB_MAXROOTDIR		0x11
83#define BPB_TOTALSECTORS	0x13
84#define BPB_SECTORSPERFAT	0x16
85#define BPB_SECTORSPERTRACK	0x18
86#define BPB_NUMHEADS		0x1A
87#define BPB_HIDDENSECTORS	0x1C
88#define BPB_SYSTEMID		54
89#define BPB_MEDIADESCRIPTOR	21
90#define BPB_SIGNATURE		38
91#define BPB_SIGNATURE_VALUE1	0x28
92#define BPB_SIGNATURE_VALUE2	0x29
93
94/*
95 * Partition types
96 */
97
98#define PARTTYPE_EMPTY	0
99#define PARTTYPE_FAT12	1
100#define PARTTYPE_FAT16	4
101#define PARTTYPE_FAT16BIG 6
102#define PARTTYPE_FAT32	0x0B
103
104/*
105 * Partition table offsets
106 */
107#define PTABLE_STATUS		0
108#define PTABLE_STARTHEAD	1
109#define PTABLE_STARTSECCYL	2	/* 2 bytes */
110#define PTABLE_TYPE		4
111#define PTABLE_ENDHEAD		5
112#define PTABLE_ENDSECCYL	6	/* 2 bytes */
113#define PTABLE_BOOTSECTOR	8	/* 4 bytes */
114#define PTABLE_NUMSECTORS	12	/* 4 bytes */
115
116#define PTABLE_SIZE		16
117#define PTABLE_COUNT		4
118#define PTABLE_OFFSET		(512-2-(PTABLE_COUNT*PTABLE_SIZE))
119
120#define PTABLE_STATUS_ACTIVE	0x80
121
122/*
123 * Directory attributes
124 */
125
126#define ATTRIB_NORMAL     0x00
127#define ATTRIB_READONLY   0x01
128#define ATTRIB_HIDDEN     0x02
129#define ATTRIB_SYSTEM     0x04
130#define ATTRIB_LABEL      0x08
131#define ATTRIB_DIR        0x10
132#define ATTRIB_ARCHIVE    0x20
133
134#define ATTRIB_LFN	  0x0F
135
136/*
137 * Macros to read fields in directory & BPB entries
138 */
139
140#define READWORD(buffer,x) (((unsigned int) (buffer)[(x)]) | \
141                           (((unsigned int) (buffer)[(x)+1]) << 8))
142
143#define READWORD32(buffer,x) (READWORD(buffer,(x)) | (READWORD(buffer,(x)+2) << 16))
144
145#define READBYTE(buffer,x) ((unsigned int) (buffer)[(x)])
146
147/*
148 * Directory entry offsets and values
149 */
150
151#define DIR_CHECKSUM		13
152#define DIR_FILELENGTH		28
153#define DIR_STARTCLUSTER	26
154#define DIR_ATTRIB		11
155#define DIR_NAMEOFFSET		0
156#define DIR_NAMELEN		8
157#define DIR_EXTOFFSET		8
158#define DIR_EXTLEN		3
159
160#define DIRENTRY_CHECKSUM(e) READBYTE(e,DIR_CHECKSUM)
161#define DIRENTRY_FILELENGTH(e) READWORD32(e,DIR_FILELENGTH)
162#define DIRENTRY_STARTCLUSTER(e) READWORD(e,DIR_STARTCLUSTER)
163#define DIRENTRY_ATTRIB(e) READBYTE(e,DIR_ATTRIB)
164
165#define DIRENTRY_LAST		0
166#define DIRENTRY_DELETED	0xE5
167#define DIRENTRY_PARENTDIR	0x2E
168
169#define DIRENTRY_LFNIDX(e) READBYTE(e,0)
170#define LFNIDX_MASK	0x1F
171#define LFNIDX_END	0x40
172#define LFNIDX_MAX	20
173
174/*  *********************************************************************
175    *  Types
176    ********************************************************************* */
177
178/*
179 * Internalized BPB
180 */
181
182typedef struct bpb_s {
183    unsigned int bpb_bytespersector;
184    unsigned int bpb_sectorspercluster;
185    unsigned int bpb_reservedsectors;
186    unsigned int bpb_numfats;
187    unsigned int bpb_maxrootdir;
188    unsigned int bpb_totalsectors;
189    unsigned int bpb_sectorsperfat;
190    unsigned int bpb_sectorspertrack;
191    unsigned int bpb_numheads;
192    unsigned int bpb_hiddensectors;
193} bpb_t;
194
195/*
196 * FAT Filesystem descriptor - contains working information
197 * about an "open" file system
198 */
199
200typedef struct fatfs_s {
201    int fat_fh;
202    int fat_refcnt;
203    bpb_t fat_bpb;
204    int fat_bits;
205    int fat_partstart;
206    uint8_t fat_dirsector[SECTORSIZE];
207    int fat_dirsecnum;
208    uint8_t fat_fatsector[SECTORSIZE];
209    int fat_fatsecnum;
210} fatfs_t;
211
212/*
213 * FAT Chain - describes a series of FAT entries
214 */
215
216typedef struct fatchain_s {
217    int fat_start;
218    uint16_t *fat_entries;
219    int fat_count;
220} fatchain_t;
221
222/*
223 * FAT File descriptor - contains working information
224 * about an open file (including the filesystem info)
225 */
226
227typedef struct fatfile_s {
228    fatfs_t *ff_fat;
229    int ff_filelength;
230    fatchain_t ff_chain;
231    int ff_curpos;
232    int ff_cursector;
233    uint8_t ff_sector[SECTORSIZE];
234} fatfile_t;
235
236/*  *********************************************************************
237    *  Prototypes
238    ********************************************************************* */
239
240static int fatfs_fileop_xinit(void **fsctx,void *filename);
241static int fatfs_fileop_pinit(void **fsctx,void *filename);
242static int fatfs_fileop_open(void **ref,void *fsctx,char *filename,int mode);
243static int fatfs_fileop_read(void *ref,hsaddr_t buf,int len);
244static int fatfs_fileop_write(void *ref,hsaddr_t buf,int len);
245static int fatfs_fileop_seek(void *ref,int offset,int how);
246static void fatfs_fileop_close(void *ref);
247static void fatfs_fileop_uninit(void *fsctx);
248
249static int fatfs_check_for_partition_table(fatfs_t *fatfs);
250
251/*  *********************************************************************
252    *  FAT fileio dispatch table
253    ********************************************************************* */
254
255/*
256 * Raw FAT (no partition table) - used only on floppies
257 */
258
259const fileio_dispatch_t fatfs_fileops = {
260    "rfat",
261    LOADFLG_NOBB,
262    fatfs_fileop_xinit,
263    fatfs_fileop_open,
264    fatfs_fileop_read,
265    fatfs_fileop_write,
266    fatfs_fileop_seek,
267    fatfs_fileop_close,
268    fatfs_fileop_uninit
269};
270
271/*
272 * Partitioned FAT - used on Zip disks, removable hard disks,
273 * hard disks, flash cards, etc.
274 */
275
276const fileio_dispatch_t pfatfs_fileops = {
277    "fat",
278    LOADFLG_NOBB,
279    fatfs_fileop_pinit,
280    fatfs_fileop_open,
281    fatfs_fileop_read,
282    fatfs_fileop_write,
283    fatfs_fileop_seek,
284    fatfs_fileop_close,
285    fatfs_fileop_uninit
286};
287
288
289/*  *********************************************************************
290    *  fat_readsector(fatfs,sector,numsec,buffer)
291    *
292    *  Read one or more sectors from the disk into memory
293    *
294    *  Input parameters:
295    *  	   fatfs - fat filesystem descriptor
296    *  	   sector - sector number
297    *  	   numsec - number of sectors to read
298    *  	   buffer - buffer to read sectors into
299    *
300    *  Return value:
301    *  	   0 if ok
302    *  	   else error code
303    ********************************************************************* */
304
305static int fat_readsector(fatfs_t *fatfs,int sector,int numsec,uint8_t *buffer)
306{
307    int res;
308
309    res = cfe_readblk(fatfs->fat_fh,(sector+fatfs->fat_partstart)*SECTORSIZE,
310		      PTR2HSADDR(buffer),numsec*SECTORSIZE);
311
312    if (res != numsec*SECTORSIZE) return CFE_ERR_IOERR;
313
314    return 0;
315}
316
317
318/*  *********************************************************************
319    *  fat_dumpbpb(bpb)
320    *
321    *  Debug function; display fields in a BPB
322    *
323    *  Input parameters:
324    *  	   bpb - BIOS parameter block structure
325    *
326    *  Return value:
327    *  	   nothing
328    ********************************************************************* */
329
330#ifdef _FATFS_DEBUG_
331static void fat_dumpbpb(bpb_t *bpb)
332{
333    xprintf("Bytes per sector    %d\n",bpb->bpb_bytespersector);
334    xprintf("Sectors per cluster %d\n",bpb->bpb_sectorspercluster);
335    xprintf("Reserved sectors    %d\n",bpb->bpb_reservedsectors);
336    xprintf("Number of FATs      %d\n",bpb->bpb_numfats);
337    xprintf("Root dir entries    %d\n",bpb->bpb_maxrootdir);
338    xprintf("Total sectors       %d\n",bpb->bpb_totalsectors);
339    xprintf("Sectors per FAT     %d\n",bpb->bpb_sectorsperfat);
340    xprintf("Sectors per track   %d\n",bpb->bpb_sectorspertrack);
341    xprintf("Number of heads     %d\n",bpb->bpb_numheads);
342    xprintf("Hidden sectors      %d\n",bpb->bpb_hiddensectors);
343}
344#endif
345
346/*  *********************************************************************
347    *  fat_findpart(fatfs)
348    *
349    *  For partitioned disks, locate the active partition
350    *  and set "fat_partstart" accordingly
351    *
352    *  Input parameters:
353    *  	   fatfs - FAT filesystem descriptor
354    *
355    *  Return value:
356    *  	   0 if we found a valid partition table; else error code
357    ********************************************************************* */
358
359static int fat_findpart(fatfs_t *fatfs)
360{
361    uint8_t buffer[SECTORSIZE];
362    uint8_t *part;
363    int res;
364    int idx;
365
366    fatfs->fat_partstart = 0;		/* make sure we get real boot sector */
367    res = fat_readsector(fatfs,0,1,buffer);
368    if (res < 0) return res;
369
370    /*
371     * Normally you're supposed to check for a JMP instruction.
372     * At least that's what many people do.  Flash MBRs don't start
373     * with JMP instructions, so just look for the seal.
374     *
375     *
376     *  if (READBYTE(buffer,BPB_JMPINSTR) != BPB_JMPINSTR_VALUE) {
377     *                 return CFE_ERR_BADFILESYS;
378     *		       }
379     */
380
381    /*
382     * Check the seal at the end of th sector
383     */
384
385    if (READWORD(buffer,BPB_SEAL) != BPB_SEAL_VALUE) return CFE_ERR_BADFILESYS;
386
387    /*
388     * Look for an active FAT partition.  The partition we want must
389     * be the active one.   We do not deal with extended partitions
390     * here.  Hey, this is supposed to be boot code!
391     */
392
393    part = &buffer[PTABLE_OFFSET];
394    for (idx = 0; idx < PTABLE_COUNT; idx++) {
395	if ((part[PTABLE_STATUS] == PTABLE_STATUS_ACTIVE) &&
396	    ((part[PTABLE_TYPE] == PARTTYPE_FAT12) ||
397	     (part[PTABLE_TYPE] == PARTTYPE_FAT16) ||
398	     (part[PTABLE_TYPE] == PARTTYPE_FAT16BIG))) {
399	    break;
400	    }
401
402	part += PTABLE_SIZE;
403	}
404
405    if (idx == PTABLE_COUNT) return CFE_ERR_BADFILESYS;
406
407    /*
408     * The info we want is really just the pointer to the
409     * boot (BPB) sector.  Get that and we'll use it for an
410     * offset into the disk later.
411     */
412
413    fatfs->fat_partstart = READWORD32(part,PTABLE_BOOTSECTOR);
414
415    return 0;
416}
417
418
419/*  *********************************************************************
420    *  fat_readbpb(fatfs)
421    *
422    *  Read and internalize the BIOS Parameter Block
423    *
424    *  Input parameters:
425    *  	   fatfs - FAT filesystem descriptor
426    *
427    *  Return value:
428    *  	   0 if ok
429    *  	   else error code (usually, BPB is not valid)
430    ********************************************************************* */
431
432static int fat_readbpb(fatfs_t *fatfs)
433{
434    uint8_t buffer[SECTORSIZE];
435    int res;
436    int datasectors;
437
438    res = fat_readsector(fatfs,0,1,buffer);
439    if (res < 0) return res;
440
441    if (READBYTE(buffer,BPB_JMPINSTR) != BPB_JMPINSTR_VALUE) return CFE_ERR_BADFILESYS;
442    if (READWORD(buffer,BPB_SEAL) != BPB_SEAL_VALUE) return CFE_ERR_BADFILESYS;
443
444    fatfs->fat_bpb.bpb_bytespersector = READWORD(buffer,BPB_BYTESPERSECTOR);
445    fatfs->fat_bpb.bpb_sectorspercluster = READBYTE(buffer,BPB_SECTORSPERCLUSTER);
446    fatfs->fat_bpb.bpb_reservedsectors = READWORD(buffer,BPB_RESERVEDSECTORS);
447    fatfs->fat_bpb.bpb_numfats = READBYTE(buffer,BPB_NUMFATS);
448    fatfs->fat_bpb.bpb_maxrootdir = READWORD(buffer,BPB_MAXROOTDIR);
449    fatfs->fat_bpb.bpb_totalsectors = READWORD(buffer,BPB_TOTALSECTORS);
450    fatfs->fat_bpb.bpb_sectorsperfat = READWORD(buffer,BPB_SECTORSPERFAT);
451    fatfs->fat_bpb.bpb_sectorspertrack = READWORD(buffer,BPB_SECTORSPERTRACK);
452    fatfs->fat_bpb.bpb_numheads = READWORD(buffer,BPB_NUMHEADS);
453    fatfs->fat_bpb.bpb_hiddensectors = READWORD(buffer,BPB_HIDDENSECTORS);
454
455    if (fatfs->fat_bpb.bpb_bytespersector != SECTORSIZE) return CFE_ERR_BADFILESYS;
456    if (fatfs->fat_bpb.bpb_numfats > 2) return CFE_ERR_BADFILESYS;
457
458    /*
459     * XXX sanity check other fields
460     */
461
462    /* Count data clusters to select FAT12 or FAT16 */
463    datasectors = fatfs->fat_bpb.bpb_totalsectors -
464        (fatfs->fat_bpb.bpb_reservedsectors +
465         (fatfs->fat_bpb.bpb_maxrootdir * DIRENTRYSIZE)/SECTORSIZE +
466         (fatfs->fat_bpb.bpb_numfats * fatfs->fat_bpb.bpb_sectorsperfat));
467    fatfs->fat_bits =
468        ((datasectors / fatfs->fat_bpb.bpb_sectorspercluster) > 4084) ? 16 : 12;
469
470#ifdef _FATFS_DEBUG_
471    fat_dumpbpb(&(fatfs->fat_bpb));
472#endif
473
474    return 0;
475}
476
477
478
479/*  *********************************************************************
480    *  fat_getentry(fatfs,entry)
481    *
482    *  Read a FAT entry.  This is more involved than you'd think,
483    *  since we have to deal with 12 and 16 (and someday 32) bit FATs,
484    *  and the nasty case where a 12-bit FAT entry crosses a sector
485    *  boundary.
486    *
487    *  Input parameters:
488    *  	   fatfs - FAT filesystem descriptor
489    *  	   entry - index of FAT entry
490    *
491    *  Return value:
492    *  	   FAT entry, or <0 if an error occured
493    ********************************************************************* */
494
495static int fat_getfatentry(fatfs_t *fatfs,int entry)
496{
497    int fatsect;
498    int byteoffset;
499    int fatstart;
500    int fatoffset;
501    uint8_t b1,b2,b3;
502    int res;
503
504    fatstart = fatfs->fat_bpb.bpb_reservedsectors;
505
506    if (fatfs->fat_bits == 12) {
507	int odd;
508	odd = entry & 1;
509	byteoffset = ((entry & ~1) * 3) / 2;
510	fatsect = byteoffset / SECTORSIZE;
511	fatoffset = byteoffset % SECTORSIZE;
512
513	if (fatfs->fat_fatsecnum != fatsect) {
514	    res = fat_readsector(fatfs,fatsect+fatstart,1,fatfs->fat_fatsector);
515	    if (res < 0) {
516		return res;
517		}
518	    fatfs->fat_fatsecnum = fatsect;
519	    }
520
521	b1 = fatfs->fat_fatsector[fatoffset];
522
523	if ((fatoffset+1) >= SECTORSIZE) {
524	    res = fat_readsector(fatfs,fatsect+1+fatstart,1,fatfs->fat_fatsector);
525	    if (res < 0) {
526		return res;
527		}
528	    fatfs->fat_fatsecnum = fatsect+1;
529	    fatoffset -= SECTORSIZE;
530	    }
531
532	b2 = fatfs->fat_fatsector[fatoffset+1];
533
534	if ((fatoffset+2) >= SECTORSIZE) {
535	    res = fat_readsector(fatfs,fatsect+1+fatstart,1,fatfs->fat_fatsector);
536	    if (res < 0) {
537		return res;
538		}
539	    fatfs->fat_fatsecnum = fatsect+1;
540	    fatoffset -= SECTORSIZE;
541	    }
542
543	b3 = fatfs->fat_fatsector[fatoffset+2];
544
545	if (odd) {
546	    return ((unsigned int) b3 << 4) + ((unsigned int) (b2 & 0xF0) >> 4);
547	    }
548	else {
549	    return ((unsigned int) (b2 & 0x0F) << 8) + ((unsigned int) b1);
550	    }
551
552	}
553    else {
554	byteoffset = entry * 2;
555	fatsect = byteoffset / SECTORSIZE;
556	fatoffset = byteoffset % SECTORSIZE;
557
558	if (fatfs->fat_fatsecnum != fatsect) {
559	    res = fat_readsector(fatfs,fatsect+fatstart,1,fatfs->fat_fatsector);
560	    if (res < 0) {
561		return res;
562		}
563	    fatfs->fat_fatsecnum = fatsect;
564	    }
565
566	b1 = fatfs->fat_fatsector[fatoffset];
567	b2 = fatfs->fat_fatsector[fatoffset+1];
568	return ((unsigned int) b1) + (((unsigned int) b2) << 8);
569	}
570}
571
572/*  *********************************************************************
573    *  fat_getrootdirentry(fatfs,entryidx,entry)
574    *
575    *  Read a root directory entry.  The FAT12/16 root directory
576    *  is a contiguous group of sectors, whose size is specified in
577    *  the BPB.  This routine just digs out an entry from there
578    *
579    *  Input parameters:
580    *  	   fatfs - FAT filesystem descriptor
581    *  	   entryidx - 0-based entry index to read
582    *  	   entry - pointer to directory entry (32 bytes)
583    *
584    *  Return value:
585    *  	   0 if ok
586    *  	   <0 if error occured
587    ********************************************************************* */
588
589static int fat_getrootdirentry(fatfs_t *fatfs,int entryidx,uint8_t *entry)
590{
591    int rootdirstart;
592    int rootdirsize;
593    int dirsecnum;
594    int res;
595
596    if (entryidx >= fatfs->fat_bpb.bpb_maxrootdir) {
597	memset(entry,0,DIRENTRYSIZE);
598	return CFE_ERR_INV_PARAM;
599	}
600
601    rootdirstart = fatfs->fat_bpb.bpb_reservedsectors +
602	fatfs->fat_bpb.bpb_numfats * fatfs->fat_bpb.bpb_sectorsperfat;
603
604    rootdirsize = fatfs->fat_bpb.bpb_maxrootdir / DIRENTRYSIZE;
605
606    dirsecnum = rootdirstart + entryidx / DIRPERSECTOR;
607
608    if (fatfs->fat_dirsecnum != dirsecnum) {
609	res = fat_readsector(fatfs,dirsecnum,1,fatfs->fat_dirsector);
610	if (res < 0) {
611	    return res;
612	    }
613	fatfs->fat_dirsecnum = dirsecnum;
614	}
615
616    memcpy(entry,&(fatfs->fat_dirsector[(entryidx % DIRPERSECTOR)*DIRENTRYSIZE]),
617	   DIRENTRYSIZE);
618
619    return 0;
620}
621
622/*  *********************************************************************
623    *  fat_checksumname(name)
624    *
625    *  Calculate the "long filename" checksum for a given short name.
626    *  All LFN directory entries associated with the short name are
627    *  given the same checksum byte, to help keep the long name
628    *  consistent.
629    *
630    *  Input parameters:
631    *  	   name - pointer to 32-byte directory entry
632    *
633    *  Return value:
634    *  	   checksum value
635    ********************************************************************* */
636
637static uint8_t fat_checksumname(uint8_t *name)
638{
639   uint8_t sum = 0;
640   uint8_t newbit;
641   int idx;
642
643   for (idx = 0; idx < 11; idx++) {
644       newbit = (sum & 1) ? 0x80 : 0x00;
645       sum >>= 1;
646       sum |= newbit;
647       sum += name[idx];
648       }
649
650   return sum;
651}
652
653#ifdef _FATFS_DEBUG_
654void fat_dumpdirentry(uint8_t *entry);
655void fat_dumpdirentry(uint8_t *entry)
656{
657    uint8_t name[32];
658    int idx;
659
660    if (entry[11] != ATTRIB_LFN) {
661	memcpy(name,entry,11);
662	name[11] = 0;
663	xprintf("%s   %02X %04X %d\n",
664	       name,DIRENTRY_ATTRIB(entry),
665	       DIRENTRY_STARTCLUSTER(entry),
666	       DIRENTRY_FILELENGTH(entry));
667	}
668    else {
669	for (idx = 0; idx < 5; idx++) {
670	    name[idx] = entry[(idx*2)+1];
671	    }
672	for (idx = 0; idx < 6; idx++) {
673	    name[idx+5] = entry[(idx*2)+14];
674	    }
675	for (idx = 0; idx < 2; idx++) {
676	    name[idx+11] = entry[(idx*2)+28];
677	    }
678	name[13] = '\0';
679	xprintf("%02X: %s   %04X  cksum %02X\n",entry[0],
680	       name,READWORD(entry,0x1A),entry[13]);
681	}
682}
683#endif
684
685
686/*  *********************************************************************
687    *  fat_walkfatchain(fat,start,arg,func)
688    *
689    *  Walk a FAT chain, calling a callback routine for each entry
690    *  we find along the way.
691    *
692    *  Input parameters:
693    *  	   fat - FAT filesystem descriptor
694    *  	   start - starting FAT entry (from the directory, usually)
695    *  	   arg - argument to pass to callback routine
696    *  	   func - function to call for each FAT entry
697    *
698    *  Return value:
699    *  	   0 if all elements processed
700    *  	   <0 if error occured
701    *  	   >0 if callback routine returned a nonzero value
702    ********************************************************************* */
703
704static int fat_walkfatchain(fatfs_t *fat,int start,
705		     void *arg,
706		     int (*func)(fatfs_t *fat,
707				 int e,
708				 int prev_e,
709				 void *arg))
710{
711    int prev_e = 0;
712    int ending_e;
713    int e;
714    int res = 0;
715
716    e = start;
717
718    /* Note: ending FAT entry can be 0x(F)FF8..0x(F)FFF. We assume that the
719       'getfatentry' call won't return values above that. */
720    if (fat->fat_bits == 12) {
721	ending_e = 0xFF8;
722	}
723    else {
724	ending_e = 0xFFF8;
725	}
726
727    while (e < ending_e) {
728	res = (*func)(fat,e,prev_e,arg);
729	if (res) break;
730	prev_e = e;
731	e = fat_getfatentry(fat,e);
732	if (e < 0) return e;
733	}
734
735    return res;
736}
737
738/*  *********************************************************************
739    *  fat_getwalkfunc(fat,e,prev_e,arg)
740    *
741    *  Callback routien to collect all of the FAT entries into
742    *  a FAT chain descriptor
743    *
744    *  Input parameters:
745    *  	   fat - FAT filesystem descriptor
746    *  	   e - current entry
747    *  	   prev_e - previous entry (0 if first entry)
748    *  	   arg - argument passed to fat_walkfatchain
749    *
750    *  Return value:
751    *  	   0 to keep walking
752    *  	   else value to return from fat_walkfatchain
753    ********************************************************************* */
754
755static int fat_getwalkfunc(fatfs_t *fat,int e,
756			   int prev_e,void *arg)
757{
758    fatchain_t *chain = arg;
759
760    if (chain->fat_entries) {
761	chain->fat_entries[chain->fat_count] = (uint16_t) e;
762	}
763
764    chain->fat_count++;
765
766    return 0;
767}
768
769/*  *********************************************************************
770    *  fat_getchain(fat,start,chain)
771    *
772    *  Walk an entire FAT chain and remember the chain in a
773    *  FAT chain descriptor
774    *
775    *  Input parameters:
776    *  	   fat - FAT filesystem descriptor
777    *  	   start - starting FAT entry
778    *  	   chain - chain descriptor
779    *
780    *  Return value:
781    *  	   0 if ok
782    *  	   else error code
783    ********************************************************************* */
784
785static int fat_getchain(fatfs_t *fat,int start,fatchain_t *chain)
786{
787    int res;
788
789    chain->fat_entries = NULL;
790    chain->fat_count = 0;
791    chain->fat_start = start;
792
793    /*
794     * walk once to count the entries.
795     *
796     * For regular files, you probably don't have to do this
797     * since you can predict exactly how many FAT entries
798     * there are given the file length.
799     */
800
801    res = fat_walkfatchain(fat,start,chain,fat_getwalkfunc);
802    if (res < 0) return res;
803
804    /*
805     * allocate space for the entries. Include one extra
806     * slot for the first entry, since the first entry
807     * does not actually appear in the FAT (the fat is
808     * only the 'next' pointers).
809     */
810
811    if (chain->fat_count == 0) return 0;
812    chain->fat_entries = KMALLOC((chain->fat_count+1)*sizeof(uint16_t),0);
813    chain->fat_count = 0;
814
815    /*
816     * walk again to collect entries
817     */
818    res = fat_walkfatchain(fat,start,chain,fat_getwalkfunc);
819    if (res < 0) return res;
820
821    return chain->fat_count;
822}
823
824
825/*  *********************************************************************
826    *  fat_freechain(chain)
827    *
828    *  Free memory associated with a FAT chain
829    *
830    *  Input parameters:
831    *  	   chain - chain descriptor
832    *
833    *  Return value:
834    *  	   nothing
835    ********************************************************************* */
836
837static void fat_freechain(fatchain_t *chain)
838{
839    if (chain->fat_entries) {
840	KFREE(chain->fat_entries);
841	chain->fat_entries = NULL;
842	}
843    chain->fat_count = 0;
844}
845
846/*  *********************************************************************
847    *  fat_clusteridx(fat,chain,idx)
848    *
849    *  Index into a FAT chain and return the nth cluster number
850    *  from the chain
851    *
852    *  Input parameters:
853    *  	   fat - fat filesystem descriptor
854    *  	   chain - chain descriptor
855    *  	   idx - index into FAT chain
856    *
857    *  Return value:
858    *  	   FAT entry at the nth index, or
859    *  	   <0 if an error occured
860    ********************************************************************* */
861static int fat_clusteridx(fatfs_t *fat,fatchain_t *chain,int idx)
862{
863    if (idx >= chain->fat_count) return CFE_ERR_INV_PARAM;	/* error! */
864    return (int) (unsigned int) chain->fat_entries[idx];
865}
866
867/*  *********************************************************************
868    *  fat_sectoridx(fat,chain,idx)
869    *
870    *  Return the sector nunber of the nth sector in a given
871    *  FAT chain.  This routine knows how to translate cluster
872    *  numbers into sector numbers.
873    *
874    *  Input parameters:
875    *  	   fat - FAT filesystem descriptor
876    *  	   chain - FAT chain
877    *  	   idx - index of which sector to find
878    *
879    *  Return value:
880    *  	   sector number
881    *  	   <0 if an error occured
882    ********************************************************************* */
883static int fat_sectoridx(fatfs_t *fat,fatchain_t *chain,int idx)
884{
885    int clusteridx;
886    int sectoridx;
887    int sector;
888    int fatentry;
889
890    clusteridx = idx / fat->fat_bpb.bpb_sectorspercluster;
891    sectoridx = idx % fat->fat_bpb.bpb_sectorspercluster;
892
893    fatentry = fat_clusteridx(fat,chain,clusteridx);
894
895    if (fatentry < 0) xprintf("ran off end of fat chain!\n");
896    if (fatentry < 2) xprintf("fat entries should be >= 2\n");
897
898    sector = fat->fat_bpb.bpb_reservedsectors +
899	(fat->fat_bpb.bpb_maxrootdir * DIRENTRYSIZE)/SECTORSIZE +
900	(fat->fat_bpb.bpb_numfats * fat->fat_bpb.bpb_sectorsperfat) +
901	(fatentry - 2) * fat->fat_bpb.bpb_sectorspercluster +
902	sectoridx;
903
904    return sector;
905}
906
907/*  *********************************************************************
908    *  fat_getsubdirentry(fat,chain,idx,direntry)
909    *
910    *  This routine is similar to fat_getrootdirentry except it
911    *  works on a subdirectory.  FAT subdirectories are like files
912    *  containing directory entries, so we use the "get nth sector
913    *  in chain" routines to walk the chains of sectors reading directory
914    *  entries.
915    *
916    *  Input parameters:
917    *      fat - FAT filesystem descriptor
918    *  	   chain - FAT chain
919    *  	   idx - index of entry to read
920    *  	   direntry - place to put directory entry we read
921    *
922    *  Return value:
923    *  	   0 if ok
924    *  	   else error code
925    ********************************************************************* */
926
927static int fat_getsubdirentry(fatfs_t *fat,fatchain_t *chain,
928		       int idx,uint8_t *direntry)
929{
930    int sector;
931    int res;
932
933    sector = fat_sectoridx(fat,chain,idx/DIRPERSECTOR);
934
935    if (sector < 0) return sector;
936
937    if (fat->fat_dirsecnum != sector) {
938	res = fat_readsector(fat,sector,1,fat->fat_dirsector);
939	if (res < 0) {
940	    return res;
941	    }
942	fat->fat_dirsecnum = sector;
943	}
944
945    memcpy(direntry,&(fat->fat_dirsector[(idx % DIRPERSECTOR)*DIRENTRYSIZE]),
946	   DIRENTRYSIZE);
947
948    return 0;
949}
950
951/*  *********************************************************************
952    *  fat_getshortname(direntry,name)
953    *
954    *  Read the short filename from a directory entry, converting
955    *  it into its classic 8.3 form
956    *
957    *  Input parameters:
958    *  	   direntry - directory entry
959    *  	   name - place to put 8.3 name
960    *
961    *  Return value:
962    *  	   nothing
963    ********************************************************************* */
964
965static void fat_getshortname(uint8_t *direntry,char *name)
966{
967    int idx;
968
969    /*
970     * Collect the base file name
971     */
972
973    for (idx = DIR_NAMEOFFSET; idx < (DIR_NAMEOFFSET+DIR_NAMELEN); idx++) {
974	if (direntry[idx] == ' ') break;
975	*name++ = direntry[idx];
976	}
977
978    /*
979     * Put in the dot for the extension only if there
980     * is an extension.
981     */
982
983    if (direntry[DIR_EXTOFFSET] != ' ') *name++ = '.';
984
985    /*
986     * Collect the extension
987     */
988
989    for (idx = DIR_EXTOFFSET; idx < (DIR_EXTOFFSET+DIR_EXTLEN); idx++) {
990	if (direntry[idx] == ' ') break;
991	*name++ = direntry[idx];
992	}
993
994    *name = '\0';
995}
996
997
998/*  *********************************************************************
999    *  fat_getlongname(fat,chain,diridx,shortentry,longname)
1000    *
1001    *  Look backwards in the directory to locate the long file name
1002    *  that corresponds to the short file name passed in 'shortentry'
1003    *
1004    *  Input parameters:
1005    *  	   fat - FAT filesystem descriptor
1006    *  	   chain - chain describing current directory, or NULL
1007    *  	           if the current directory is the root directory
1008    *  	   diridx - index of the short file name
1009    *  	   shortentry - points to the short directory entry
1010    *  	   longname - buffer to receive long file name (up to 261 chars)
1011    *
1012    *  Return value:
1013    *  	   0 if ok
1014    *  	   else error code
1015    ********************************************************************* */
1016
1017static int fat_getlongname(fatfs_t *fat,fatchain_t *chain,int diridx,
1018			   uint8_t *shortentry,char *longname)
1019{
1020    int lfnidx = 1;
1021    uint8_t checksum;
1022    uint8_t direntry[DIRENTRYSIZE];
1023    int idx;
1024    char *lfnptr;
1025    int badlfn = 0;
1026
1027    *longname = '\0';
1028
1029    /*
1030     * idx is the entry # of the short name
1031     */
1032
1033    checksum = fat_checksumname(shortentry);
1034
1035    /*
1036     * Start working backwards from current entry
1037     * and collect pieces of the lfn
1038     */
1039
1040    lfnptr = longname;
1041    diridx--;
1042
1043    while (diridx >= 0) {
1044
1045	/*
1046	 * Read previous entry
1047	 */
1048
1049	if (chain) {
1050	    fat_getsubdirentry(fat,chain,diridx,direntry);
1051	    }
1052	else {
1053	    fat_getrootdirentry(fat,diridx,direntry);
1054	    }
1055
1056	/*
1057	 * Checksum must match, it must have the right entry index,
1058	 * and it must have the LFN attribute
1059	 */
1060
1061	if (DIRENTRY_CHECKSUM(direntry) != checksum) {
1062	    badlfn = 1;
1063	    break;
1064	    }
1065	if ((DIRENTRY_LFNIDX(direntry) & LFNIDX_MASK) != lfnidx) {
1066	    badlfn = 1;
1067	    break;
1068	    }
1069
1070	if (DIRENTRY_ATTRIB(direntry) != ATTRIB_LFN) {
1071	    badlfn = 1;
1072	    break;
1073	    }
1074
1075	/*
1076	 * Collect the chars from the filename.  Note we
1077	 * don't deal well with real unicode chars here.
1078	 */
1079
1080	for (idx = 0; idx < 5; idx++) {
1081	    *lfnptr++ = direntry[(idx*2)+1];
1082	    }
1083	for (idx = 0; idx < 6; idx++) {
1084	    *lfnptr++ = direntry[(idx*2)+14];
1085	    }
1086	for (idx = 0; idx < 2; idx++) {
1087	    *lfnptr++ = direntry[(idx*2)+28];
1088	    }
1089
1090	/*
1091	 * Don't go too far
1092	 */
1093
1094	if (DIRENTRY_LFNIDX(direntry) & LFNIDX_END) break;
1095	lfnidx++;
1096	if (lfnidx > LFNIDX_MAX) {
1097	    badlfn = 1;
1098	    break;
1099	    }
1100
1101	diridx--;
1102	}
1103
1104    /*
1105     * Null terminate the lfn
1106     */
1107
1108    *lfnptr = 0;
1109
1110    if (badlfn) {
1111	longname[0] = 0;
1112	return CFE_ERR_FILENOTFOUND;
1113	}
1114
1115    return 0;
1116}
1117
1118
1119/*  *********************************************************************
1120    *  fat_scandir(fat,chain,name,direntry)
1121    *
1122    *  Scan a single directory looking for a file name
1123    *
1124    *  Input parameters:
1125    *  	   fat - FAT filesystem descriptor
1126    *  	   chain - FAT chain for directory or NULL for root directory
1127    *  	   name - name of file to look for (short or long name)
1128    *  	   direntry - place to put directory entry if we find one
1129    *
1130    *  Return value:
1131    *  	   1 if name was found
1132    *      0 if name was not found
1133    *  	   else <0 is error code
1134    ********************************************************************* */
1135
1136
1137static int fat_scandir(fatfs_t *fat,fatchain_t *chain,
1138		       char *name,uint8_t *direntry)
1139{
1140    int idx;
1141    int count;
1142    char shortname[16];
1143    char longname[280];
1144
1145    /*
1146     * Get directory size
1147     */
1148
1149    if (chain) {
1150	count = (chain->fat_count * fat->fat_bpb.bpb_sectorspercluster) * DIRPERSECTOR;
1151	}
1152    else {
1153	count = (int) fat->fat_bpb.bpb_maxrootdir;
1154	}
1155
1156    /*
1157     * Scan whole directory
1158     */
1159
1160    for (idx = 0; idx < count; idx++) {
1161
1162	/*
1163	 * Get entry by root or chain depending...
1164	 */
1165
1166	if (chain) {
1167	    fat_getsubdirentry(fat,chain,idx,direntry);
1168	    }
1169	else {
1170	    fat_getrootdirentry(fat,idx,direntry);
1171	    }
1172
1173	/*
1174	 * Ignore stuff we don't want to see
1175	 */
1176
1177	if (direntry[0] == DIRENTRY_LAST) break;	/* stop if at end of dir */
1178	if (direntry[0] == DIRENTRY_DELETED) continue;	/* skip deleted entries */
1179	if (direntry[0] == DIRENTRY_PARENTDIR) continue; /* skip ./.. entries */
1180
1181	if (DIRENTRY_ATTRIB(direntry) == ATTRIB_LFN) continue;	/* skip LFNs */
1182	if (DIRENTRY_ATTRIB(direntry) & ATTRIB_LABEL) continue;	/* skip volume labels */
1183
1184	/*
1185	 * Get actual file names from directory
1186	 */
1187
1188	fat_getshortname(direntry,shortname);
1189	fat_getlongname(fat,chain,idx,direntry,longname);
1190
1191
1192	if (name) {
1193	    if (strcmpi(name,shortname) == 0) return 1;
1194	    if (longname[0] && (strcmpi(name,longname) == 0)) return 1;
1195	    }
1196	else {
1197	    xprintf("%-30s",longname[0] ? longname : shortname);
1198//	    xprintf(" Clus=%04X",DIRENTRY_STARTCLUSTER(direntry));
1199//	    xprintf(" Attrib=%02X",DIRENTRY_ATTRIB(direntry));
1200//	    xprintf(" Size=%d",DIRENTRY_FILELENGTH(direntry));
1201	    xprintf("%d",DIRENTRY_FILELENGTH(direntry));
1202	    xprintf("\n");
1203	    }
1204	}
1205
1206    return 0;
1207}
1208
1209/*  *********************************************************************
1210    *  fat_findfile(fat,name,direntry)
1211    *
1212    *  Locate a directory entry given a complete path name
1213    *
1214    *  Input parameters:
1215    *  	   fat - FAT filesystem descriptor
1216    *  	   name - name of file to locate (forward or reverse slashses ok)
1217    *  	   direntry - place to put directory entry we find
1218    *
1219    *  Return value:
1220    *  	   0 if file not found
1221    *  	   1 if file was found
1222    *  	   <0 if error occurs
1223    ********************************************************************* */
1224
1225static int fat_findfile(fatfs_t *fat,char *name,uint8_t *direntry)
1226{
1227    char *namecopy;
1228    char *namepart;
1229    char *ptr;
1230    fatchain_t chain;
1231    int res;
1232    int e;
1233
1234    /*
1235     * Copy the name, we're going to hack it up
1236     */
1237
1238    namecopy = strdup(name);
1239
1240    /*
1241     * Chew off the first piece up to the first slash.  Remove
1242     * a leading slash if it is there.
1243     */
1244
1245    ptr = namecopy;
1246
1247    if ((*ptr == '/') || (*ptr == '\\')) ptr++;
1248
1249    namepart = ptr;
1250    while (*ptr && (*ptr != '/') && (*ptr != '\\')) ptr++;
1251    if (*ptr) *ptr++ = '\0';
1252
1253    /*
1254     * Scan the root directory looking for the first piece
1255     */
1256
1257    res = fat_scandir(fat,NULL,namepart,direntry);
1258    if (res == 0) {
1259	KFREE(namecopy);
1260	return 0;		/* file not found */
1261	}
1262
1263
1264    /*
1265     * Start scanning subdirectories until we run out
1266     * of directory components.
1267     */
1268
1269    namepart = ptr;
1270    while (*ptr && (*ptr != '/') && (*ptr != '\\')) ptr++;
1271    if (*ptr) *ptr++ = '\0';
1272    if (!*namepart) namepart = NULL;
1273
1274
1275    while (namepart) {
1276
1277	/*
1278	 * Scan the subdirectory
1279	 */
1280
1281	e = DIRENTRY_STARTCLUSTER(direntry);
1282	memset(&chain,0,sizeof(chain));
1283	fat_getchain(fat,e,&chain);
1284	res = fat_scandir(fat,&chain,namepart,direntry);
1285	if (res == 0) {
1286	    break;
1287	    }
1288	fat_freechain(&chain);
1289
1290	/*
1291	 * Advance to the next piece
1292	 */
1293
1294	namepart = ptr;
1295	while (*ptr && (*ptr != '/') && (*ptr != '\\')) ptr++;
1296	if (*ptr) *ptr++ = '\0';
1297	if (!*namepart) namepart = NULL;
1298
1299	/*
1300	 * If there's more to go and we hit something that
1301	 * is not a directory, stop here.
1302	 */
1303
1304	if (namepart && !(DIRENTRY_ATTRIB(direntry) & ATTRIB_DIR)) {
1305	    res = 0;
1306	    }
1307	}
1308
1309    KFREE(namecopy);
1310
1311    /*
1312     * The last piece we enumerate has to be a file.
1313     */
1314
1315    if ((res > 0) &&
1316	(DIRENTRY_ATTRIB(direntry) & ATTRIB_DIR)) {
1317	return 0;
1318	}
1319
1320    return res;
1321}
1322
1323
1324/*  *********************************************************************
1325    *  fat_init(fat,name)
1326    *
1327    *  Create the filesystem descriptor and attach to the hardware
1328    *  device.
1329    *
1330    *  Input parameters:
1331    *  	   fat - filesystem descriptor
1332    *  	   name - hardware device name
1333    *	   part - true to look for partition tables
1334    *
1335    *  Return value:
1336    *  	   0 if ok
1337    *  	   else error code
1338    ********************************************************************* */
1339
1340static int fat_init(fatfs_t *fat,char *name,int part)
1341{
1342    int res;
1343
1344    memset(fat,0,sizeof(fatfs_t));
1345    fat->fat_dirsecnum = -1;
1346    fat->fat_fatsecnum = -1;
1347
1348    fat->fat_fh = cfe_open(name);
1349
1350    if (fat->fat_fh < 0) return fat->fat_fh;
1351
1352    res = fatfs_check_for_partition_table(fat);
1353    /* If we were able to figure it out, use that as the default */
1354    if (res >= 0) part = res;
1355
1356    if (part) {
1357	res = fat_findpart(fat);
1358	if (res < 0) {
1359	    cfe_close(fat->fat_fh);
1360	    fat->fat_fh = -1;
1361	    return res;
1362	    }
1363	}
1364
1365    res = fat_readbpb(fat);
1366    if (res != 0) {
1367	cfe_close(fat->fat_fh);
1368	fat->fat_fh = -1;
1369	return res;
1370	}
1371
1372    return 0;
1373}
1374
1375/*  *********************************************************************
1376    *  fat_uninit(fat)
1377    *
1378    *  Uninit the filesystem descriptor and release any resources
1379    *  we allocated.
1380    *
1381    *  Input parameters:
1382    *  	   fat - filesystem descriptor
1383    *
1384    *  Return value:
1385    *  	   nothing
1386    ********************************************************************* */
1387
1388static void fat_uninit(fatfs_t *fat)
1389{
1390    if (fat->fat_fh >= 0) cfe_close(fat->fat_fh);
1391    fat->fat_fh = -1;
1392}
1393
1394int fatfs_fileop_dir(void *fsctx);
1395int fatfs_fileop_dir(void *fsctx)
1396{
1397    fatfs_t *fatfs = fsctx;
1398    uint8_t direntry[32];
1399
1400    fat_scandir(fatfs,NULL,NULL,direntry);
1401
1402    return 0;
1403}
1404
1405/*  *********************************************************************
1406    *  fatfs_fileop_init(fsctx,devname)
1407    *
1408    *  Create a FAT filesystem context and open the associated
1409    *  block device.
1410    *
1411    *  Input parameters:
1412    *  	   fsctx - file system context (return pointer)
1413    *  	   devname - device name to open
1414    *	   part - true to look for a partition table
1415    *
1416    *  Return value:
1417    *  	   0 if ok, else error
1418    ********************************************************************* */
1419
1420static int fatfs_fileop_init(void **fsctx,char *devname,int part)
1421{
1422    int res;
1423    fatfs_t *fatfs;
1424
1425    /*
1426     * Allocate a file system context
1427     */
1428
1429    fatfs = (fatfs_t *) KMALLOC(sizeof(fatfs_t),0);
1430    if (!fatfs) return CFE_ERR_NOMEM;
1431
1432    /*
1433     * Open a handle to the underlying device
1434     */
1435
1436    res = fat_init(fatfs,devname,part);
1437    if (res != 0) {
1438	KFREE(fatfs);
1439	return res;
1440	}
1441
1442    *fsctx = fatfs;
1443
1444    return 0;
1445}
1446
1447/*  *********************************************************************
1448    *  fatfs_check_for_partition_table(fatfs)
1449    *
1450    *  This routine attempts to determine if the disk contains a
1451    *  partition table or if it contains a standard MS-DOS boot recod.
1452    *  We try to find both, and return what we find, or an error
1453    *  if it is still unclear.
1454    *
1455    *  Input parameters:
1456    *  	   fatfs - fat filesystem context
1457    *
1458    *  Return value:
1459    *  	   0 if no partition table
1460    *  	   1 if partition table
1461    *  	   <0 = error occured, could not tell or I/O error
1462    ********************************************************************* */
1463
1464static int fatfs_check_for_partition_table(fatfs_t *fatfs)
1465{
1466    int res;
1467    uint8_t buffer[SECTORSIZE];
1468    uint8_t *part;
1469    int idx;
1470    int foundit = 0;
1471
1472    /*
1473     * Read sector 0
1474     */
1475
1476    fatfs->fat_partstart = 0;
1477    res = fat_readsector(fatfs,0,1,buffer);
1478    if (res < 0) return res;
1479
1480    /*
1481     * Check the seal at the end of the sector.  Both
1482     * boot sector and MBR should contain this seal.
1483     */
1484    if (READWORD(buffer,BPB_SEAL) != BPB_SEAL_VALUE) {
1485	res =  CFE_ERR_BADFILESYS;
1486	return res;
1487	}
1488
1489    /*
1490     * See Microsoft Knowledgebase article # Q140418, it contains
1491     * a good description of the boot sector format.
1492     *
1493     * If the extended information is present, and SystemID is "FAT"
1494     * and the "bytes per sector" is 512, assume it's a regular boot block
1495     */
1496
1497    if (((buffer[BPB_SIGNATURE] == BPB_SIGNATURE_VALUE1) ||
1498	 (buffer[BPB_SIGNATURE] == BPB_SIGNATURE_VALUE2)) &&
1499	(memcmp(&buffer[BPB_SYSTEMID],"FAT",3) == 0) &&
1500	 (READWORD(buffer,BPB_BYTESPERSECTOR) == 512)) {
1501	/* Not partitioned */
1502	res = 0;
1503	return res;
1504	}
1505
1506    /* If no extended information is present, check a few other key values. */
1507
1508    if ((READWORD(buffer,BPB_BYTESPERSECTOR) == 512) &&
1509	(READWORD(buffer,BPB_RESERVEDSECTORS) >= 1) &&
1510	((READWORD(buffer,BPB_MEDIADESCRIPTOR) & 0xF0) == 0xF0)) {
1511	res = 0;
1512	return res;
1513	}
1514
1515    /*
1516     * If we're still confused, look for a partition table with a valid FAT
1517     * partition on it.  We might not detect a partition table that has
1518     * only non-FAT partitions on it, like a disk with all Linux partitions,
1519     * but that is fine here in the FATFS module, since we only want to
1520     * find FAT partitions anyway.
1521     */
1522    part = &buffer[PTABLE_OFFSET];
1523    for (idx = 0; idx < PTABLE_COUNT; idx++) {
1524
1525	if (((part[PTABLE_STATUS] == PTABLE_STATUS_ACTIVE) ||
1526	     (part[PTABLE_STATUS] == 0x00)) &&
1527	    ((part[PTABLE_TYPE] == PARTTYPE_FAT12) ||
1528	     (part[PTABLE_TYPE] == PARTTYPE_FAT16) ||
1529	     (part[PTABLE_TYPE] == PARTTYPE_FAT16BIG))) {
1530	    foundit = 1;
1531	    res = 1; /*Partition table present*/
1532	    break;
1533	    }
1534	part += PTABLE_SIZE;
1535	}
1536
1537    /*
1538     * If at this point we did not find what we were looking for,
1539     * return an error.
1540     */
1541    if (foundit) {
1542	res = 1; /*Partition table is present.*/
1543	}
1544    else {
1545	/*Error!  We can't decide if partition table exists or not*/
1546	res = CFE_ERR_BADFILESYS;
1547	}
1548
1549    return res;
1550}
1551
1552static int fatfs_fileop_xinit(void **fsctx,void *dev)
1553{
1554    char *devname = (char *) dev;
1555
1556    return fatfs_fileop_init(fsctx,devname,0);
1557}
1558
1559static int fatfs_fileop_pinit(void **fsctx,void *dev)
1560{
1561    char *devname = (char *) dev;
1562
1563    return fatfs_fileop_init(fsctx,devname,1);
1564}
1565
1566
1567
1568/*  *********************************************************************
1569    *  fatfs_fileop_open(ref,name)
1570    *
1571    *  Open a file on the FAT device.
1572    *
1573    *  Input parameters:
1574    *  	   ref - place to store pointer to fileinfo
1575    *      fsctx - filesystem context
1576    *  	   name - name of file to open
1577    *
1578    *  Return value:
1579    *  	   0 if ok
1580    *  	   else error code
1581    ********************************************************************* */
1582
1583static int fatfs_fileop_open(void **ref,void *fsctx,char *name,int mode)
1584{
1585    int res;
1586    uint8_t direntry[DIRENTRYSIZE];
1587    fatfile_t *ff;
1588    fatfs_t *fatfs;
1589
1590    if (mode != FILE_MODE_READ) return CFE_ERR_UNSUPPORTED;
1591
1592    fatfs = (fatfs_t *) fsctx;
1593
1594    ff = (fatfile_t *) KMALLOC(sizeof(fatfile_t),0);
1595    if (ff == NULL) return CFE_ERR_NOMEM;
1596
1597    memset(ff,0,sizeof(fatfile_t));
1598
1599    ff->ff_fat = fatfs;
1600
1601    res = fat_findfile(ff->ff_fat,name,direntry);
1602    if (res <= 0) {
1603	return CFE_ERR_FILENOTFOUND;		/* not found */
1604	}
1605
1606    /*
1607     * Okay, the file was found.  Enumerate the FAT chain
1608     * associated with this file.
1609     */
1610
1611    ff->ff_filelength = DIRENTRY_FILELENGTH(direntry);
1612
1613    ff->ff_curpos = 0;
1614    ff->ff_cursector = -1;
1615
1616    res = fat_getchain(ff->ff_fat,
1617		       DIRENTRY_STARTCLUSTER(direntry),
1618		       &(ff->ff_chain));
1619
1620    if (res < 0) {
1621	KFREE(ff);
1622	return res;
1623	}
1624
1625    /*
1626     * Return the file handle
1627     */
1628
1629
1630    fatfs->fat_refcnt++;
1631    *ref = (void *) ff;
1632    return 0;
1633}
1634
1635
1636/*  *********************************************************************
1637    *  fatfs_fileop_close(ref)
1638    *
1639    *  Close the file.
1640    *
1641    *  Input parameters:
1642    *  	   ref - pointer to open file information
1643    *
1644    *  Return value:
1645    *  	   nothing
1646    ********************************************************************* */
1647
1648static void fatfs_fileop_close(void *ref)
1649{
1650    fatfile_t *file = (fatfile_t *) ref;
1651    fatfs_t *fatctx = file->ff_fat;
1652
1653    fatctx->fat_refcnt--;
1654
1655    fat_freechain(&(file->ff_chain));
1656    KFREE(file);
1657}
1658
1659
1660/*  *********************************************************************
1661    *  fatfs_fileop_uninit(ref)
1662    *
1663    *  Uninitialize the file system.
1664    *
1665    *  Input parameters:
1666    *  	   fsctx - filesystem context
1667    *
1668    *  Return value:
1669    *  	   nothing
1670    ********************************************************************* */
1671static void fatfs_fileop_uninit(void *fsctx)
1672{
1673    fatfs_t *fatctx = (fatfs_t *) fsctx;
1674
1675    if (fatctx->fat_refcnt) {
1676	xprintf("fatfs_fileop_unint: warning: refcnt should be zero\n");
1677	}
1678
1679    fat_uninit(fatctx);
1680
1681    KFREE(fatctx);
1682}
1683
1684
1685/*  *********************************************************************
1686    *  fatfs_fileop_seek(ref,offset,how)
1687    *
1688    *  Move the file pointer within the file
1689    *
1690    *  Input parameters:
1691    *  	   ref - pointer to open file information
1692    *  	   offset - new file location or distance to move
1693    *  	   how - method for moving
1694    *
1695    *  Return value:
1696    *  	   new file offset
1697    *  	   <0 if error occured
1698    ********************************************************************* */
1699
1700static int fatfs_fileop_seek(void *ref,int offset,int how)
1701{
1702    fatfile_t *file = (fatfile_t *) ref;
1703
1704    switch (how) {
1705	case FILE_SEEK_BEGINNING:
1706	    file->ff_curpos = offset;
1707	    break;
1708	case FILE_SEEK_CURRENT:
1709	    file->ff_curpos += offset;
1710	    break;
1711	default:
1712	    break;
1713	}
1714
1715    if (file->ff_curpos >= file->ff_filelength) {
1716	file->ff_curpos = file->ff_filelength;
1717	}
1718
1719    return file->ff_curpos;
1720}
1721
1722
1723/*  *********************************************************************
1724    *  fatfs_fileop_read(ref,buf,len)
1725    *
1726    *  Read data from the file.
1727    *
1728    *  Input parameters:
1729    *  	   ref - pointer to open file information
1730    *  	   buf - buffer to read data into
1731    *  	   len - number of bytes to read
1732    *
1733    *  Return value:
1734    *  	   number of bytes read
1735    *  	   <0 if error occured
1736    *  	   0 means eof
1737    ********************************************************************* */
1738
1739static int fatfs_fileop_read(void *ref,hsaddr_t buf,int len)
1740{
1741    fatfile_t *file = (fatfile_t *) ref;
1742    int amtcopy;
1743    int ttlcopy = 0;
1744    int offset;
1745    int sector;
1746    int secidx;
1747    int origpos;
1748    int res;
1749    uint8_t temp_buf[SECTORSIZE];
1750
1751    /*
1752     * Remember orig position in case we have an error
1753     */
1754
1755    origpos = file->ff_curpos;
1756
1757    /*
1758     * bounds check the length based on the file length
1759     */
1760
1761    if ((file->ff_curpos + len) > file->ff_filelength) {
1762	len = file->ff_filelength - file->ff_curpos;
1763	}
1764
1765    res = 0;
1766
1767    /*
1768     * while ther is still data to be transferred
1769     */
1770
1771
1772    while (len) {
1773
1774	/*
1775	 * Calculate the sector offset and index in the sector
1776	 */
1777
1778	offset = file->ff_curpos % SECTORSIZE;
1779	secidx = file->ff_curpos / SECTORSIZE;
1780
1781	sector = fat_sectoridx(file->ff_fat,&(file->ff_chain),secidx);
1782
1783	if (sector < 0) {
1784	    xprintf("should not happen, sector = -1!\n");
1785	    return sector;
1786	    }
1787
1788	/*
1789	 * first transfer up to the sector boundary
1790	 */
1791
1792	amtcopy = len;
1793	if (amtcopy > (SECTORSIZE-offset)) {
1794	    amtcopy = (SECTORSIZE-offset);
1795	    }
1796
1797	/*
1798	 * If transferring exactly a sector, on a sector
1799	 * boundary, read the data directly into the user buffer
1800	 *
1801	 * Extra credit:  See if we can transfer more than one
1802	 * sector at a time, by determining if we can read a run of
1803	 * contiguous sectors (very likely)
1804	 *
1805	 * Otherwise: read into the sector buffer and
1806	 * transfer the data to user memory.
1807	 */
1808
1809	if ((offset == 0) && (amtcopy == SECTORSIZE)) {
1810	    res = fat_readsector(file->ff_fat,sector,1,temp_buf);
1811	    if (res < 0) {
1812		xprintf("I/O error!\n");
1813		break;
1814		}
1815	    hs_memcpy_to_hs(buf,temp_buf,amtcopy);
1816	    }
1817	else {
1818	    if (file->ff_cursector != sector) {
1819		res = fat_readsector(file->ff_fat,sector,1,file->ff_sector);
1820		if (res < 0) {
1821		    break;
1822		    }
1823		file->ff_cursector = sector;
1824		}
1825	    hs_memcpy_to_hs(buf,&(file->ff_sector[offset]),amtcopy);
1826	    }
1827
1828	/*
1829	 * Adjust/update all our pointers.
1830	 */
1831
1832	buf += amtcopy;
1833	file->ff_curpos += amtcopy;
1834	ttlcopy += amtcopy;
1835	len -= amtcopy;
1836
1837	/*
1838	 * see if we ran off the end of the file.  Should not
1839	 * be necessary.
1840	 */
1841
1842	if (file->ff_curpos >= file->ff_filelength) {
1843	    /* should not be necessary */
1844	    break;
1845	    }
1846	}
1847
1848    /*
1849     * If an error occured, get out now.
1850     */
1851
1852    if (res < 0) {
1853	file->ff_curpos = origpos;
1854	return res;
1855	}
1856
1857    return ttlcopy;
1858
1859}
1860
1861static int fatfs_fileop_write(void *ref,hsaddr_t buf,int len)
1862{
1863    return CFE_ERR_UNSUPPORTED;
1864}
1865
1866
1867#if 0
1868
1869void main(int argc,char *argv[])
1870{
1871    fatfs_t fat;
1872    int idx;
1873    unsigned int e;
1874    uint8_t direntry[DIRENTRYSIZE];
1875    fatchain_t chain;
1876    int res;
1877
1878
1879    fat_init(&fat,"floppy.raw");
1880
1881
1882    if (fat_readbpb(&fat) == 0) {
1883	fat_dumpbpb(&fat.fat_bpb);
1884	}
1885
1886#if 0
1887    for (idx = 0; idx < (int) fat.fat_bpb.bpb_maxrootdir; idx++) {
1888	fat_getrootdirentry(&fat,idx,direntry);
1889	if (direntry[0] == 0) break;
1890	if (direntry[0] == 0xE5) continue;
1891	xprintf("%3d: ",idx);
1892	fat_dumpdirentry(direntry);
1893	}
1894#endif
1895
1896    fat_scandir(&fat,NULL,NULL,direntry);
1897
1898    for (e = 0x150; e < 0x160; e++) {
1899	xprintf("Entry %03X is %03X\n",e,fat_getfatentry(&fat,e));
1900	}
1901
1902#if 0
1903    e = 0x36E;
1904    while (e != 0xFFF) {
1905	e = fat_getfatentry(&fat,e);
1906	xprintf("%03X ",e);
1907	}
1908#endif
1909
1910
1911    xprintf("\n\n");
1912    e = 0x36E;
1913    memset(&chain,0,sizeof(chain));
1914    fat_getchain(&fat,e,&chain);
1915    fat_scandir(&fat,&chain,NULL,direntry);
1916    fat_freechain(&chain);
1917
1918    xprintf("\n\n");
1919    e = 0x36F;
1920    memset(&chain,0,sizeof(chain));
1921    fat_getchain(&fat,e,&chain);
1922    fat_scandir(&fat,&chain,NULL,direntry);
1923    fat_freechain(&chain);
1924
1925    xprintf("\n\n");
1926    e = 0x370;
1927    memset(&chain,0,sizeof(chain));
1928    fat_getchain(&fat,e,&chain);
1929    fat_scandir(&fat,&chain,NULL,direntry);
1930    fat_freechain(&chain);
1931
1932    xprintf("\n\n");
1933
1934    res = fat_findfile(&fat,argc > 1 ? argv[1] : "/usr/local/include/ansidecl.h",direntry);
1935    xprintf("res = %d\n",res);
1936
1937    if (res) fat_dumpdirentry(direntry);
1938
1939    close(fat.fat_fh);
1940}
1941
1942void main(int argc,char *argv[])
1943{
1944    void *ref;
1945    int res;
1946    char buffer[257];
1947    int total = 0;
1948
1949//    res = fatfs_fileop_open(&ref,"floppy.raw:/usr/local/include/bfdlink.h");
1950    res = fatfs_fileop_open(&ref,"floppy.raw:/idedrv.h");
1951
1952    if (res != 0) {
1953	xprintf("Could not open file: %d\n",res);
1954	exit(1);
1955	}
1956
1957    for (;;) {
1958	res = fatfs_fileop_read(ref,buffer,39);
1959//    xprintf("read returned %d\n",res);
1960	if (res <= 0) break;
1961
1962	if (res > 0) {
1963	    total += res;
1964	    buffer[res] = 0;
1965	    xprintf("%s",buffer);
1966	    }
1967	}
1968
1969    if (res < 0) xprintf("error! \n");
1970
1971    xprintf("[total %d]\n",total);
1972
1973    fatfs_fileop_close(ref);
1974
1975    exit(0);
1976
1977}
1978
1979#endif
1980