1 /*----------------------------------------------------------------------------/
2 / Petit FatFs - FAT file system module R0.01a (C)ChaN, 2010
3 /-----------------------------------------------------------------------------/
4 / Petit FatFs module is an open source software to implement FAT file system to
5 / small embedded systems. This is a free software and is opened for education,
6 / research and commercial developments under license policy of following terms.
8 / Copyright (C) 2010, ChaN, all right reserved.
10 / * The Petit FatFs module is a free software and there is NO WARRANTY.
11 / * No restriction on use. You can use, modify and redistribute it for
12 / personal, non-profit or commercial use UNDER YOUR RESPONSIBILITY.
13 / * Redistributions of source code must retain the above copyright notice.
15 /-----------------------------------------------------------------------------/
16 / Jun 15,'09 R0.01a Branched from FatFs R0.07b
17 /----------------------------------------------------------------------------*/
19 #include "pff.h" /* Petit FatFs configurations and declarations */
20 #include "diskio.h" /* Declarations of low level disk I/O functions */
23 /*--------------------------------------------------------------------------
27 ---------------------------------------------------------------------------*/
30 FATFS
*FatFs
; /* Pointer to the file system object (logical drive) */
33 /*--------------------------------------------------------------------------
37 ---------------------------------------------------------------------------*/
40 /*-----------------------------------------------------------------------*/
41 /* String functions */
42 /*-----------------------------------------------------------------------*/
46 void mem_set (void* dst
, int val
, int cnt
) {
48 while (cnt
--) *d
++ = (char)val
;
51 /* Compare memory to memory */
53 int mem_cmp (const void* dst
, const void* src
, int cnt
) {
54 const char *d
= (const char *)dst
, *s
= (const char *)src
;
56 while (cnt
-- && (r
= *d
++ - *s
++) == 0) ;
60 /* Check if chr is contained in the string */
62 int chk_chr (const char* str
, int chr
) {
63 while (*str
&& *str
!= chr
) str
++;
69 /*-----------------------------------------------------------------------*/
70 /* FAT access - Read value of a FAT entry */
71 /*-----------------------------------------------------------------------*/
74 CLUST
get_fat ( /* 1:IO error, Else:Cluster status */
75 CLUST clst
/* Cluster# to get the link information */
83 if (clst
< 2 || clst
>= fs
->max_clust
) /* Range check */
86 switch (fs
->fs_type
) {
88 bc
= (WORD
)clst
; bc
+= bc
/ 2;
89 ofs
= bc
% 512; bc
/= 512;
91 if (disk_readp(buf
, fs
->fatbase
+ bc
, ofs
, 2)) break;
93 if (disk_readp(buf
, fs
->fatbase
+ bc
, 511, 1)) break;
94 if (disk_readp(buf
+1, fs
->fatbase
+ bc
+ 1, 0, 1)) break;
97 return (clst
& 1) ?
(wc
>> 4) : (wc
& 0xFFF);
100 if (disk_readp(buf
, fs
->fatbase
+ clst
/ 256, (WORD
)(((WORD
)clst
% 256) * 2), 2)) break;
104 if (disk_readp(buf
, fs
->fatbase
+ clst
/ 128, (WORD
)(((WORD
)clst
% 128) * 4), 4)) break;
105 return LD_DWORD(buf
) & 0x0FFFFFFF;
109 return 1; /* An error occurred at the disk I/O layer */
115 /*-----------------------------------------------------------------------*/
116 /* Get sector# from cluster# */
117 /*-----------------------------------------------------------------------*/
120 DWORD
clust2sect ( /* !=0: Sector number, 0: Failed - invalid cluster# */
121 CLUST clst
/* Cluster# to be converted */
128 if (clst
>= (fs
->max_clust
- 2)) return 0; /* Invalid cluster# */
129 return (DWORD
)clst
* fs
->csize
+ fs
->database
;
135 /*-----------------------------------------------------------------------*/
136 /* Directory handling - Rewind directory index */
137 /*-----------------------------------------------------------------------*/
141 DIR *dj
/* Pointer to directory object */
150 if (clst
== 1 || clst
>= fs
->max_clust
) /* Check start cluster range */
153 if (!clst
&& fs
->fs_type
== FS_FAT32
) /* Replace cluster# 0 with root cluster# if in FAT32 */
156 dj
->clust
= clst
; /* Current cluster */
157 dj
->sect
= clst ?
clust2sect(clst
) : fs
->dirbase
; /* Current sector */
159 return FR_OK
; /* Seek succeeded */
165 /*-----------------------------------------------------------------------*/
166 /* Directory handling - Move directory index next */
167 /*-----------------------------------------------------------------------*/
170 FRESULT
dir_next ( /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not stretch */
171 DIR *dj
/* Pointer to directory object */
180 if (!i
|| !dj
->sect
) /* Report EOT when index has reached 65535 */
183 if (!(i
& (16-1))) { /* Sector changed? */
184 dj
->sect
++; /* Next sector */
186 if (dj
->clust
== 0) { /* Static table */
187 if (i
>= fs
->n_rootdir
) /* Report EOT when end of table */
190 else { /* Dynamic table */
191 if (((i
/ 16) & (fs
->csize
-1)) == 0) { /* Cluster changed? */
192 clst
= get_fat(dj
->clust
); /* Get next cluster */
193 if (clst
<= 1) return FR_DISK_ERR
;
194 if (clst
>= fs
->max_clust
) /* When it reached end of dynamic table */
195 return FR_NO_FILE
; /* Report EOT */
196 dj
->clust
= clst
; /* Initialize data for new cluster */
197 dj
->sect
= clust2sect(clst
);
210 /*-----------------------------------------------------------------------*/
211 /* Directory handling - Find an object in the directory */
212 /*-----------------------------------------------------------------------*/
216 DIR *dj
/* Pointer to the directory object linked to the file name */
223 res
= dir_rewind(dj
); /* Rewind directory object */
224 if (res
!= FR_OK
) return res
;
228 res
= disk_readp(dir
, dj
->sect
, (WORD
)((dj
->index
% 16) * 32), 32) /* Read an entry */
229 ? FR_DISK_ERR
: FR_OK
;
230 if (res
!= FR_OK
) break;
231 c
= dir
[DIR_Name
]; /* First character */
232 if (c
== 0) { res
= FR_NO_FILE
; break; } /* Reached to end of table */
233 if (!(dir
[DIR_Attr
] & AM_VOL
) && !mem_cmp(dir
, dj
->fn
, 11)) /* Is it a valid entry? */
235 res
= dir_next(dj
); /* Next entry */
236 } while (res
== FR_OK
);
244 /*-----------------------------------------------------------------------*/
245 /* Read an object from the directory */
246 /*-----------------------------------------------------------------------*/
250 DIR *dj
/* Pointer to the directory object to store read object name */
260 res
= disk_readp(dir
, dj
->sect
, (WORD
)((dj
->index
% 16) * 32), 32) /* Read an entry */
261 ? FR_DISK_ERR
: FR_OK
;
262 if (res
!= FR_OK
) break;
264 if (c
== 0) { res
= FR_NO_FILE
; break; } /* Reached to end of table */
265 a
= dir
[DIR_Attr
] & AM_MASK
;
266 if (c
!= 0xE5 && c
!= '.' && !(a
& AM_VOL
)) /* Is it a valid entry? */
268 res
= dir_next(dj
); /* Next entry */
269 if (res
!= FR_OK
) break;
272 if (res
!= FR_OK
) dj
->sect
= 0;
280 /*-----------------------------------------------------------------------*/
281 /* Pick a segment and create the object name in directory form */
282 /*-----------------------------------------------------------------------*/
286 FRESULT
create_name (
287 DIR *dj
, /* Pointer to the directory object */
288 const char **path
/* Pointer to pointer to the segment in the path string */
291 BYTE c
, ni
, si
, i
, *sfn
;
294 /* Create file name in directory form */
296 mem_set(sfn
, ' ', 11);
301 if (c
< ' ' || c
== '/') break; /* Break on end of segment */
302 if (c
== '.' || i
>= ni
) {
303 if (ni
!= 8 || c
!= '.') return FR_INVALID_NAME
;
307 if (c
>= 0x7F || chk_chr(" +,;[=\\]\"*:<>\?|", c
)) /* Reject unallowable chrs for SFN */
308 return FR_INVALID_NAME
;
309 if (c
>='a' && c
<= 'z') c
-= 0x20;
312 if (!i
) return FR_INVALID_NAME
; /* Reject null string */
313 *path
= &p
[si
]; /* Rerun pointer to the next segment */
315 sfn
[11] = (c
< ' ') ?
1 : 0; /* Set last segment flag if end of path */
323 /*-----------------------------------------------------------------------*/
324 /* Get file information from directory entry */
325 /*-----------------------------------------------------------------------*/
328 void get_fileinfo ( /* No return code */
329 DIR *dj
, /* Pointer to the directory object */
330 FILINFO
*fno
/* Pointer to store the file information */
340 for (i
= 0; i
< 8; i
++) { /* Copy file name body */
343 if (c
== 0x05) c
= 0xE5;
346 if (dir
[8] != ' ') { /* Copy file name extension */
348 for (i
= 8; i
< 11; i
++) {
354 fno
->fattrib
= dir
[DIR_Attr
]; /* Attribute */
355 fno
->fsize
= LD_DWORD(dir
+DIR_FileSize
); /* Size */
356 fno
->fdate
= LD_WORD(dir
+DIR_WrtDate
); /* Date */
357 fno
->ftime
= LD_WORD(dir
+DIR_WrtTime
); /* Time */
361 #endif /* _USE_DIR */
365 /*-----------------------------------------------------------------------*/
366 /* Follow a file path */
367 /*-----------------------------------------------------------------------*/
370 FRESULT
follow_path ( /* FR_OK(0): successful, !=0: error code */
371 DIR *dj
, /* Directory object to return last directory and found object */
372 const char *path
/* Full-path string to find a file or directory */
379 if (*path
== '/') path
++; /* Strip heading separator */
380 dj
->sclust
= 0; /* Set start directory (always root dir) */
382 if ((BYTE
)*path
< ' ') { /* Null path means the root directory */
383 res
= dir_rewind(dj
);
386 } else { /* Follow path */
388 res
= create_name(dj
, &path
); /* Get a segment */
389 if (res
!= FR_OK
) break;
390 res
= dir_find(dj
); /* Find it */
391 if (res
!= FR_OK
) { /* Could not find the object */
392 if (res
== FR_NO_FILE
&& !*(dj
->fn
+11))
396 if (*(dj
->fn
+11)) break; /* Last segment match. Function completed. */
397 dir
= FatFs
->buf
; /* There is next segment. Follow the sub directory */
398 if (!(dir
[DIR_Attr
] & AM_DIR
)) { /* Cannot follow because it is a file */
399 res
= FR_NO_PATH
; break;
403 ((DWORD
)LD_WORD(dir
+DIR_FstClusHI
) << 16) |
405 LD_WORD(dir
+DIR_FstClusLO
);
415 /*-----------------------------------------------------------------------*/
416 /* Check a sector if it is an FAT boot record */
417 /*-----------------------------------------------------------------------*/
420 BYTE
check_fs ( /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record, 3:Error */
421 BYTE
*buf
, /* Working buffer */
422 DWORD sect
/* Sector# (lba) to check if it is an FAT boot record or not */
425 if (disk_readp(buf
, sect
, 510, 2)) /* Read the boot sector */
427 if (LD_WORD(buf
) != 0xAA55) /* Check record signature */
430 if (!disk_readp(buf
, sect
, BS_FilSysType
, 2) && LD_WORD(buf
) == 0x4146) /* Check FAT12/16 */
433 if (!disk_readp(buf
, sect
, BS_FilSysType32
, 2) && LD_WORD(buf
) == 0x4146) /* Check FAT32 */
442 /*--------------------------------------------------------------------------
446 --------------------------------------------------------------------------*/
450 /*-----------------------------------------------------------------------*/
451 /* Mount/Unmount a Logical Drive */
452 /*-----------------------------------------------------------------------*/
455 FATFS
*fs
/* Pointer to new file system object (NULL: Unmount) */
459 DWORD bsect
, fsize
, tsect
, mclst
;
463 if (!fs
) return FR_OK
; /* Unregister fs object */
465 if (disk_initialize() & STA_NOINIT
) /* Check if the drive is ready or not */
468 /* Search FAT partition on the drive */
470 fmt
= check_fs(buf
, bsect
); /* Check sector 0 as an SFD format */
471 if (fmt
== 1) { /* Not an FAT boot record, it may be FDISK format */
472 /* Check a partition listed in top of the partition table */
473 if (disk_readp(buf
, bsect
, MBR_Table
, 16)) { /* 1st partition entry */
476 if (buf
[4]) { /* Is the partition existing? */
477 bsect
= LD_DWORD(&buf
[8]); /* Partition offset in LBA */
478 fmt
= check_fs(buf
, bsect
); /* Check the partition */
482 if (fmt
== 3) return FR_DISK_ERR
;
483 if (fmt
) return FR_NO_FILESYSTEM
; /* No valid FAT partition is found */
485 /* Initialize the file system object */
486 if (disk_readp(buf
, bsect
, 13, sizeof(buf
))) return FR_DISK_ERR
;
488 fsize
= LD_WORD(buf
+BPB_FATSz16
-13); /* Number of sectors per FAT */
489 if (!fsize
) fsize
= LD_DWORD(buf
+BPB_FATSz32
-13);
491 fsize
*= buf
[BPB_NumFATs
-13]; /* Number of sectors in FAT area */
492 fs
->fatbase
= bsect
+ LD_WORD(buf
+BPB_RsvdSecCnt
-13); /* FAT start sector (lba) */
493 fs
->csize
= buf
[BPB_SecPerClus
-13]; /* Number of sectors per cluster */
494 fs
->n_rootdir
= LD_WORD(buf
+BPB_RootEntCnt
-13); /* Number of root directory entries */
495 tsect
= LD_WORD(buf
+BPB_TotSec16
-13); /* Number of sectors on the file system */
496 if (!tsect
) tsect
= LD_DWORD(buf
+BPB_TotSec32
-13);
497 mclst
= (tsect
/* Last cluster# + 1 */
498 - LD_WORD(buf
+BPB_RsvdSecCnt
-13) - fsize
- fs
->n_rootdir
/ 16
500 fs
->max_clust
= (CLUST
)mclst
;
502 fmt
= FS_FAT12
; /* Determine the FAT sub type */
503 if (mclst
>= 0xFF7) fmt
= FS_FAT16
; /* Number of clusters >= 0xFF5 */
504 if (mclst
>= 0xFFF7) /* Number of clusters >= 0xFFF5 */
508 return FR_NO_FILESYSTEM
;
511 fs
->fs_type
= fmt
; /* FAT sub-type */
514 fs
->dirbase
= LD_DWORD(buf
+(BPB_RootClus
-13)); /* Root directory start cluster */
517 fs
->dirbase
= fs
->fatbase
+ fsize
; /* Root directory start sector (lba) */
518 fs
->database
= fs
->fatbase
+ fsize
+ fs
->n_rootdir
/ 16; /* Data start sector (lba) */
529 /*-----------------------------------------------------------------------*/
530 /* Open or Create a File */
531 /*-----------------------------------------------------------------------*/
534 const char *path
/* Pointer to the file name */
539 BYTE sp
[12], dir
[32];
543 if (!fs
) /* Check file system */
544 return FR_NOT_ENABLED
;
549 res
= follow_path(&dj
, path
); /* Follow the file path */
550 if (res
!= FR_OK
) return res
; /* Follow failed */
551 if (!dir
[0] || (dir
[DIR_Attr
] & AM_DIR
)) /* It is a directory */
554 fs
->org_clust
= /* File start cluster */
556 ((DWORD
)LD_WORD(dir
+DIR_FstClusHI
) << 16) |
558 LD_WORD(dir
+DIR_FstClusLO
);
559 fs
->fsize
= LD_DWORD(dir
+DIR_FileSize
); /* File size */
560 fs
->fptr
= 0; /* File pointer */
569 /*-----------------------------------------------------------------------*/
571 /*-----------------------------------------------------------------------*/
574 void* dest
, /* Pointer to the destination object */
575 WORD btr
, /* Number of bytes to read (bit15:destination) */
576 WORD
* br
/* Pointer to number of bytes read */
588 if (!fs
) return FR_NOT_ENABLED
; /* Check file system */
589 if (!(fs
->flag
& FA_READ
))
590 return FR_INVALID_OBJECT
;
592 remain
= fs
->fsize
- fs
->fptr
;
593 if (btr
> remain
) btr
= (UINT
)remain
; /* Truncate btr by remaining bytes */
595 for ( ; btr
; /* Repeat until all data transferred */
596 rbuff
+= rcnt
, fs
->fptr
+= rcnt
, *br
+= rcnt
, btr
-= rcnt
) {
597 if ((fs
->fptr
% 512) == 0) { /* On the sector boundary? */
598 if ((fs
->fptr
/ 512 % fs
->csize
) == 0) { /* On the cluster boundary? */
599 clst
= (fs
->fptr
== 0) ?
/* On the top of the file? */
600 fs
->org_clust
: get_fat(fs
->curr_clust
);
602 fs
->flag
= 0; return FR_DISK_ERR
;
604 fs
->curr_clust
= clst
; /* Update current cluster */
605 fs
->csect
= 0; /* Reset sector offset in the cluster */
607 sect
= clust2sect(fs
->curr_clust
); /* Get current sector */
609 fs
->flag
= 0; return FR_DISK_ERR
;
613 fs
->csect
++; /* Next sector address in the cluster */
615 rcnt
= 512 - ((WORD
)fs
->fptr
% 512); /* Get partial sector data from sector buffer */
616 if (rcnt
> btr
) rcnt
= btr
;
617 if (fs
->flag
& FA_STREAM
) {
618 dr
= disk_readp(dest
, fs
->dsect
, (WORD
)(fs
->fptr
% 512), (WORD
)(rcnt
| 0x8000));
620 dr
= disk_readp(rbuff
, fs
->dsect
, (WORD
)(fs
->fptr
% 512), rcnt
);
624 return (dr
== RES_STRERR
) ? FR_STREAM_ERR
: FR_DISK_ERR
;
635 /*-----------------------------------------------------------------------*/
636 /* Seek File R/W Pointer */
637 /*-----------------------------------------------------------------------*/
640 DWORD ofs
/* File pointer from top of file */
644 DWORD bcs
, nsect
, ifptr
;
648 if (!fs
) return FR_NOT_ENABLED
; /* Check file system */
649 if (!(fs
->flag
& FA_READ
))
650 return FR_INVALID_OBJECT
;
652 if (ofs
> fs
->fsize
) ofs
= fs
->fsize
; /* Clip offset with the file size */
656 bcs
= (DWORD
)fs
->csize
* 512; /* Cluster size (byte) */
658 (ofs
- 1) / bcs
>= (ifptr
- 1) / bcs
) { /* When seek to same or following cluster, */
659 fs
->fptr
= (ifptr
- 1) & ~(bcs
- 1); /* start from the current cluster */
661 clst
= fs
->curr_clust
;
662 } else { /* When seek to back cluster, */
663 clst
= fs
->org_clust
; /* start from the first cluster */
664 fs
->curr_clust
= clst
;
666 while (ofs
> bcs
) { /* Cluster following loop */
667 clst
= get_fat(clst
); /* Follow cluster chain if not in write mode */
668 if (clst
<= 1 || clst
>= fs
->max_clust
) {
669 fs
->flag
= 0; return FR_DISK_ERR
;
671 fs
->curr_clust
= clst
;
676 fs
->csect
= (BYTE
)(ofs
/ 512) + 1; /* Sector offset in the cluster */
677 nsect
= clust2sect(clst
); /* Current sector */
679 fs
->flag
= 0; return FR_DISK_ERR
;
681 fs
->dsect
= nsect
+ fs
->csect
- 1;
690 /*-----------------------------------------------------------------------*/
691 /* Create a Directory Object */
692 /*-----------------------------------------------------------------------*/
695 DIR *dj
, /* Pointer to directory object to create */
696 const char *path
/* Pointer to the directory path */
700 BYTE sp
[12], dir
[32];
704 if (!fs
) { /* Check file system */
705 res
= FR_NOT_ENABLED
;
709 res
= follow_path(dj
, path
); /* Follow the path to the directory */
710 if (res
== FR_OK
) { /* Follow completed */
711 if (dir
[0]) { /* It is not the root dir */
712 if (dir
[DIR_Attr
] & AM_DIR
) { /* The object is a directory */
715 ((DWORD
)LD_WORD(dir
+DIR_FstClusHI
) << 16) |
717 LD_WORD(dir
+DIR_FstClusLO
);
718 } else { /* The object is not a directory */
723 res
= dir_rewind(dj
); /* Rewind dir */
726 if (res
== FR_NO_FILE
) res
= FR_NO_PATH
;
735 /*-----------------------------------------------------------------------*/
736 /* Read Directory Entry in Sequence */
737 /*-----------------------------------------------------------------------*/
740 DIR *dj
, /* Pointer to the open directory object */
741 FILINFO
*fno
/* Pointer to file information to return */
745 BYTE sp
[12], dir
[32];
749 if (!fs
) { /* Check file system */
750 res
= FR_NOT_ENABLED
;
755 res
= dir_rewind(dj
);
758 if (res
== FR_NO_FILE
) {
762 if (res
== FR_OK
) { /* A valid entry is found */
763 get_fileinfo(dj
, fno
); /* Get the object information */
764 res
= dir_next(dj
); /* Increment index for next */
765 if (res
== FR_NO_FILE
) {