1 /* *********************************************************************
2 * Broadcom Common Firmware Environment (CFE)
4 * FAT file system File: cfe_fatfs.c
6 * This module knows how to read files from a FAT formatted
7 * file system (12 or 16 bit fats only for now)
9 * Eventually, we'll also include support for the FAT Translation
10 * Layer (FTL) on PCMCIA flash file systems.
12 * Author: Mitch Lichtenberg (mpl@broadcom.com)
14 *********************************************************************
16 * Copyright 2000,2001,2002,2003
17 * Broadcom Corporation. All rights reserved.
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.
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.
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.
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 ********************************************************************* */
52 #include "lib_types.h"
53 #include "lib_string.h"
54 #include "lib_queue.h"
55 #include "lib_malloc.h"
56 #include "lib_printf.h"
58 #include "cfe_error.h"
59 #include "cfe_fileops.h"
61 #include "cfe_devfuncs.h"
63 #include "cfe_loader.h"
68 /* *********************************************************************
70 ********************************************************************* */
72 #define SECTORSIZE 512
73 #define DIRENTRYSIZE 32
74 #define DIRPERSECTOR (SECTORSIZE/DIRENTRYSIZE)
76 /*#define _FATFS_DEBUG_*/
79 * Bios Parameter Block offsets and values
82 #define BPB_JMPINSTR 0x00
83 #define BPB_JMPINSTR_VALUE 0xEB
84 #define BPB_JMPINSTR_VALUE2 0xE9
85 #define BPB_SEAL 0x1FE
86 #define BPB_SEAL_VALUE 0xAA55
88 #define BPB_BYTESPERSECTOR 0x0B
89 #define BPB_SECTORSPERCLUSTER 0x0D
90 #define BPB_RESERVEDSECTORS 0x0E
91 #define BPB_NUMFATS 0x10
92 #define BPB_MAXROOTDIR 0x11
93 #define BPB_TOTALSECTORS 0x13
94 #define BPB_SECTORSPERFAT 0x16
95 #define BPB_SECTORSPERTRACK 0x18
96 #define BPB_NUMHEADS 0x1A
97 #define BPB_HIDDENSECTORS 0x1C
98 #define BPB_SYSTEMID 54
99 #define BPB_MEDIADESCRIPTOR 21
100 #define BPB_SIGNATURE 38
101 #define BPB_SIGNATURE_VALUE1 0x28
102 #define BPB_SIGNATURE_VALUE2 0x29
108 #define PARTTYPE_EMPTY 0
109 #define PARTTYPE_FAT12 1
110 #define PARTTYPE_FAT16 4
111 #define PARTTYPE_FAT16BIG 6
112 #define PARTTYPE_FAT32 0x0B
115 * Partition table offsets
117 #define PTABLE_STATUS 0
118 #define PTABLE_STARTHEAD 1
119 #define PTABLE_STARTSECCYL 2 /* 2 bytes */
120 #define PTABLE_TYPE 4
121 #define PTABLE_ENDHEAD 5
122 #define PTABLE_ENDSECCYL 6 /* 2 bytes */
123 #define PTABLE_BOOTSECTOR 8 /* 4 bytes */
124 #define PTABLE_NUMSECTORS 12 /* 4 bytes */
126 #define PTABLE_SIZE 16
127 #define PTABLE_COUNT 4
128 #define PTABLE_OFFSET (512-2-(PTABLE_COUNT*PTABLE_SIZE))
130 #define PTABLE_STATUS_ACTIVE 0x80
133 * Directory attributes
136 #define ATTRIB_NORMAL 0x00
137 #define ATTRIB_READONLY 0x01
138 #define ATTRIB_HIDDEN 0x02
139 #define ATTRIB_SYSTEM 0x04
140 #define ATTRIB_LABEL 0x08
141 #define ATTRIB_DIR 0x10
142 #define ATTRIB_ARCHIVE 0x20
144 #define ATTRIB_LFN 0x0F
147 * Macros to read fields in directory & BPB entries
150 #define READWORD(buffer,x) (((unsigned int) (buffer)[(x)]) | \
151 (((unsigned int) (buffer)[(x)+1]) << 8))
153 #define READWORD32(buffer,x) (READWORD(buffer,(x)) | (READWORD(buffer,(x)+2) << 16))
155 #define READBYTE(buffer,x) ((unsigned int) (buffer)[(x)])
158 * Directory entry offsets and values
161 #define DIR_CHECKSUM 13
162 #define DIR_FILELENGTH 28
163 #define DIR_STARTCLUSTER 26
164 #define DIR_ATTRIB 11
165 #define DIR_NAMEOFFSET 0
166 #define DIR_NAMELEN 8
167 #define DIR_EXTOFFSET 8
170 #define DIRENTRY_CHECKSUM(e) READBYTE(e,DIR_CHECKSUM)
171 #define DIRENTRY_FILELENGTH(e) READWORD32(e,DIR_FILELENGTH)
172 #define DIRENTRY_STARTCLUSTER(e) READWORD(e,DIR_STARTCLUSTER)
173 #define DIRENTRY_ATTRIB(e) READBYTE(e,DIR_ATTRIB)
175 #define DIRENTRY_LAST 0
176 #define DIRENTRY_DELETED 0xE5
177 #define DIRENTRY_PARENTDIR 0x2E
179 #define DIRENTRY_LFNIDX(e) READBYTE(e,0)
180 #define LFNIDX_MASK 0x1F
181 #define LFNIDX_END 0x40
182 #define LFNIDX_MAX 20
184 /* *********************************************************************
186 ********************************************************************* */
192 typedef struct bpb_s
{
193 unsigned int bpb_bytespersector
;
194 unsigned int bpb_sectorspercluster
;
195 unsigned int bpb_reservedsectors
;
196 unsigned int bpb_numfats
;
197 unsigned int bpb_maxrootdir
;
198 unsigned int bpb_totalsectors
;
199 unsigned int bpb_sectorsperfat
;
200 unsigned int bpb_sectorspertrack
;
201 unsigned int bpb_numheads
;
202 unsigned int bpb_hiddensectors
;
206 * FAT Filesystem descriptor - contains working information
207 * about an "open" file system
210 typedef struct fatfs_s
{
216 uint8_t fat_dirsector
[SECTORSIZE
];
218 uint8_t fat_fatsector
[SECTORSIZE
];
223 * FAT Chain - describes a series of FAT entries
226 typedef struct fatchain_s
{
228 uint16_t *fat_entries
;
233 * FAT File descriptor - contains working information
234 * about an open file (including the filesystem info)
237 typedef struct fatfile_s
{
243 uint8_t ff_sector
[SECTORSIZE
];
246 /* *********************************************************************
248 ********************************************************************* */
250 static int fatfs_fileop_xinit(void **fsctx
,void *filename
);
251 static int fatfs_fileop_pinit(void **fsctx
,void *filename
);
252 static int fatfs_fileop_open(void **ref
,void *fsctx
,char *filename
,int mode
);
253 static int fatfs_fileop_read(void *ref
,uint8_t *buf
,int len
);
254 static int fatfs_fileop_write(void *ref
,uint8_t *buf
,int len
);
255 static int fatfs_fileop_seek(void *ref
,int offset
,int how
);
256 static void fatfs_fileop_close(void *ref
);
257 static void fatfs_fileop_uninit(void *fsctx
);
259 static int fatfs_check_for_partition_table(fatfs_t
*fatfs
);
261 /* *********************************************************************
262 * FAT fileio dispatch table
263 ********************************************************************* */
266 * Raw FAT (no partition table) - used only on floppies
269 const fileio_dispatch_t fatfs_fileops
= {
282 * Partitioned FAT - used on Zip disks, removable hard disks,
283 * hard disks, flash cards, etc.
286 const fileio_dispatch_t pfatfs_fileops
= {
299 /* *********************************************************************
300 * fat_readsector(fatfs,sector,numsec,buffer)
302 * Read one or more sectors from the disk into memory
305 * fatfs - fat filesystem descriptor
306 * sector - sector number
307 * numsec - number of sectors to read
308 * buffer - buffer to read sectors into
313 ********************************************************************* */
315 static int fat_readsector(fatfs_t
*fatfs
,int sector
,int numsec
,uint8_t *buffer
)
319 res
= cfe_readblk(fatfs
->fat_fh
,(sector
+fatfs
->fat_partstart
)*SECTORSIZE
,
320 buffer
,numsec
*SECTORSIZE
);
322 if (res
!= numsec
*SECTORSIZE
) return CFE_ERR_IOERR
;
328 /* *********************************************************************
331 * Debug function; display fields in a BPB
334 * bpb - BIOS parameter block structure
338 ********************************************************************* */
341 static void fat_dumpbpb(bpb_t
*bpb
)
343 xprintf("Bytes per sector %d\n",bpb
->bpb_bytespersector
);
344 xprintf("Sectors per cluster %d\n",bpb
->bpb_sectorspercluster
);
345 xprintf("Reserved sectors %d\n",bpb
->bpb_reservedsectors
);
346 xprintf("Number of FATs %d\n",bpb
->bpb_numfats
);
347 xprintf("Root dir entries %d\n",bpb
->bpb_maxrootdir
);
348 xprintf("Total sectors %d\n",bpb
->bpb_totalsectors
);
349 xprintf("Sectors per FAT %d\n",bpb
->bpb_sectorsperfat
);
350 xprintf("Sectors per track %d\n",bpb
->bpb_sectorspertrack
);
351 xprintf("Number of heads %d\n",bpb
->bpb_numheads
);
352 xprintf("Hidden sectors %d\n",bpb
->bpb_hiddensectors
);
356 /* *********************************************************************
357 * fat_findpart(fatfs)
359 * For partitioned disks, locate the active partition
360 * and set "fat_partstart" accordingly
363 * fatfs - FAT filesystem descriptor
366 * 0 if we found a valid partition table; else error code
367 ********************************************************************* */
369 static int fat_findpart(fatfs_t
*fatfs
)
371 uint8_t buffer
[SECTORSIZE
];
376 fatfs
->fat_partstart
= 0; /* make sure we get real boot sector */
377 res
= fat_readsector(fatfs
,0,1,buffer
);
378 if (res
< 0) return res
;
381 * Normally you're supposed to check for a JMP instruction.
382 * At least that's what many people do. Flash MBRs don't start
383 * with JMP instructions, so just look for the seal.
386 * if (READBYTE(buffer,BPB_JMPINSTR) != BPB_JMPINSTR_VALUE) {
387 * return CFE_ERR_BADFILESYS;
392 * Check the seal at the end of th sector
395 if (READWORD(buffer
,BPB_SEAL
) != BPB_SEAL_VALUE
) return CFE_ERR_BADFILESYS
;
398 * Look for an active FAT partition. The partition we want must
399 * be the active one. We do not deal with extended partitions
400 * here. Hey, this is supposed to be boot code!
403 part
= &buffer
[PTABLE_OFFSET
];
404 for (idx
= 0; idx
< PTABLE_COUNT
; idx
++) {
405 if ((part
[PTABLE_STATUS
] == PTABLE_STATUS_ACTIVE
) &&
406 ((part
[PTABLE_TYPE
] == PARTTYPE_FAT12
) ||
407 (part
[PTABLE_TYPE
] == PARTTYPE_FAT16
) ||
408 (part
[PTABLE_TYPE
] == PARTTYPE_FAT16BIG
))) {
415 if (idx
== PTABLE_COUNT
) return CFE_ERR_BADFILESYS
;
418 * The info we want is really just the pointer to the
419 * boot (BPB) sector. Get that and we'll use it for an
420 * offset into the disk later.
423 fatfs
->fat_partstart
= READWORD32(part
,PTABLE_BOOTSECTOR
);
429 /* *********************************************************************
432 * Read and internalize the BIOS Parameter Block
435 * fatfs - FAT filesystem descriptor
439 * else error code (usually, BPB is not valid)
440 ********************************************************************* */
442 static int fat_readbpb(fatfs_t
*fatfs
)
444 uint8_t buffer
[SECTORSIZE
];
447 res
= fat_readsector(fatfs
,0,1,buffer
);
448 if (res
< 0) return res
;
450 if (READBYTE(buffer
,BPB_JMPINSTR
) != BPB_JMPINSTR_VALUE
) return CFE_ERR_BADFILESYS
;
451 if (READWORD(buffer
,BPB_SEAL
) != BPB_SEAL_VALUE
) return CFE_ERR_BADFILESYS
;
453 fatfs
->fat_bpb
.bpb_bytespersector
= READWORD(buffer
,BPB_BYTESPERSECTOR
);
454 fatfs
->fat_bpb
.bpb_sectorspercluster
= READBYTE(buffer
,BPB_SECTORSPERCLUSTER
);
455 fatfs
->fat_bpb
.bpb_reservedsectors
= READWORD(buffer
,BPB_RESERVEDSECTORS
);
456 fatfs
->fat_bpb
.bpb_numfats
= READBYTE(buffer
,BPB_NUMFATS
);
457 fatfs
->fat_bpb
.bpb_maxrootdir
= READWORD(buffer
,BPB_MAXROOTDIR
);
458 fatfs
->fat_bpb
.bpb_totalsectors
= READWORD(buffer
,BPB_TOTALSECTORS
);
459 fatfs
->fat_bpb
.bpb_sectorsperfat
= READWORD(buffer
,BPB_SECTORSPERFAT
);
460 fatfs
->fat_bpb
.bpb_sectorspertrack
= READWORD(buffer
,BPB_SECTORSPERTRACK
);
461 fatfs
->fat_bpb
.bpb_numheads
= READWORD(buffer
,BPB_NUMHEADS
);
462 fatfs
->fat_bpb
.bpb_hiddensectors
= READWORD(buffer
,BPB_HIDDENSECTORS
);
464 fatfs
->fat_twelvebit
= 1;
465 if (memcmp(&buffer
[0x36],"FAT16 ",8) == 0) fatfs
->fat_twelvebit
= 0;
467 if (fatfs
->fat_bpb
.bpb_bytespersector
!= SECTORSIZE
) return CFE_ERR_BADFILESYS
;
468 if (fatfs
->fat_bpb
.bpb_numfats
> 2) return CFE_ERR_BADFILESYS
;
472 fat_dumpbpb(&(fatfs
->fat_bpb
));
480 /* *********************************************************************
481 * fat_getentry(fatfs,entry)
483 * Read a FAT entry. This is more involved than you'd think,
484 * since we have to deal with 12 and 16 (and someday 32) bit FATs,
485 * and the nasty case where a 12-bit FAT entry crosses a sector
489 * fatfs - FAT filesystem descriptor
490 * entry - index of FAT entry
493 * FAT entry, or <0 if an error occured
494 ********************************************************************* */
496 static int fat_getfatentry(fatfs_t
*fatfs
,int entry
)
505 fatstart
= fatfs
->fat_bpb
.bpb_reservedsectors
;
507 if (fatfs
->fat_twelvebit
) {
510 byteoffset
= ((entry
& ~1) * 3) / 2;
511 fatsect
= byteoffset
/ SECTORSIZE
;
512 fatoffset
= byteoffset
% SECTORSIZE
;
514 if (fatfs
->fat_fatsecnum
!= fatsect
) {
515 res
= fat_readsector(fatfs
,fatsect
+fatstart
,1,fatfs
->fat_fatsector
);
519 fatfs
->fat_fatsecnum
= fatsect
;
522 b1
= fatfs
->fat_fatsector
[fatoffset
];
524 if ((fatoffset
+1) >= SECTORSIZE
) {
525 res
= fat_readsector(fatfs
,fatsect
+1+fatstart
,1,fatfs
->fat_fatsector
);
529 fatfs
->fat_fatsecnum
= fatsect
+1;
530 fatoffset
-= SECTORSIZE
;
533 b2
= fatfs
->fat_fatsector
[fatoffset
+1];
535 if ((fatoffset
+2) >= SECTORSIZE
) {
536 res
= fat_readsector(fatfs
,fatsect
+1+fatstart
,1,fatfs
->fat_fatsector
);
540 fatfs
->fat_fatsecnum
= fatsect
+1;
541 fatoffset
-= SECTORSIZE
;
544 b3
= fatfs
->fat_fatsector
[fatoffset
+2];
547 return ((unsigned int) b3
<< 4) + ((unsigned int) (b2
& 0xF0) >> 4);
550 return ((unsigned int) (b2
& 0x0F) << 8) + ((unsigned int) b1
);
555 byteoffset
= entry
* 2;
556 fatsect
= byteoffset
/ SECTORSIZE
;
557 fatoffset
= byteoffset
% SECTORSIZE
;
559 if (fatfs
->fat_fatsecnum
!= fatsect
) {
560 res
= fat_readsector(fatfs
,fatsect
+fatstart
,1,fatfs
->fat_fatsector
);
564 fatfs
->fat_fatsecnum
= fatsect
;
567 b1
= fatfs
->fat_fatsector
[fatoffset
];
568 b2
= fatfs
->fat_fatsector
[fatoffset
+1];
569 return ((unsigned int) b1
) + (((unsigned int) b2
) << 8);
573 /* *********************************************************************
574 * fat_getrootdirentry(fatfs,entryidx,entry)
576 * Read a root directory entry. The FAT12/16 root directory
577 * is a contiguous group of sectors, whose size is specified in
578 * the BPB. This routine just digs out an entry from there
581 * fatfs - FAT filesystem descriptor
582 * entryidx - 0-based entry index to read
583 * entry - pointer to directory entry (32 bytes)
587 * <0 if error occured
588 ********************************************************************* */
590 static int fat_getrootdirentry(fatfs_t
*fatfs
,int entryidx
,uint8_t *entry
)
597 if (entryidx
>= fatfs
->fat_bpb
.bpb_maxrootdir
) {
598 memset(entry
,0,DIRENTRYSIZE
);
599 return CFE_ERR_INV_PARAM
;
602 rootdirstart
= fatfs
->fat_bpb
.bpb_reservedsectors
+
603 fatfs
->fat_bpb
.bpb_numfats
* fatfs
->fat_bpb
.bpb_sectorsperfat
;
605 rootdirsize
= fatfs
->fat_bpb
.bpb_maxrootdir
/ DIRENTRYSIZE
;
607 dirsecnum
= rootdirstart
+ entryidx
/ DIRPERSECTOR
;
609 if (fatfs
->fat_dirsecnum
!= dirsecnum
) {
610 res
= fat_readsector(fatfs
,dirsecnum
,1,fatfs
->fat_dirsector
);
614 fatfs
->fat_dirsecnum
= dirsecnum
;
617 memcpy(entry
,&(fatfs
->fat_dirsector
[(entryidx
% DIRPERSECTOR
)*DIRENTRYSIZE
]),
623 /* *********************************************************************
624 * fat_checksumname(name)
626 * Calculate the "long filename" checksum for a given short name.
627 * All LFN directory entries associated with the short name are
628 * given the same checksum byte, to help keep the long name
632 * name - pointer to 32-byte directory entry
636 ********************************************************************* */
638 static uint8_t fat_checksumname(uint8_t *name
)
644 for (idx
= 0; idx
< 11; idx
++) {
645 newbit
= (sum
& 1) ? 0x80 : 0x00;
655 void fat_dumpdirentry(uint8_t *entry
);
656 void fat_dumpdirentry(uint8_t *entry
)
661 if (entry
[11] != ATTRIB_LFN
) {
662 memcpy(name
,entry
,11);
664 xprintf("%s %02X %04X %d\n",
665 name
,DIRENTRY_ATTRIB(entry
),
666 DIRENTRY_STARTCLUSTER(entry
),
667 DIRENTRY_FILELENGTH(entry
));
670 for (idx
= 0; idx
< 5; idx
++) {
671 name
[idx
] = entry
[(idx
*2)+1];
673 for (idx
= 0; idx
< 6; idx
++) {
674 name
[idx
+5] = entry
[(idx
*2)+14];
676 for (idx
= 0; idx
< 2; idx
++) {
677 name
[idx
+11] = entry
[(idx
*2)+28];
680 xprintf("%02X: %s %04X cksum %02X\n",entry
[0],
681 name
,READWORD(entry
,0x1A),entry
[13]);
687 /* *********************************************************************
688 * fat_walkfatchain(fat,start,arg,func)
690 * Walk a FAT chain, calling a callback routine for each entry
691 * we find along the way.
694 * fat - FAT filesystem descriptor
695 * start - starting FAT entry (from the directory, usually)
696 * arg - argument to pass to callback routine
697 * func - function to call for each FAT entry
700 * 0 if all elements processed
701 * <0 if error occured
702 * >0 if callback routine returned a nonzero value
703 ********************************************************************* */
705 static int fat_walkfatchain(fatfs_t
*fat
,int start
,
707 int (*func
)(fatfs_t
*fat
,
719 /* Note: ending FAT entry can be 0x(F)FF8..0x(F)FFF. We assume that the
720 'getfatentry' call won't return values above that. */
721 if (fat
->fat_twelvebit
) {
728 while (e
< ending_e
) {
729 res
= (*func
)(fat
,e
,prev_e
,arg
);
732 e
= fat_getfatentry(fat
,e
);
739 /* *********************************************************************
740 * fat_getwalkfunc(fat,e,prev_e,arg)
742 * Callback routien to collect all of the FAT entries into
743 * a FAT chain descriptor
746 * fat - FAT filesystem descriptor
748 * prev_e - previous entry (0 if first entry)
749 * arg - argument passed to fat_walkfatchain
753 * else value to return from fat_walkfatchain
754 ********************************************************************* */
756 static int fat_getwalkfunc(fatfs_t
*fat
,int e
,
757 int prev_e
,void *arg
)
759 fatchain_t
*chain
= arg
;
761 if (chain
->fat_entries
) {
762 chain
->fat_entries
[chain
->fat_count
] = (uint16_t) e
;
770 /* *********************************************************************
771 * fat_getchain(fat,start,chain)
773 * Walk an entire FAT chain and remember the chain in a
774 * FAT chain descriptor
777 * fat - FAT filesystem descriptor
778 * start - starting FAT entry
779 * chain - chain descriptor
784 ********************************************************************* */
786 static int fat_getchain(fatfs_t
*fat
,int start
,fatchain_t
*chain
)
790 chain
->fat_entries
= NULL
;
791 chain
->fat_count
= 0;
792 chain
->fat_start
= start
;
795 * walk once to count the entries.
797 * For regular files, you probably don't have to do this
798 * since you can predict exactly how many FAT entries
799 * there are given the file length.
802 res
= fat_walkfatchain(fat
,start
,chain
,fat_getwalkfunc
);
803 if (res
< 0) return res
;
806 * allocate space for the entries. Include one extra
807 * slot for the first entry, since the first entry
808 * does not actually appear in the FAT (the fat is
809 * only the 'next' pointers).
812 if (chain
->fat_count
== 0) return 0;
813 chain
->fat_entries
= KMALLOC((chain
->fat_count
+1)*sizeof(uint16_t),0);
814 chain
->fat_count
= 0;
817 * walk again to collect entries
819 res
= fat_walkfatchain(fat
,start
,chain
,fat_getwalkfunc
);
820 if (res
< 0) return res
;
822 return chain
->fat_count
;
826 /* *********************************************************************
827 * fat_freechain(chain)
829 * Free memory associated with a FAT chain
832 * chain - chain descriptor
836 ********************************************************************* */
838 static void fat_freechain(fatchain_t
*chain
)
840 if (chain
->fat_entries
) {
841 KFREE(chain
->fat_entries
);
842 chain
->fat_entries
= NULL
;
844 chain
->fat_count
= 0;
847 /* *********************************************************************
848 * fat_clusteridx(fat,chain,idx)
850 * Index into a FAT chain and return the nth cluster number
854 * fat - fat filesystem descriptor
855 * chain - chain descriptor
856 * idx - index into FAT chain
859 * FAT entry at the nth index, or
860 * <0 if an error occured
861 ********************************************************************* */
862 static int fat_clusteridx(fatfs_t
*fat
,fatchain_t
*chain
,int idx
)
864 if (idx
>= chain
->fat_count
) return CFE_ERR_INV_PARAM
; /* error! */
865 return (int) (unsigned int) chain
->fat_entries
[idx
];
868 /* *********************************************************************
869 * fat_sectoridx(fat,chain,idx)
871 * Return the sector nunber of the nth sector in a given
872 * FAT chain. This routine knows how to translate cluster
873 * numbers into sector numbers.
876 * fat - FAT filesystem descriptor
878 * idx - index of which sector to find
882 * <0 if an error occured
883 ********************************************************************* */
884 static int fat_sectoridx(fatfs_t
*fat
,fatchain_t
*chain
,int idx
)
891 clusteridx
= idx
/ fat
->fat_bpb
.bpb_sectorspercluster
;
892 sectoridx
= idx
% fat
->fat_bpb
.bpb_sectorspercluster
;
894 fatentry
= fat_clusteridx(fat
,chain
,clusteridx
);
896 if (fatentry
< 0) xprintf("ran off end of fat chain!\n");
897 if (fatentry
< 2) xprintf("fat entries should be >= 2\n");
899 sector
= fat
->fat_bpb
.bpb_reservedsectors
+
900 (fat
->fat_bpb
.bpb_maxrootdir
* DIRENTRYSIZE
)/SECTORSIZE
+
901 (fat
->fat_bpb
.bpb_numfats
* fat
->fat_bpb
.bpb_sectorsperfat
) +
902 (fatentry
- 2) * fat
->fat_bpb
.bpb_sectorspercluster
+
908 /* *********************************************************************
909 * fat_getsubdirentry(fat,chain,idx,direntry)
911 * This routine is similar to fat_getrootdirentry except it
912 * works on a subdirectory. FAT subdirectories are like files
913 * containing directory entries, so we use the "get nth sector
914 * in chain" routines to walk the chains of sectors reading directory
918 * fat - FAT filesystem descriptor
920 * idx - index of entry to read
921 * direntry - place to put directory entry we read
926 ********************************************************************* */
928 static int fat_getsubdirentry(fatfs_t
*fat
,fatchain_t
*chain
,
929 int idx
,uint8_t *direntry
)
934 sector
= fat_sectoridx(fat
,chain
,idx
/DIRPERSECTOR
);
936 if (sector
< 0) return sector
;
938 if (fat
->fat_dirsecnum
!= sector
) {
939 res
= fat_readsector(fat
,sector
,1,fat
->fat_dirsector
);
943 fat
->fat_dirsecnum
= sector
;
946 memcpy(direntry
,&(fat
->fat_dirsector
[(idx
% DIRPERSECTOR
)*DIRENTRYSIZE
]),
952 /* *********************************************************************
953 * fat_getshortname(direntry,name)
955 * Read the short filename from a directory entry, converting
956 * it into its classic 8.3 form
959 * direntry - directory entry
960 * name - place to put 8.3 name
964 ********************************************************************* */
966 static void fat_getshortname(uint8_t *direntry
,char *name
)
971 * Collect the base file name
974 for (idx
= DIR_NAMEOFFSET
; idx
< (DIR_NAMEOFFSET
+DIR_NAMELEN
); idx
++) {
975 if (direntry
[idx
] == ' ') break;
976 *name
++ = direntry
[idx
];
980 * Put in the dot for the extension only if there
984 if (direntry
[DIR_EXTOFFSET
] != ' ') *name
++ = '.';
987 * Collect the extension
990 for (idx
= DIR_EXTOFFSET
; idx
< (DIR_EXTOFFSET
+DIR_EXTLEN
); idx
++) {
991 if (direntry
[idx
] == ' ') break;
992 *name
++ = direntry
[idx
];
999 /* *********************************************************************
1000 * fat_getlongname(fat,chain,diridx,shortentry,longname)
1002 * Look backwards in the directory to locate the long file name
1003 * that corresponds to the short file name passed in 'shortentry'
1006 * fat - FAT filesystem descriptor
1007 * chain - chain describing current directory, or NULL
1008 * if the current directory is the root directory
1009 * diridx - index of the short file name
1010 * shortentry - points to the short directory entry
1011 * longname - buffer to receive long file name (up to 261 chars)
1016 ********************************************************************* */
1018 static int fat_getlongname(fatfs_t
*fat
,fatchain_t
*chain
,int diridx
,
1019 uint8_t *shortentry
,char *longname
)
1023 uint8_t direntry
[DIRENTRYSIZE
];
1031 * idx is the entry # of the short name
1034 checksum
= fat_checksumname(shortentry
);
1037 * Start working backwards from current entry
1038 * and collect pieces of the lfn
1044 while (diridx
> 0) {
1047 * Read previous entry
1051 fat_getsubdirentry(fat
,chain
,diridx
,direntry
);
1054 fat_getrootdirentry(fat
,diridx
,direntry
);
1058 * Checksum must match, it must have the right entry index,
1059 * and it must have the LFN attribute
1062 if (DIRENTRY_CHECKSUM(direntry
) != checksum
) {
1066 if ((DIRENTRY_LFNIDX(direntry
) & LFNIDX_MASK
) != lfnidx
) {
1071 if (DIRENTRY_ATTRIB(direntry
) != ATTRIB_LFN
) {
1077 * Collect the chars from the filename. Note we
1078 * don't deal well with real unicode chars here.
1081 for (idx
= 0; idx
< 5; idx
++) {
1082 *lfnptr
++ = direntry
[(idx
*2)+1];
1084 for (idx
= 0; idx
< 6; idx
++) {
1085 *lfnptr
++ = direntry
[(idx
*2)+14];
1087 for (idx
= 0; idx
< 2; idx
++) {
1088 *lfnptr
++ = direntry
[(idx
*2)+28];
1095 if (DIRENTRY_LFNIDX(direntry
) & LFNIDX_END
) break;
1097 if (lfnidx
> LFNIDX_MAX
) {
1106 * Null terminate the lfn
1113 return CFE_ERR_FILENOTFOUND
;
1120 /* *********************************************************************
1121 * fat_scandir(fat,chain,name,direntry)
1123 * Scan a single directory looking for a file name
1126 * fat - FAT filesystem descriptor
1127 * chain - FAT chain for directory or NULL for root directory
1128 * name - name of file to look for (short or long name)
1129 * direntry - place to put directory entry if we find one
1132 * 1 if name was found
1133 * 0 if name was not found
1134 * else <0 is error code
1135 ********************************************************************* */
1138 static int fat_scandir(fatfs_t
*fat
,fatchain_t
*chain
,
1139 char *name
,uint8_t *direntry
)
1147 * Get directory size
1151 count
= (chain
->fat_count
* fat
->fat_bpb
.bpb_sectorspercluster
) * DIRPERSECTOR
;
1154 count
= (int) fat
->fat_bpb
.bpb_maxrootdir
;
1158 * Scan whole directory
1161 for (idx
= 0; idx
< count
; idx
++) {
1164 * Get entry by root or chain depending...
1168 fat_getsubdirentry(fat
,chain
,idx
,direntry
);
1171 fat_getrootdirentry(fat
,idx
,direntry
);
1175 * Ignore stuff we don't want to see
1178 if (direntry
[0] == DIRENTRY_LAST
) break; /* stop if at end of dir */
1179 if (direntry
[0] == DIRENTRY_DELETED
) continue; /* skip deleted entries */
1180 if (direntry
[0] == DIRENTRY_PARENTDIR
) continue; /* skip ./.. entries */
1182 if (DIRENTRY_ATTRIB(direntry
) == ATTRIB_LFN
) continue; /* skip LFNs */
1183 if (DIRENTRY_ATTRIB(direntry
) & ATTRIB_LABEL
) continue; /* skip volume labels */
1186 * Get actual file names from directory
1189 fat_getshortname(direntry
,shortname
);
1190 fat_getlongname(fat
,chain
,idx
,direntry
,longname
);
1194 if (strcmpi(name
,shortname
) == 0) return 1;
1195 if (longname
[0] && (strcmpi(name
,longname
) == 0)) return 1;
1198 xprintf("%-30s",longname
[0] ? longname
: shortname
);
1199 // xprintf(" Clus=%04X",DIRENTRY_STARTCLUSTER(direntry));
1200 // xprintf(" Attrib=%02X",DIRENTRY_ATTRIB(direntry));
1201 // xprintf(" Size=%d",DIRENTRY_FILELENGTH(direntry));
1202 xprintf("%d",DIRENTRY_FILELENGTH(direntry
));
1210 /* *********************************************************************
1211 * fat_findfile(fat,name,direntry)
1213 * Locate a directory entry given a complete path name
1216 * fat - FAT filesystem descriptor
1217 * name - name of file to locate (forward or reverse slashses ok)
1218 * direntry - place to put directory entry we find
1221 * 0 if file not found
1222 * 1 if file was found
1223 * <0 if error occurs
1224 ********************************************************************* */
1226 static int fat_findfile(fatfs_t
*fat
,char *name
,uint8_t *direntry
)
1236 * Copy the name, we're going to hack it up
1239 namecopy
= strdup(name
);
1242 * Chew off the first piece up to the first slash. Remove
1243 * a leading slash if it is there.
1248 if ((*ptr
== '/') || (*ptr
== '\\')) ptr
++;
1251 while (*ptr
&& (*ptr
!= '/') && (*ptr
!= '\\')) ptr
++;
1252 if (*ptr
) *ptr
++ = '\0';
1255 * Scan the root directory looking for the first piece
1258 res
= fat_scandir(fat
,NULL
,namepart
,direntry
);
1261 return 0; /* file not found */
1266 * Start scanning subdirectories until we run out
1267 * of directory components.
1271 while (*ptr
&& (*ptr
!= '/') && (*ptr
!= '\\')) ptr
++;
1272 if (*ptr
) *ptr
++ = '\0';
1273 if (!*namepart
) namepart
= NULL
;
1279 * Scan the subdirectory
1282 e
= DIRENTRY_STARTCLUSTER(direntry
);
1283 memset(&chain
,0,sizeof(chain
));
1284 fat_getchain(fat
,e
,&chain
);
1285 res
= fat_scandir(fat
,&chain
,namepart
,direntry
);
1289 fat_freechain(&chain
);
1292 * Advance to the next piece
1296 while (*ptr
&& (*ptr
!= '/') && (*ptr
!= '\\')) ptr
++;
1297 if (*ptr
) *ptr
++ = '\0';
1298 if (!*namepart
) namepart
= NULL
;
1301 * If there's more to go and we hit something that
1302 * is not a directory, stop here.
1305 if (namepart
&& !(DIRENTRY_ATTRIB(direntry
) & ATTRIB_DIR
)) {
1313 * The last piece we enumerate has to be a file.
1317 (DIRENTRY_ATTRIB(direntry
) & ATTRIB_DIR
)) {
1325 /* *********************************************************************
1326 * fat_init(fat,name)
1328 * Create the filesystem descriptor and attach to the hardware
1332 * fat - filesystem descriptor
1333 * name - hardware device name
1334 * part - true to look for partition tables
1339 ********************************************************************* */
1341 static int fat_init(fatfs_t
*fat
,char *name
,int part
)
1345 memset(fat
,0,sizeof(fatfs_t
));
1346 fat
->fat_dirsecnum
= -1;
1347 fat
->fat_fatsecnum
= -1;
1349 fat
->fat_fh
= cfe_open(name
);
1351 if (fat
->fat_fh
< 0) return fat
->fat_fh
;
1353 res
= fatfs_check_for_partition_table(fat
);
1354 /* If we were able to figure it out, use that as the default */
1355 if (res
>= 0) part
= res
;
1358 res
= fat_findpart(fat
);
1360 cfe_close(fat
->fat_fh
);
1366 res
= fat_readbpb(fat
);
1368 cfe_close(fat
->fat_fh
);
1376 /* *********************************************************************
1379 * Uninit the filesystem descriptor and release any resources
1383 * fat - filesystem descriptor
1387 ********************************************************************* */
1389 static void fat_uninit(fatfs_t
*fat
)
1391 if (fat
->fat_fh
>= 0) cfe_close(fat
->fat_fh
);
1395 int fatfs_fileop_dir(void *fsctx
);
1396 int fatfs_fileop_dir(void *fsctx
)
1398 fatfs_t
*fatfs
= fsctx
;
1399 uint8_t direntry
[32];
1401 fat_scandir(fatfs
,NULL
,NULL
,direntry
);
1406 /* *********************************************************************
1407 * fatfs_fileop_init(fsctx,devname)
1409 * Create a FAT filesystem context and open the associated
1413 * fsctx - file system context (return pointer)
1414 * devname - device name to open
1415 * part - true to look for a partition table
1418 * 0 if ok, else error
1419 ********************************************************************* */
1421 static int fatfs_fileop_init(void **fsctx
,char *devname
,int part
)
1427 * Allocate a file system context
1430 fatfs
= (fatfs_t
*) KMALLOC(sizeof(fatfs_t
),0);
1431 if (!fatfs
) return CFE_ERR_NOMEM
;
1434 * Open a handle to the underlying device
1437 res
= fat_init(fatfs
,devname
,part
);
1448 /* *********************************************************************
1449 * fatfs_check_for_partition_table(fatfs)
1451 * This routine attempts to determine if the disk contains a
1452 * partition table or if it contains a standard MS-DOS boot recod.
1453 * We try to find both, and return what we find, or an error
1454 * if it is still unclear.
1457 * fatfs - fat filesystem context
1460 * 0 if no partition table
1461 * 1 if partition table
1462 * <0 = error occured, could not tell or I/O error
1463 ********************************************************************* */
1465 static int fatfs_check_for_partition_table(fatfs_t
*fatfs
)
1468 uint8_t buffer
[SECTORSIZE
];
1477 fatfs
->fat_partstart
= 0;
1478 res
= fat_readsector(fatfs
,0,1,buffer
);
1479 if (res
< 0) return res
;
1482 * Check the seal at the end of th sector. Both
1483 * boot sector and MBR should contain this seal.
1485 if (READWORD(buffer
,BPB_SEAL
) != BPB_SEAL_VALUE
) {
1486 res
= CFE_ERR_BADFILESYS
;
1491 * See Microsoft Knowledgebase article # Q140418, it contains
1492 * a good description of the boot sector format.
1494 * If the extended information is present, and SystemID is "FAT"
1495 * and the "bytes per sector" is 512, assume it's a regular boot block
1498 if (((buffer
[BPB_SIGNATURE
] == BPB_SIGNATURE_VALUE1
) ||
1499 (buffer
[BPB_SIGNATURE
] == BPB_SIGNATURE_VALUE2
)) &&
1500 (memcmp(&buffer
[BPB_SYSTEMID
],"FAT",3) == 0) &&
1501 (READWORD(buffer
,BPB_BYTESPERSECTOR
) == 512)) {
1502 /* Not partitioned */
1507 /* If no extended information is present, check a few other key values. */
1509 if ((READWORD(buffer
,BPB_BYTESPERSECTOR
) == 512) &&
1510 (READWORD(buffer
,BPB_RESERVEDSECTORS
) >= 1) &&
1511 ((READWORD(buffer
,BPB_MEDIADESCRIPTOR
) & 0xF0) == 0xF0)) {
1517 * If we're still confused, look for a partition table with a valid FAT
1518 * partition on it. We might not detect a partition table that has
1519 * only non-FAT partitions on it, like a disk with all Linux partitions,
1520 * but that is fine here in the FATFS module, since we only want to
1521 * find FAT partitions anyway.
1523 part
= &buffer
[PTABLE_OFFSET
];
1524 for (idx
= 0; idx
< PTABLE_COUNT
; idx
++) {
1526 if (((part
[PTABLE_STATUS
] == PTABLE_STATUS_ACTIVE
) ||
1527 (part
[PTABLE_STATUS
] == 0x00)) &&
1528 ((part
[PTABLE_TYPE
] == PARTTYPE_FAT12
) ||
1529 (part
[PTABLE_TYPE
] == PARTTYPE_FAT16
) ||
1530 (part
[PTABLE_TYPE
] == PARTTYPE_FAT16BIG
))) {
1532 res
= 1; /*Partition table present*/
1535 part
+= PTABLE_SIZE
;
1539 * If at this point we did not find what we were looking for,
1543 res
= 1; /*Partition table is present.*/
1546 /*Error! We can't decide if partition table exists or not*/
1547 res
= CFE_ERR_BADFILESYS
;
1553 static int fatfs_fileop_xinit(void **fsctx
,void *dev
)
1555 char *devname
= (char *) dev
;
1557 return fatfs_fileop_init(fsctx
,devname
,0);
1560 static int fatfs_fileop_pinit(void **fsctx
,void *dev
)
1562 char *devname
= (char *) dev
;
1564 return fatfs_fileop_init(fsctx
,devname
,1);
1569 /* *********************************************************************
1570 * fatfs_fileop_open(ref,name)
1572 * Open a file on the FAT device.
1575 * ref - place to store pointer to fileinfo
1576 * fsctx - filesystem context
1577 * name - name of file to open
1582 ********************************************************************* */
1584 static int fatfs_fileop_open(void **ref
,void *fsctx
,char *name
,int mode
)
1587 uint8_t direntry
[DIRENTRYSIZE
];
1591 if (mode
!= FILE_MODE_READ
) return CFE_ERR_UNSUPPORTED
;
1593 fatfs
= (fatfs_t
*) fsctx
;
1595 ff
= (fatfile_t
*) KMALLOC(sizeof(fatfile_t
),0);
1596 if (ff
== NULL
) return CFE_ERR_NOMEM
;
1598 memset(ff
,0,sizeof(fatfile_t
));
1602 res
= fat_findfile(ff
->ff_fat
,name
,direntry
);
1604 return CFE_ERR_FILENOTFOUND
; /* not found */
1608 * Okay, the file was found. Enumerate the FAT chain
1609 * associated with this file.
1612 ff
->ff_filelength
= DIRENTRY_FILELENGTH(direntry
);
1615 ff
->ff_cursector
= -1;
1617 res
= fat_getchain(ff
->ff_fat
,
1618 DIRENTRY_STARTCLUSTER(direntry
),
1627 * Return the file handle
1631 fatfs
->fat_refcnt
++;
1637 /* *********************************************************************
1638 * fatfs_fileop_close(ref)
1643 * ref - pointer to open file information
1647 ********************************************************************* */
1649 static void fatfs_fileop_close(void *ref
)
1651 fatfile_t
*file
= (fatfile_t
*) ref
;
1652 fatfs_t
*fatctx
= file
->ff_fat
;
1654 fatctx
->fat_refcnt
--;
1656 fat_freechain(&(file
->ff_chain
));
1661 /* *********************************************************************
1662 * fatfs_fileop_uninit(ref)
1664 * Uninitialize the file system.
1667 * fsctx - filesystem context
1671 ********************************************************************* */
1672 static void fatfs_fileop_uninit(void *fsctx
)
1674 fatfs_t
*fatctx
= (fatfs_t
*) fsctx
;
1676 if (fatctx
->fat_refcnt
) {
1677 xprintf("fatfs_fileop_unint: warning: refcnt should be zero\n");
1686 /* *********************************************************************
1687 * fatfs_fileop_seek(ref,offset,how)
1689 * Move the file pointer within the file
1692 * ref - pointer to open file information
1693 * offset - new file location or distance to move
1694 * how - method for moving
1698 * <0 if error occured
1699 ********************************************************************* */
1701 static int fatfs_fileop_seek(void *ref
,int offset
,int how
)
1703 fatfile_t
*file
= (fatfile_t
*) ref
;
1706 case FILE_SEEK_BEGINNING
:
1707 file
->ff_curpos
= offset
;
1709 case FILE_SEEK_CURRENT
:
1710 file
->ff_curpos
+= offset
;
1716 if (file
->ff_curpos
>= file
->ff_filelength
) {
1717 file
->ff_curpos
= file
->ff_filelength
;
1720 return file
->ff_curpos
;
1724 /* *********************************************************************
1725 * fatfs_fileop_read(ref,buf,len)
1727 * Read data from the file.
1730 * ref - pointer to open file information
1731 * buf - buffer to read data into
1732 * len - number of bytes to read
1735 * number of bytes read
1736 * <0 if error occured
1738 ********************************************************************* */
1740 static int fatfs_fileop_read(void *ref
,uint8_t *buf
,int len
)
1742 fatfile_t
*file
= (fatfile_t
*) ref
;
1750 uint8_t temp_buf
[SECTORSIZE
];
1753 * Remember orig position in case we have an error
1756 origpos
= file
->ff_curpos
;
1759 * bounds check the length based on the file length
1762 if ((file
->ff_curpos
+ len
) > file
->ff_filelength
) {
1763 len
= file
->ff_filelength
- file
->ff_curpos
;
1769 * while ther is still data to be transferred
1776 * Calculate the sector offset and index in the sector
1779 offset
= file
->ff_curpos
% SECTORSIZE
;
1780 secidx
= file
->ff_curpos
/ SECTORSIZE
;
1782 sector
= fat_sectoridx(file
->ff_fat
,&(file
->ff_chain
),secidx
);
1785 xprintf("should not happen, sector = -1!\n");
1790 * first transfer up to the sector boundary
1794 if (amtcopy
> (SECTORSIZE
-offset
)) {
1795 amtcopy
= (SECTORSIZE
-offset
);
1799 * If transferring exactly a sector, on a sector
1800 * boundary, read the data directly into the user buffer
1802 * Extra credit: See if we can transfer more than one
1803 * sector at a time, by determining if we can read a run of
1804 * contiguous sectors (very likely)
1806 * Otherwise: read into the sector buffer and
1807 * transfer the data to user memory.
1810 if ((offset
== 0) && (amtcopy
== SECTORSIZE
)) {
1811 res
= fat_readsector(file
->ff_fat
,sector
,1,temp_buf
);
1813 xprintf("I/O error!\n");
1816 memcpy(buf
,temp_buf
,amtcopy
);
1819 if (file
->ff_cursector
!= sector
) {
1820 res
= fat_readsector(file
->ff_fat
,sector
,1,file
->ff_sector
);
1824 file
->ff_cursector
= sector
;
1826 memcpy(buf
,&(file
->ff_sector
[offset
]),amtcopy
);
1830 * Adjust/update all our pointers.
1834 file
->ff_curpos
+= amtcopy
;
1839 * see if we ran off the end of the file. Should not
1843 if (file
->ff_curpos
>= file
->ff_filelength
) {
1844 /* should not be necessary */
1850 * If an error occured, get out now.
1854 file
->ff_curpos
= origpos
;
1862 static int fatfs_fileop_write(void *ref
,uint8_t *buf
,int len
)
1864 return CFE_ERR_UNSUPPORTED
;