Attempts to merge SPI_TFT2 & SPI_TFT_ILI9341

Dependencies:   SPI_TFTx2 TFT_fonts TOUCH_TFTx2 mbed

Fork of CANary by Tick Tock

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ff.cpp Source File

ff.cpp

00001 /*----------------------------------------------------------------------------/
00002 /  FatFs - FAT file system module  R0.07e                    (C)ChaN, 2009
00003 /-----------------------------------------------------------------------------/
00004 / FatFs module is a generic FAT file system module for small embedded systems.
00005 / This is a free software that opened for education, research and commercial
00006 / developments under license policy of following trems.
00007 /
00008 /  Copyright (C) 2009, ChaN, all right reserved.
00009 /
00010 / * The FatFs module is a free software and there is NO WARRANTY.
00011 / * No restriction on use. You can use, modify and redistribute it for
00012 /   personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.
00013 / * Redistributions of source code must retain the above copyright notice.
00014 /
00015 /-----------------------------------------------------------------------------/
00016 / Feb 26,'06 R0.00  Prototype.
00017 /
00018 / Apr 29,'06 R0.01  First stable version.
00019 /
00020 / Jun 01,'06 R0.02  Added FAT12 support.
00021 /                   Removed unbuffered mode.
00022 /                   Fixed a problem on small (<32M) patition.
00023 / Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM).
00024 /
00025 / Sep 22,'06 R0.03  Added f_rename().
00026 /                   Changed option _FS_MINIMUM to _FS_MINIMIZE.
00027 / Dec 11,'06 R0.03a Improved cluster scan algolithm to write files fast.
00028 /                   Fixed f_mkdir() creates incorrect directory on FAT32.
00029 /
00030 / Feb 04,'07 R0.04  Supported multiple drive system.
00031 /                   Changed some interfaces for multiple drive system.
00032 /                   Changed f_mountdrv() to f_mount().
00033 /                   Added f_mkfs().
00034 / Apr 01,'07 R0.04a Supported multiple partitions on a plysical drive.
00035 /                   Added a capability of extending file size to f_lseek().
00036 /                   Added minimization level 3.
00037 /                   Fixed an endian sensitive code in f_mkfs().
00038 / May 05,'07 R0.04b Added a configuration option _USE_NTFLAG.
00039 /                   Added FSInfo support.
00040 /                   Fixed DBCS name can result FR_INVALID_NAME.
00041 /                   Fixed short seek (<= csize) collapses the file object.
00042 /
00043 / Aug 25,'07 R0.05  Changed arguments of f_read(), f_write() and f_mkfs().
00044 /                   Fixed f_mkfs() on FAT32 creates incorrect FSInfo.
00045 /                   Fixed f_mkdir() on FAT32 creates incorrect directory.
00046 / Feb 03,'08 R0.05a Added f_truncate() and f_utime().
00047 /                   Fixed off by one error at FAT sub-type determination.
00048 /                   Fixed btr in f_read() can be mistruncated.
00049 /                   Fixed cached sector is not flushed when create and close
00050 /                   without write.
00051 /
00052 / Apr 01,'08 R0.06  Added fputc(), fputs(), fprintf() and fgets().
00053 /                   Improved performance of f_lseek() on moving to the same
00054 /                   or following cluster.
00055 /
00056 / Apr 01,'09 R0.07  Merged Tiny-FatFs as a buffer configuration option.
00057 /                   Added long file name support.
00058 /                   Added multiple code page support.
00059 /                   Added re-entrancy for multitask operation.
00060 /                   Added auto cluster size selection to f_mkfs().
00061 /                   Added rewind option to f_readdir().
00062 /                   Changed result code of critical errors.
00063 /                   Renamed string functions to avoid name collision.
00064 / Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg.
00065 /                   Added multiple sector size support.
00066 / Jun 21,'09 R0.07c Fixed f_unlink() can return FR_OK on error.
00067 /                   Fixed wrong cache control in f_lseek().
00068 /                   Added relative path feature.
00069 /                   Added f_chdir() and f_chdrive().
00070 /                   Added proper case conversion to extended char.
00071 / Nov 03,'09 R0.07e Separated out configuration options from ff.h to ffconf.h.
00072 /                   Fixed f_unlink() fails to remove a sub-dir on _FS_RPATH.
00073 /                   Fixed name matching error on the 13 char boundary.
00074 /                   Added a configuration option, _LFN_UNICODE.
00075 /                   Changed f_readdir() to return the SFN with always upper
00076 /                   case on non-LFN cfg.
00077 /---------------------------------------------------------------------------*/
00078 
00079 #include "ff.h"            /* FatFs configurations and declarations */
00080 #include "diskio.h"        /* Declarations of low level disk I/O functions */
00081 
00082 /*--------------------------------------------------------------------------
00083 
00084    Module Private Definitions
00085 
00086 ---------------------------------------------------------------------------*/
00087 
00088 #if _FATFS != 0x007E
00089 #error Wrong include file (ff.h).
00090 #endif
00091 
00092 #if _FS_REENTRANT
00093 #if _USE_LFN == 1
00094 #error Static LFN work area must not be used in re-entrant configuration.
00095 #endif
00096 #define    ENTER_FF(fs)        { if (!lock_fs(fs)) return FR_TIMEOUT; }
00097 #define    LEAVE_FF(fs, res)    { unlock_fs(fs, res); return res; }
00098 
00099 #else
00100 #define    ENTER_FF(fs)
00101 #define LEAVE_FF(fs, res)    return res
00102 
00103 #endif
00104 
00105 #define    ABORT(fs, res)        { fp->flag |= FA__ERROR; LEAVE_FF(fs, res); }
00106 
00107 #ifndef NULL
00108 #define    NULL    0
00109 #endif
00110 
00111 /* Name status flags */
00112 #define NS            11        /* Offset of name status byte */
00113 #define NS_LOSS        0x01    /* Out of 8.3 format */
00114 #define NS_LFN        0x02    /* Force to create LFN entry */
00115 #define NS_LAST        0x04    /* Last segment */
00116 #define NS_BODY        0x08    /* Lower case flag (body) */
00117 #define NS_EXT        0x10    /* Lower case flag (ext) */
00118 #define NS_DOT        0x20    /* Dot entry */
00119 
00120 
00121 
00122 
00123 /*--------------------------------------------------------------------------
00124 
00125    Private Work Area
00126 
00127 ---------------------------------------------------------------------------*/
00128 
00129 #if _DRIVES < 1 || _DRIVES > 9
00130 #error Number of drives must be 1-9.
00131 #endif
00132 static
00133 FATFS *FatFs[_DRIVES];    /* Pointer to the file system objects (logical drives) */
00134 
00135 static
00136 WORD Fsid;                /* File system mount ID */
00137 
00138 #if _FS_RPATH
00139 static
00140 BYTE Drive;                /* Current drive */
00141 #endif
00142 
00143 
00144 #if _USE_LFN == 1    /* LFN with static LFN working buffer */
00145 static
00146 WCHAR LfnBuf[_MAX_LFN + 1];
00147 #define    NAMEBUF(sp,lp)    BYTE sp[12]; WCHAR *lp = LfnBuf
00148 #define INITBUF(dj,sp,lp)    dj.fn = sp; dj.lfn = lp
00149 
00150 #elif _USE_LFN > 1    /* LFN with dynamic LFN working buffer */
00151 #define    NAMEBUF(sp,lp)    BYTE sp[12]; WCHAR lbuf[_MAX_LFN + 1], *lp = lbuf
00152 #define INITBUF(dj,sp,lp)    dj.fn = sp; dj.lfn = lp
00153 
00154 #else                /* No LFN */
00155 #define    NAMEBUF(sp,lp)    BYTE sp[12]
00156 #define INITBUF(dj,sp,lp)    dj.fn = sp
00157 
00158 #endif
00159 
00160 
00161 
00162 
00163 /*--------------------------------------------------------------------------
00164 
00165    Module Private Functions
00166 
00167 ---------------------------------------------------------------------------*/
00168 
00169 
00170 /*-----------------------------------------------------------------------*/
00171 /* String functions                                                      */
00172 /*-----------------------------------------------------------------------*/
00173 
00174 /* Copy memory to memory */
00175 static
00176 void mem_cpy (void* dst, const void* src, int cnt) {
00177     char *d = (char*)dst;
00178     const char *s = (const char *)src;
00179     while (cnt--) *d++ = *s++;
00180 }
00181 
00182 /* Fill memory */
00183 static
00184 void mem_set (void* dst, int val, int cnt) {
00185     char *d = (char*)dst;
00186     while (cnt--) *d++ = (char)val;
00187 }
00188 
00189 /* Compare memory to memory */
00190 static
00191 int mem_cmp (const void* dst, const void* src, int cnt) {
00192     const char *d = (const char *)dst, *s = (const char *)src;
00193     int r = 0;
00194     while (cnt-- && (r = *d++ - *s++) == 0) ;
00195     return r;
00196 }
00197 
00198 /* Check if chr is contained in the string */
00199 static
00200 int chk_chr (const char* str, int chr) {
00201     while (*str && *str != chr) str++;
00202     return *str;
00203 }
00204 
00205 
00206 
00207 /*-----------------------------------------------------------------------*/
00208 /* Request/Release grant to access the volume                            */
00209 /*-----------------------------------------------------------------------*/
00210 #if _FS_REENTRANT
00211 
00212 static
00213 BOOL lock_fs (
00214     FATFS *fs        /* File system object */
00215 )
00216 {
00217     return ff_req_grant(fs->sobj);
00218 }
00219 
00220 
00221 static
00222 void unlock_fs (
00223     FATFS *fs,        /* File system object */
00224     FRESULT res        /* Result code to be returned */
00225 )
00226 {
00227     if (res != FR_NOT_ENABLED &&
00228         res != FR_INVALID_DRIVE &&
00229         res != FR_INVALID_OBJECT &&
00230         res != FR_TIMEOUT) {
00231         ff_rel_grant(fs->sobj);
00232     }
00233 }
00234 #endif
00235 
00236 
00237 
00238 /*-----------------------------------------------------------------------*/
00239 /* Change window offset                                                  */
00240 /*-----------------------------------------------------------------------*/
00241 
00242 static
00243 FRESULT move_window (
00244     FATFS *fs,        /* File system object */
00245     DWORD sector    /* Sector number to make apperance in the fs->win[] */
00246 )                    /* Move to zero only writes back dirty window */
00247 {
00248     DWORD wsect;
00249 
00250 
00251     wsect = fs->winsect;
00252     if (wsect != sector) {    /* Changed current window */
00253 #if !_FS_READONLY
00254         if (fs->wflag) {    /* Write back dirty window if needed */
00255             if (disk_write(fs->drive, fs->win, wsect, 1) != RES_OK)
00256                 return FR_DISK_ERR;
00257             fs->wflag = 0;
00258             if (wsect < (fs->fatbase + fs->sects_fat)) {    /* In FAT area */
00259                 BYTE nf;
00260                 for (nf = fs->n_fats; nf > 1; nf--) {    /* Refrect the change to all FAT copies */
00261                     wsect += fs->sects_fat;
00262                     disk_write(fs->drive, fs->win, wsect, 1);
00263                 }
00264             }
00265         }
00266 #endif
00267         if (sector) {
00268             if (disk_read(fs->drive, fs->win, sector, 1) != RES_OK)
00269                 return FR_DISK_ERR;
00270             fs->winsect = sector;
00271         }
00272     }
00273 
00274     return FR_OK;
00275 }
00276 
00277 
00278 
00279 
00280 /*-----------------------------------------------------------------------*/
00281 /* Clean-up cached data                                                  */
00282 /*-----------------------------------------------------------------------*/
00283 #if !_FS_READONLY
00284 static
00285 FRESULT sync (    /* FR_OK: successful, FR_DISK_ERR: failed */
00286     FATFS *fs    /* File system object */
00287 )
00288 {
00289     FRESULT res;
00290 
00291 
00292     res = move_window(fs, 0);
00293     if (res == FR_OK) {
00294         /* Update FSInfo sector if needed */
00295         if (fs->fs_type == FS_FAT32 && fs->fsi_flag) {
00296             fs->winsect = 0;
00297             mem_set(fs->win, 0, 512);
00298             ST_WORD(fs->win+BS_55AA, 0xAA55);
00299             ST_DWORD(fs->win+FSI_LeadSig, 0x41615252);
00300             ST_DWORD(fs->win+FSI_StrucSig, 0x61417272);
00301             ST_DWORD(fs->win+FSI_Free_Count, fs->free_clust);
00302             ST_DWORD(fs->win+FSI_Nxt_Free, fs->last_clust);
00303             disk_write(fs->drive, fs->win, fs->fsi_sector, 1);
00304             fs->fsi_flag = 0;
00305         }
00306         /* Make sure that no pending write process in the physical drive */
00307         if (disk_ioctl(fs->drive, CTRL_SYNC, (void*)NULL) != RES_OK)
00308             res = FR_DISK_ERR;
00309     }
00310 
00311     return res;
00312 }
00313 #endif
00314 
00315 
00316 
00317 
00318 /*-----------------------------------------------------------------------*/
00319 /* FAT access - Read value of a FAT entry                                */
00320 /*-----------------------------------------------------------------------*/
00321 
00322 
00323 DWORD get_fat (    /* 0xFFFFFFFF:Disk error, 1:Interal error, Else:Cluster status */
00324     FATFS *fs,    /* File system object */
00325     DWORD clst    /* Cluster# to get the link information */
00326 )
00327 {
00328     UINT wc, bc;
00329     DWORD fsect;
00330 
00331 
00332     if (clst < 2 || clst >= fs->max_clust)    /* Range check */
00333         return 1;
00334 
00335     fsect = fs->fatbase;
00336     switch (fs->fs_type) {
00337     case FS_FAT12 :
00338         bc = clst; bc += bc / 2;
00339         if (move_window(fs, fsect + (bc / SS(fs)))) break;
00340         wc = fs->win[bc & (SS(fs) - 1)]; bc++;
00341         if (move_window(fs, fsect + (bc / SS(fs)))) break;
00342         wc |= (WORD)fs->win[bc & (SS(fs) - 1)] << 8;
00343         return (clst & 1) ? (wc >> 4) : (wc & 0xFFF);
00344 
00345     case FS_FAT16 :
00346         if (move_window(fs, fsect + (clst / (SS(fs) / 2)))) break;
00347         return LD_WORD(&fs->win[((WORD)clst * 2) & (SS(fs) - 1)]);
00348 
00349     case FS_FAT32 :
00350         if (move_window(fs, fsect + (clst / (SS(fs) / 4)))) break;
00351         return LD_DWORD(&fs->win[((WORD)clst * 4) & (SS(fs) - 1)]) & 0x0FFFFFFF;
00352     }
00353 
00354     return 0xFFFFFFFF;    /* An error occured at the disk I/O layer */
00355 }
00356 
00357 
00358 
00359 
00360 /*-----------------------------------------------------------------------*/
00361 /* FAT access - Change value of a FAT entry                              */
00362 /*-----------------------------------------------------------------------*/
00363 #if !_FS_READONLY
00364 
00365 FRESULT put_fat (
00366     FATFS *fs,    /* File system object */
00367     DWORD clst,    /* Cluster# to be changed in range of 2 to fs->max_clust - 1 */
00368     DWORD val    /* New value to mark the cluster */
00369 )
00370 {
00371     UINT bc;
00372     BYTE *p;
00373     DWORD fsect;
00374     FRESULT res;
00375 
00376 
00377     if (clst < 2 || clst >= fs->max_clust) {    /* Range check */
00378         res = FR_INT_ERR;
00379 
00380     } else {
00381         fsect = fs->fatbase;
00382         switch (fs->fs_type) {
00383         case FS_FAT12 :
00384             bc = clst; bc += bc / 2;
00385             res = move_window(fs, fsect + (bc / SS(fs)));
00386             if (res != FR_OK) break;
00387             p = &fs->win[bc & (SS(fs) - 1)];
00388             *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val;
00389             bc++;
00390             fs->wflag = 1;
00391             res = move_window(fs, fsect + (bc / SS(fs)));
00392             if (res != FR_OK) break;
00393             p = &fs->win[bc & (SS(fs) - 1)];
00394             *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F));
00395             break;
00396 
00397         case FS_FAT16 :
00398             res = move_window(fs, fsect + (clst / (SS(fs) / 2)));
00399             if (res != FR_OK) break;
00400             ST_WORD(&fs->win[((WORD)clst * 2) & (SS(fs) - 1)], (WORD)val);
00401             break;
00402 
00403         case FS_FAT32 :
00404             res = move_window(fs, fsect + (clst / (SS(fs) / 4)));
00405             if (res != FR_OK) break;
00406             ST_DWORD(&fs->win[((WORD)clst * 4) & (SS(fs) - 1)], val);
00407             break;
00408 
00409         default :
00410             res = FR_INT_ERR;
00411         }
00412         fs->wflag = 1;
00413     }
00414 
00415     return res;
00416 }
00417 #endif /* !_FS_READONLY */
00418 
00419 
00420 
00421 
00422 /*-----------------------------------------------------------------------*/
00423 /* FAT handling - Remove a cluster chain                                 */
00424 /*-----------------------------------------------------------------------*/
00425 #if !_FS_READONLY
00426 static
00427 FRESULT remove_chain (
00428     FATFS *fs,            /* File system object */
00429     DWORD clst            /* Cluster# to remove a chain from */
00430 )
00431 {
00432     FRESULT res;
00433     DWORD nxt;
00434 
00435 
00436     if (clst < 2 || clst >= fs->max_clust) {    /* Check the range of cluster# */
00437         res = FR_INT_ERR;
00438 
00439     } else {
00440         res = FR_OK;
00441         while (clst < fs->max_clust) {            /* Not a last link? */
00442             nxt = get_fat(fs, clst);            /* Get cluster status */
00443             if (nxt == 0) break;                /* Empty cluster? */
00444             if (nxt == 1) { res = FR_INT_ERR; break; }    /* Internal error? */
00445             if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }    /* Disk error? */
00446             res = put_fat(fs, clst, 0);            /* Mark the cluster "empty" */
00447             if (res != FR_OK) break;
00448             if (fs->free_clust != 0xFFFFFFFF) {    /* Update FSInfo */
00449                 fs->free_clust++;
00450                 fs->fsi_flag = 1;
00451             }
00452             clst = nxt;    /* Next cluster */
00453         }
00454     }
00455 
00456     return res;
00457 }
00458 #endif
00459 
00460 
00461 
00462 
00463 /*-----------------------------------------------------------------------*/
00464 /* FAT handling - Stretch or Create a cluster chain                      */
00465 /*-----------------------------------------------------------------------*/
00466 #if !_FS_READONLY
00467 static
00468 DWORD create_chain (    /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
00469     FATFS *fs,            /* File system object */
00470     DWORD clst            /* Cluster# to stretch. 0 means create a new chain. */
00471 )
00472 {
00473     DWORD cs, ncl, scl, mcl;
00474 
00475 
00476     mcl = fs->max_clust;
00477     if (clst == 0) {        /* Create new chain */
00478         scl = fs->last_clust;            /* Get suggested start point */
00479         if (scl == 0 || scl >= mcl) scl = 1;
00480     }
00481     else {                    /* Stretch existing chain */
00482         cs = get_fat(fs, clst);            /* Check the cluster status */
00483         if (cs < 2) return 1;            /* It is an invalid cluster */
00484         if (cs < mcl) return cs;        /* It is already followed by next cluster */
00485         scl = clst;
00486     }
00487 
00488     ncl = scl;                /* Start cluster */
00489     for (;;) {
00490         ncl++;                            /* Next cluster */
00491         if (ncl >= mcl) {                /* Wrap around */
00492             ncl = 2;
00493             if (ncl > scl) return 0;    /* No free custer */
00494         }
00495         cs = get_fat(fs, ncl);            /* Get the cluster status */
00496         if (cs == 0) break;                /* Found a free cluster */
00497         if (cs == 0xFFFFFFFF || cs == 1)/* An error occured */
00498             return cs;
00499         if (ncl == scl) return 0;        /* No free custer */
00500     }
00501 
00502     if (put_fat(fs, ncl, 0x0FFFFFFF))    /* Mark the new cluster "in use" */
00503         return 0xFFFFFFFF;
00504     if (clst != 0) {                    /* Link it to the previous one if needed */
00505         if (put_fat(fs, clst, ncl))
00506             return 0xFFFFFFFF;
00507     }
00508 
00509     fs->last_clust = ncl;                /* Update FSINFO */
00510     if (fs->free_clust != 0xFFFFFFFF) {
00511         fs->free_clust--;
00512         fs->fsi_flag = 1;
00513     }
00514 
00515     return ncl;        /* Return new cluster number */
00516 }
00517 #endif /* !_FS_READONLY */
00518 
00519 
00520 
00521 
00522 /*-----------------------------------------------------------------------*/
00523 /* Get sector# from cluster#                                             */
00524 /*-----------------------------------------------------------------------*/
00525 
00526 
00527 DWORD clust2sect (    /* !=0: Sector number, 0: Failed - invalid cluster# */
00528     FATFS *fs,        /* File system object */
00529     DWORD clst        /* Cluster# to be converted */
00530 )
00531 {
00532     clst -= 2;
00533     if (clst >= (fs->max_clust - 2)) return 0;        /* Invalid cluster# */
00534     return clst * fs->csize + fs->database;
00535 }
00536 
00537 
00538 
00539 
00540 /*-----------------------------------------------------------------------*/
00541 /* Directory handling - Seek directory index                             */
00542 /*-----------------------------------------------------------------------*/
00543 
00544 static
00545 FRESULT dir_seek (
00546     eDIR *dj,        /* Pointer to directory object */
00547     WORD idx        /* Directory index number */
00548 )
00549 {
00550     DWORD clst;
00551     WORD ic;
00552 
00553 
00554     dj->index = idx;
00555     clst = dj->sclust;
00556     if (clst == 1 || clst >= dj->fs->max_clust)    /* Check start cluster range */
00557         return FR_INT_ERR;
00558     if (!clst && dj->fs->fs_type == FS_FAT32)    /* Replace cluster# 0 with root cluster# if in FAT32 */
00559         clst = dj->fs->dirbase;
00560 
00561     if (clst == 0) {    /* Static table */
00562         dj->clust = clst;
00563         if (idx >= dj->fs->n_rootdir)        /* Index is out of range */
00564             return FR_INT_ERR;
00565         dj->sect = dj->fs->dirbase + idx / (SS(dj->fs) / 32);    /* Sector# */
00566     }
00567     else {                /* Dynamic table */
00568         ic = SS(dj->fs) / 32 * dj->fs->csize;    /* Entries per cluster */
00569         while (idx >= ic) {    /* Follow cluster chain */
00570             clst = get_fat(dj->fs, clst);                /* Get next cluster */
00571             if (clst == 0xFFFFFFFF) return FR_DISK_ERR;    /* Disk error */
00572             if (clst < 2 || clst >= dj->fs->max_clust)    /* Reached to end of table or int error */
00573                 return FR_INT_ERR;
00574             idx -= ic;
00575         }
00576         dj->clust = clst;
00577         dj->sect = clust2sect(dj->fs, clst) + idx / (SS(dj->fs) / 32);    /* Sector# */
00578     }
00579 
00580     dj->dir = dj->fs->win + (idx % (SS(dj->fs) / 32)) * 32;    /* Ptr to the entry in the sector */
00581 
00582     return FR_OK;    /* Seek succeeded */
00583 }
00584 
00585 
00586 
00587 
00588 /*-----------------------------------------------------------------------*/
00589 /* Directory handling - Move directory index next                        */
00590 /*-----------------------------------------------------------------------*/
00591 
00592 static
00593 FRESULT dir_next (    /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not streach */
00594     eDIR *dj,        /* Pointer to directory object */
00595     BOOL streach    /* FALSE: Do not streach table, TRUE: Streach table if needed */
00596 )
00597 {
00598     DWORD clst;
00599     WORD i;
00600 
00601 
00602     i = dj->index + 1;
00603     if (!i || !dj->sect)    /* Report EOT when index has reached 65535 */
00604         return FR_NO_FILE;
00605 
00606     if (!(i % (SS(dj->fs) / 32))) {    /* Sector changed? */
00607         dj->sect++;                    /* Next sector */
00608 
00609         if (dj->clust == 0) {    /* Static table */
00610             if (i >= dj->fs->n_rootdir)    /* Report EOT when end of table */
00611                 return FR_NO_FILE;
00612         }
00613         else {                    /* Dynamic table */
00614             if (((i / (SS(dj->fs) / 32)) & (dj->fs->csize - 1)) == 0) {    /* Cluster changed? */
00615                 clst = get_fat(dj->fs, dj->clust);                /* Get next cluster */
00616                 if (clst <= 1) return FR_INT_ERR;
00617                 if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
00618                 if (clst >= dj->fs->max_clust) {                /* When it reached end of dynamic table */
00619 #if !_FS_READONLY
00620                     BYTE c;
00621                     if (!streach) return FR_NO_FILE;            /* When do not streach, report EOT */
00622                     clst = create_chain(dj->fs, dj->clust);        /* Streach cluster chain */
00623                     if (clst == 0) return FR_DENIED;            /* No free cluster */
00624                     if (clst == 1) return FR_INT_ERR;
00625                     if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
00626                     /* Clean-up streached table */
00627                     if (move_window(dj->fs, 0)) return FR_DISK_ERR;    /* Flush active window */
00628                     mem_set(dj->fs->win, 0, SS(dj->fs));            /* Clear window buffer */
00629                     dj->fs->winsect = clust2sect(dj->fs, clst);    /* Cluster start sector */
00630                     for (c = 0; c < dj->fs->csize; c++) {        /* Fill the new cluster with 0 */
00631                         dj->fs->wflag = 1;
00632                         if (move_window(dj->fs, 0)) return FR_DISK_ERR;
00633                         dj->fs->winsect++;
00634                     }
00635                     dj->fs->winsect -= c;                        /* Rewind window address */
00636 #else
00637                     return FR_NO_FILE;            /* Report EOT */
00638 #endif
00639                 }
00640                 dj->clust = clst;                /* Initialize data for new cluster */
00641                 dj->sect = clust2sect(dj->fs, clst);
00642             }
00643         }
00644     }
00645 
00646     dj->index = i;
00647     dj->dir = dj->fs->win + (i % (SS(dj->fs) / 32)) * 32;
00648 
00649     return FR_OK;
00650 }
00651 
00652 
00653 
00654 
00655 /*-----------------------------------------------------------------------*/
00656 /* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry   */
00657 /*-----------------------------------------------------------------------*/
00658 #if _USE_LFN
00659 static
00660 const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30};    /* Offset of LFN chars in the directory entry */
00661 
00662 
00663 static
00664 BOOL cmp_lfn (            /* TRUE:Matched, FALSE:Not matched */
00665     WCHAR *lfnbuf,        /* Pointer to the LFN to be compared */
00666     BYTE *dir            /* Pointer to the directory entry containing a part of LFN */
00667 )
00668 {
00669     int i, s;
00670     WCHAR wc, uc;
00671 
00672 
00673     i = ((dir[LDIR_Ord] & 0xBF) - 1) * 13;    /* Get offset in the LFN buffer */
00674     s = 0; wc = 1;
00675     do {
00676         uc = LD_WORD(dir+LfnOfs[s]);    /* Pick an LFN character from the entry */
00677         if (wc) {    /* Last char has not been processed */
00678             wc = ff_wtoupper(uc);        /* Convert it to upper case */
00679             if (i >= _MAX_LFN || wc != ff_wtoupper(lfnbuf[i++]))    /* Compare it */
00680                 return FALSE;            /* Not matched */
00681         } else {
00682             if (uc != 0xFFFF) return FALSE;    /* Check filler */
00683         }
00684     } while (++s < 13);                /* Repeat until all chars in the entry are checked */
00685 
00686     if ((dir[LDIR_Ord] & 0x40) && wc && lfnbuf[i])    /* Last segment matched but different length */
00687         return FALSE;
00688 
00689     return TRUE;                    /* The part of LFN matched */
00690 }
00691 
00692 
00693 
00694 static
00695 BOOL pick_lfn (            /* TRUE:Succeeded, FALSE:Buffer overflow */
00696     WCHAR *lfnbuf,        /* Pointer to the Unicode-LFN buffer */
00697     BYTE *dir            /* Pointer to the directory entry */
00698 )
00699 {
00700     int i, s;
00701     WCHAR wc, uc;
00702 
00703 
00704     i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13;    /* Offset in the LFN buffer */
00705 
00706     s = 0; wc = 1;
00707     do {
00708         uc = LD_WORD(dir+LfnOfs[s]);            /* Pick an LFN character from the entry */
00709         if (wc) {    /* Last char has not been processed */
00710             if (i >= _MAX_LFN) return FALSE;    /* Buffer overflow? */
00711             lfnbuf[i++] = wc = uc;                /* Store it */
00712         } else {
00713             if (uc != 0xFFFF) return FALSE;        /* Check filler */
00714         }
00715     } while (++s < 13);                        /* Read all character in the entry */
00716 
00717     if (dir[LDIR_Ord] & 0x40) {                /* Put terminator if it is the last LFN part */
00718         if (i >= _MAX_LFN) return FALSE;    /* Buffer overflow? */
00719         lfnbuf[i] = 0;
00720     }
00721 
00722     return TRUE;
00723 }
00724 
00725 
00726 #if !_FS_READONLY
00727 static
00728 void fit_lfn (
00729     const WCHAR *lfnbuf,    /* Pointer to the LFN buffer */
00730     BYTE *dir,                /* Pointer to the directory entry */
00731     BYTE ord,                /* LFN order (1-20) */
00732     BYTE sum                /* SFN sum */
00733 )
00734 {
00735     int i, s;
00736     WCHAR wc;
00737 
00738 
00739     dir[LDIR_Chksum] = sum;            /* Set check sum */
00740     dir[LDIR_Attr] = AM_LFN;        /* Set attribute. LFN entry */
00741     dir[LDIR_Type] = 0;
00742     ST_WORD(dir+LDIR_FstClusLO, 0);
00743 
00744     i = (ord - 1) * 13;                /* Get offset in the LFN buffer */
00745     s = wc = 0;
00746     do {
00747         if (wc != 0xFFFF) wc = lfnbuf[i++];    /* Get an effective char */
00748         ST_WORD(dir+LfnOfs[s], wc);    /* Put it */
00749         if (!wc) wc = 0xFFFF;        /* Padding chars following last char */
00750     } while (++s < 13);
00751     if (wc == 0xFFFF || !lfnbuf[i]) ord |= 0x40;    /* Bottom LFN part is the start of LFN sequence */
00752     dir[LDIR_Ord] = ord;            /* Set the LFN order */
00753 }
00754 
00755 #endif
00756 #endif
00757 
00758 
00759 
00760 /*-----------------------------------------------------------------------*/
00761 /* Create numbered name                                                  */
00762 /*-----------------------------------------------------------------------*/
00763 #if _USE_LFN
00764 void gen_numname (
00765     BYTE *dst,            /* Pointer to genartated SFN */
00766     const BYTE *src,    /* Pointer to source SFN to be modified */
00767     const WCHAR *lfn,    /* Pointer to LFN */
00768     WORD num            /* Sequense number */
00769 )
00770 {
00771     char ns[8];
00772     int i, j;
00773 
00774 
00775     mem_cpy(dst, src, 11);
00776 
00777     if (num > 5) {    /* On many collisions, generate a hash number instead of sequencial number */
00778         do num = (num >> 1) + (num << 15) + (WORD)*lfn++; while (*lfn);
00779     }
00780 
00781     /* itoa */
00782     i = 7;
00783     do {
00784         ns[i--] = (num % 10) + '0';
00785         num /= 10;
00786     } while (num);
00787     ns[i] = '~';
00788 
00789     /* Append the number */
00790     for (j = 0; j < i && dst[j] != ' '; j++) {
00791         if (IsDBCS1(dst[j])) {
00792             if (j == i - 1) break;
00793             j++;
00794         }
00795     }
00796     do {
00797         dst[j++] = (i < 8) ? ns[i++] : ' ';
00798     } while (j < 8);
00799 }
00800 #endif
00801 
00802 
00803 
00804 
00805 /*-----------------------------------------------------------------------*/
00806 /* Calculate sum of an SFN                                               */
00807 /*-----------------------------------------------------------------------*/
00808 #if _USE_LFN
00809 static
00810 BYTE sum_sfn (
00811     const BYTE *dir        /* Ptr to directory entry */
00812 )
00813 {
00814     BYTE sum = 0;
00815     int n = 11;
00816 
00817     do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n);
00818     return sum;
00819 }
00820 #endif
00821 
00822 
00823 
00824 
00825 /*-----------------------------------------------------------------------*/
00826 /* Directory handling - Find an object in the directory                  */
00827 /*-----------------------------------------------------------------------*/
00828 
00829 static
00830 FRESULT dir_find (
00831     eDIR *dj            /* Pointer to the directory object linked to the file name */
00832 )
00833 {
00834     FRESULT res;
00835     BYTE c, *dir;
00836 #if _USE_LFN
00837     BYTE a, ord, sum;
00838 #endif
00839 
00840     res = dir_seek(dj, 0);            /* Rewind directory object */
00841     if (res != FR_OK) return res;
00842 
00843 #if _USE_LFN
00844     ord = sum = 0xFF;
00845 #endif
00846     do {
00847         res = move_window(dj->fs, dj->sect);
00848         if (res != FR_OK) break;
00849         dir = dj->dir;                    /* Ptr to the directory entry of current index */
00850         c = dir[DIR_Name];
00851         if (c == 0) { res = FR_NO_FILE; break; }    /* Reached to end of table */
00852 #if _USE_LFN    /* LFN configuration */
00853         a = dir[DIR_Attr] & AM_MASK;
00854         if (c == 0xE5 || ((a & AM_VOL) && a != AM_LFN)) {    /* An entry without valid data */
00855             ord = 0xFF;
00856         } else {
00857             if (a == AM_LFN) {            /* An LFN entry is found */
00858                 if (dj->lfn) {
00859                     if (c & 0x40) {        /* Is it start of LFN sequence? */
00860                         sum = dir[LDIR_Chksum];
00861                         c &= 0xBF; ord = c;    /* LFN start order */
00862                         dj->lfn_idx = dj->index;
00863                     }
00864                     /* Check validity of the LFN entry and compare it with given name */
00865                     ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF;
00866                 }
00867             } else {                    /* An SFN entry is found */
00868                 if (!ord && sum == sum_sfn(dir)) break;    /* LFN matched? */
00869                 ord = 0xFF; dj->lfn_idx = 0xFFFF;    /* Reset LFN sequence */
00870                 if (!(dj->fn[NS] & NS_LOSS) && !mem_cmp(dir, dj->fn, 11)) break;    /* SFN matched? */
00871             }
00872         }
00873 #else        /* Non LFN configuration */
00874         if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dj->fn, 11)) /* Is it a valid entry? */
00875             break;
00876 #endif
00877         res = dir_next(dj, FALSE);        /* Next entry */
00878     } while (res == FR_OK);
00879 
00880     return res;
00881 }
00882 
00883 
00884 
00885 
00886 /*-----------------------------------------------------------------------*/
00887 /* Read an object from the directory                                     */
00888 /*-----------------------------------------------------------------------*/
00889 #if _FS_MINIMIZE <= 1
00890 static
00891 FRESULT dir_read (
00892     eDIR *dj            /* Pointer to the directory object that pointing the entry to be read */
00893 )
00894 {
00895     FRESULT res;
00896     BYTE c, *dir;
00897 #if _USE_LFN
00898     BYTE a, ord = 0xFF, sum = 0xFF;
00899 #endif
00900 
00901     res = FR_NO_FILE;
00902     while (dj->sect) {
00903         res = move_window(dj->fs, dj->sect);
00904         if (res != FR_OK) break;
00905         dir = dj->dir;                    /* Ptr to the directory entry of current index */
00906         c = dir[DIR_Name];
00907         if (c == 0) { res = FR_NO_FILE; break; }    /* Reached to end of table */
00908 #if _USE_LFN    /* LFN configuration */
00909         a = dir[DIR_Attr] & AM_MASK;
00910         if (c == 0xE5 || (!_FS_RPATH && c == '.') || ((a & AM_VOL) && a != AM_LFN)) {    /* An entry without valid data */
00911             ord = 0xFF;
00912         } else {
00913             if (a == AM_LFN) {            /* An LFN entry is found */
00914                 if (c & 0x40) {            /* Is it start of LFN sequence? */
00915                     sum = dir[LDIR_Chksum];
00916                     c &= 0xBF; ord = c;
00917                     dj->lfn_idx = dj->index;
00918                 }
00919                 /* Check LFN validity and capture it */
00920                 ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF;
00921             } else {                    /* An SFN entry is found */
00922                 if (ord || sum != sum_sfn(dir))    /* Is there a valid LFN? */
00923                     dj->lfn_idx = 0xFFFF;        /* It has no LFN. */
00924                 break;
00925             }
00926         }
00927 #else        /* Non LFN configuration */
00928         if (c != 0xE5 && (_FS_RPATH || c != '.') && !(dir[DIR_Attr] & AM_VOL))    /* Is it a valid entry? */
00929             break;
00930 #endif
00931         res = dir_next(dj, FALSE);                /* Next entry */
00932         if (res != FR_OK) break;
00933     }
00934 
00935     if (res != FR_OK) dj->sect = 0;
00936 
00937     return res;
00938 }
00939 #endif
00940 
00941 
00942 
00943 /*-----------------------------------------------------------------------*/
00944 /* Register an object to the directory                                   */
00945 /*-----------------------------------------------------------------------*/
00946 #if !_FS_READONLY
00947 static
00948 FRESULT dir_register (    /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */
00949     eDIR *dj                /* Target directory with object name to be created */
00950 )
00951 {
00952     FRESULT res;
00953     BYTE c, *dir;
00954 #if _USE_LFN    /* LFN configuration */
00955     WORD n, ne, is;
00956     BYTE sn[12], *fn, sum;
00957     WCHAR *lfn;
00958 
00959 
00960     fn = dj->fn; lfn = dj->lfn;
00961     mem_cpy(sn, fn, 12);
00962 
00963     if (_FS_RPATH && (sn[NS] & NS_DOT)) return FR_INVALID_NAME;    /* Cannot create dot entry */
00964 
00965     if (sn[NS] & NS_LOSS) {            /* When LFN is out of 8.3 format, generate a numbered name */
00966         fn[NS] = 0; dj->lfn = NULL;            /* Find only SFN */
00967         for (n = 1; n < 100; n++) {
00968             gen_numname(fn, sn, lfn, n);    /* Generate a numbered name */
00969             res = dir_find(dj);                /* Check if the name collides with existing SFN */
00970             if (res != FR_OK) break;
00971         }
00972         if (n == 100) return FR_DENIED;        /* Abort if too many collisions */
00973         if (res != FR_NO_FILE) return res;    /* Abort if the result is other than 'not collided' */
00974         fn[NS] = sn[NS]; dj->lfn = lfn;
00975     }
00976 
00977     if (sn[NS] & NS_LFN) {            /* When LFN is to be created, reserve reserve an SFN + LFN entries. */
00978         for (ne = 0; lfn[ne]; ne++) ;
00979         ne = (ne + 25) / 13;
00980     } else {                        /* Otherwise reserve only an SFN entry. */
00981         ne = 1;
00982     }
00983 
00984     /* Reserve contiguous entries */
00985     res = dir_seek(dj, 0);
00986     if (res != FR_OK) return res;
00987     n = is = 0;
00988     do {
00989         res = move_window(dj->fs, dj->sect);
00990         if (res != FR_OK) break;
00991         c = *dj->dir;                /* Check the entry status */
00992         if (c == 0xE5 || c == 0) {    /* Is it a blank entry? */
00993             if (n == 0) is = dj->index;    /* First index of the contigulus entry */
00994             if (++n == ne) break;    /* A contiguous entry that requiered count is found */
00995         } else {
00996             n = 0;                    /* Not a blank entry. Restart to search */
00997         }
00998         res = dir_next(dj, TRUE);    /* Next entry with table streach */
00999     } while (res == FR_OK);
01000 
01001     if (res == FR_OK && ne > 1) {    /* Initialize LFN entry if needed */
01002         res = dir_seek(dj, is);
01003         if (res == FR_OK) {
01004             sum = sum_sfn(dj->fn);    /* Sum of the SFN tied to the LFN */
01005             ne--;
01006             do {                    /* Store LFN entries in bottom first */
01007                 res = move_window(dj->fs, dj->sect);
01008                 if (res != FR_OK) break;
01009                 fit_lfn(dj->lfn, dj->dir, (BYTE)ne, sum);
01010                 dj->fs->wflag = 1;
01011                 res = dir_next(dj, FALSE);    /* Next entry */
01012             } while (res == FR_OK && --ne);
01013         }
01014     }
01015 
01016 #else    /* Non LFN configuration */
01017     res = dir_seek(dj, 0);
01018     if (res == FR_OK) {
01019         do {    /* Find a blank entry for the SFN */
01020             res = move_window(dj->fs, dj->sect);
01021             if (res != FR_OK) break;
01022             c = *dj->dir;
01023             if (c == 0xE5 || c == 0) break;    /* Is it a blank entry? */
01024             res = dir_next(dj, TRUE);        /* Next entry with table streach */
01025         } while (res == FR_OK);
01026     }
01027 #endif
01028 
01029     if (res == FR_OK) {        /* Initialize the SFN entry */
01030         res = move_window(dj->fs, dj->sect);
01031         if (res == FR_OK) {
01032             dir = dj->dir;
01033             mem_set(dir, 0, 32);        /* Clean the entry */
01034             mem_cpy(dir, dj->fn, 11);    /* Put SFN */
01035             dir[DIR_NTres] = *(dj->fn+NS) & (NS_BODY | NS_EXT);    /* Put NT flag */
01036             dj->fs->wflag = 1;
01037         }
01038     }
01039 
01040     return res;
01041 }
01042 #endif /* !_FS_READONLY */
01043 
01044 
01045 
01046 
01047 /*-----------------------------------------------------------------------*/
01048 /* Remove an object from the directory                                   */
01049 /*-----------------------------------------------------------------------*/
01050 #if !_FS_READONLY && !_FS_MINIMIZE
01051 static
01052 FRESULT dir_remove (    /* FR_OK: Successful, FR_DISK_ERR: A disk error */
01053     eDIR *dj                /* Directory object pointing the entry to be removed */
01054 )
01055 {
01056     FRESULT res;
01057 #if _USE_LFN    /* LFN configuration */
01058     WORD i;
01059 
01060     i = dj->index;    /* SFN index */
01061     res = dir_seek(dj, (WORD)((dj->lfn_idx == 0xFFFF) ? i : dj->lfn_idx));    /* Goto the SFN or top of the LFN entries */
01062     if (res == FR_OK) {
01063         do {
01064             res = move_window(dj->fs, dj->sect);
01065             if (res != FR_OK) break;
01066             *dj->dir = 0xE5;            /* Mark the entry "deleted" */
01067             dj->fs->wflag = 1;
01068             if (dj->index >= i) break;    /* When reached SFN, all entries of the object has been deleted. */
01069             res = dir_next(dj, FALSE);    /* Next entry */
01070         } while (res == FR_OK);
01071         if (res == FR_NO_FILE) res = FR_INT_ERR;
01072     }
01073 
01074 #else            /* Non LFN configuration */
01075     res = dir_seek(dj, dj->index);
01076     if (res == FR_OK) {
01077         res = move_window(dj->fs, dj->sect);
01078         if (res == FR_OK) {
01079             *dj->dir = 0xE5;            /* Mark the entry "deleted" */
01080             dj->fs->wflag = 1;
01081         }
01082     }
01083 #endif
01084 
01085     return res;
01086 }
01087 #endif /* !_FS_READONLY */
01088 
01089 
01090 
01091 
01092 /*-----------------------------------------------------------------------*/
01093 /* Pick a segment and create the object name in directory form           */
01094 /*-----------------------------------------------------------------------*/
01095 
01096 static
01097 FRESULT create_name (
01098     eDIR *dj,            /* Pointer to the directory object */
01099     const XCHAR **path    /* Pointer to pointer to the segment in the path string */
01100 )
01101 {
01102 #ifdef _EXCVT
01103     static const BYTE cvt[] = _EXCVT;
01104 #endif
01105 
01106 #if _USE_LFN    /* LFN configuration */
01107     BYTE b, cf;
01108     WCHAR w, *lfn;
01109     int i, ni, si, di;
01110     const XCHAR *p;
01111 
01112     /* Create LFN in Unicode */
01113     si = di = 0;
01114     p = *path;
01115     lfn = dj->lfn;
01116     for (;;) {
01117         w = p[si++];                    /* Get a character */
01118         if (w < ' ' || w == '/' || w == '\\') break;    /* Break on end of segment */
01119         if (di >= _MAX_LFN)                /* Reject too long name */
01120             return FR_INVALID_NAME;
01121 #if !_LFN_UNICODE
01122         w &= 0xFF;
01123         if (IsDBCS1(w)) {                /* If it is a DBC 1st byte */
01124             b = p[si++];                /* Get 2nd byte */
01125             if (!IsDBCS2(b))            /* Reject invalid code for DBC */
01126                 return FR_INVALID_NAME;
01127             w = (w << 8) + b;
01128         }
01129         w = ff_convert(w, 1);            /* Convert OEM to Unicode */
01130         if (!w) return FR_INVALID_NAME;    /* Reject invalid code */
01131 #endif
01132         if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) /* Reject illegal chars for LFN */
01133             return FR_INVALID_NAME;
01134         lfn[di++] = w;                    /* Store the Unicode char */
01135     }
01136     *path = &p[si];                        /* Rerurn pointer to the next segment */
01137     cf = (w < ' ') ? NS_LAST : 0;        /* Set last segment flag if end of path */
01138 #if _FS_RPATH
01139     if ((di == 1 && lfn[di - 1] == '.') || /* Is this a dot entry? */
01140         (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) {
01141         lfn[di] = 0;
01142         for (i = 0; i < 11; i++)
01143             dj->fn[i] = (i < di) ? '.' : ' ';
01144         dj->fn[i] = cf | NS_DOT;        /* This is a dot entry */
01145         return FR_OK;
01146     }
01147 #endif
01148     while (di) {                        /* Strip trailing spaces and dots */
01149         w = lfn[di - 1];
01150         if (w != ' ' && w != '.') break;
01151         di--;
01152     }
01153     if (!di) return FR_INVALID_NAME;    /* Reject null string */
01154 
01155     lfn[di] = 0;                        /* LFN is created */
01156 
01157     /* Create SFN in directory form */
01158     mem_set(dj->fn, ' ', 11);
01159     for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ;    /* Strip leading spaces and dots */
01160     if (si) cf |= NS_LOSS | NS_LFN;
01161     while (di && lfn[di - 1] != '.') di--;    /* Find extension (di<=si: no extension) */
01162 
01163     b = i = 0; ni = 8;
01164     for (;;) {
01165         w = lfn[si++];                    /* Get an LFN char */
01166         if (!w) break;                    /* Break on enf of the LFN */
01167         if (w == ' ' || (w == '.' && si != di)) {    /* Remove spaces and dots */
01168             cf |= NS_LOSS | NS_LFN; continue;
01169         }
01170 
01171         if (i >= ni || si == di) {        /* Extension or end of SFN */
01172             if (ni == 11) {                /* Long extension */
01173                 cf |= NS_LOSS | NS_LFN; break;
01174             }
01175             if (si != di) cf |= NS_LOSS | NS_LFN;    /* Out of 8.3 format */
01176             if (si > di) break;            /* No extension */
01177             si = di; i = 8; ni = 11;    /* Enter extension section */
01178             b <<= 2; continue;
01179         }
01180 
01181         if (w >= 0x80) {                /* Non ASCII char */
01182 #ifdef _EXCVT
01183             w = ff_convert(w, 0);        /* Unicode -> OEM code */
01184             if (w) w = cvt[w - 0x80];    /* Convert extended char to upper (SBCS) */
01185 #else
01186             w = ff_convert(ff_wtoupper(w), 0);    /* Upper converted Unicode -> OEM code */
01187 #endif
01188             cf |= NS_LFN;                /* Force create LFN entry */
01189         }
01190 
01191         if (_DF1S && w >= 0x100) {        /* Double byte char */
01192             if (i >= ni - 1) {
01193                 cf |= NS_LOSS | NS_LFN; i = ni; continue;
01194             }
01195             dj->fn[i++] = (BYTE)(w >> 8);
01196         } else {                        /* Single byte char */
01197             if (!w || chk_chr("+,;[=]", w)) {        /* Replace illegal chars for SFN */
01198                 w = '_'; cf |= NS_LOSS | NS_LFN;    /* Lossy conversion */
01199             } else {
01200                 if (IsUpper(w)) {        /* ASCII large capital */
01201                     b |= 2;
01202                 } else {
01203                     if (IsLower(w)) {    /* ASCII small capital */
01204                         b |= 1; w -= 0x20;
01205                     }
01206                 }
01207             }
01208         }
01209         dj->fn[i++] = (BYTE)w;
01210     }
01211 
01212     if (dj->fn[0] == 0xE5) dj->fn[0] = 0x05;    /* If the first char collides with deleted mark, replace it with 0x05 */
01213 
01214     if (ni == 8) b <<= 2;
01215     if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03)    /* Create LFN entry when there are composite capitals */
01216         cf |= NS_LFN;
01217     if (!(cf & NS_LFN)) {                        /* When LFN is in 8.3 format without extended char, NT flags are created */
01218         if ((b & 0x03) == 0x01) cf |= NS_EXT;    /* NT flag (Extension has only small capital) */
01219         if ((b & 0x0C) == 0x04) cf |= NS_BODY;    /* NT flag (Filename has only small capital) */
01220     }
01221 
01222     dj->fn[NS] = cf;    /* SFN is created */
01223 
01224     return FR_OK;
01225 
01226 
01227 #else    /* Non-LFN configuration */
01228     BYTE b, c, d, *sfn;
01229     int ni, si, i;
01230     const char *p;
01231 
01232     /* Create file name in directory form */
01233     sfn = dj->fn;
01234     mem_set(sfn, ' ', 11);
01235     si = i = b = 0; ni = 8;
01236     p = *path;
01237 #if _FS_RPATH
01238     if (p[si] == '.') { /* Is this a dot entry? */
01239         for (;;) {
01240             c = p[si++];
01241             if (c != '.' || si >= 3) break;
01242             sfn[i++] = c;
01243         }
01244         if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME;
01245         *path = &p[si];                                    /* Rerurn pointer to the next segment */
01246         sfn[NS] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT;    /* Set last segment flag if end of path */
01247         return FR_OK;
01248     }
01249 #endif
01250     for (;;) {
01251         c = p[si++];
01252         if (c <= ' ' || c == '/' || c == '\\') break;    /* Break on end of segment */
01253         if (c == '.' || i >= ni) {
01254             if (ni != 8 || c != '.') return FR_INVALID_NAME;
01255             i = 8; ni = 11;
01256             b <<= 2; continue;
01257         }
01258         if (c >= 0x80) {                /* Extended char */
01259 #ifdef _EXCVT
01260             c = cvt[c - 0x80];            /* Convert extend char (SBCS) */
01261 #else
01262             b |= 3;                        /* Eliminate NT flag if ext char is exist */
01263 #if !_DF1S    /* ASCII only cfg */
01264             return FR_INVALID_NAME;
01265 #endif
01266 #endif
01267         }
01268         if (IsDBCS1(c)) {                /* DBC 1st byte? */
01269             d = p[si++];                /* Get 2nd byte */
01270             if (!IsDBCS2(d) || i >= ni - 1)    /* Reject invalid DBC */
01271                 return FR_INVALID_NAME;
01272             sfn[i++] = c;
01273             sfn[i++] = d;
01274         } else {                        /* Single byte code */
01275             if (chk_chr(" \"*+,[=]|\x7F", c))    /* Reject illegal chrs for SFN */
01276                 return FR_INVALID_NAME;
01277             if (IsUpper(c)) {            /* ASCII large capital? */
01278                 b |= 2;
01279             } else {
01280                 if (IsLower(c)) {        /* ASCII small capital? */
01281                     b |= 1; c -= 0x20;
01282                 }
01283             }
01284             sfn[i++] = c;
01285         }
01286     }
01287     *path = &p[si];                        /* Rerurn pointer to the next segment */
01288     c = (c <= ' ') ? NS_LAST : 0;        /* Set last segment flag if end of path */
01289 
01290     if (!i) return FR_INVALID_NAME;        /* Reject null string */
01291     if (sfn[0] == 0xE5) sfn[0] = 0x05;    /* When first char collides with 0xE5, replace it with 0x05 */
01292 
01293     if (ni == 8) b <<= 2;
01294     if ((b & 0x03) == 0x01) c |= NS_EXT;    /* NT flag (Extension has only small capital) */
01295     if ((b & 0x0C) == 0x04) c |= NS_BODY;    /* NT flag (Filename has only small capital) */
01296 
01297     sfn[NS] = c;        /* Store NT flag, File name is created */
01298 
01299     return FR_OK;
01300 #endif
01301 }
01302 
01303 
01304 
01305 
01306 /*-----------------------------------------------------------------------*/
01307 /* Get file information from directory entry                             */
01308 /*-----------------------------------------------------------------------*/
01309 #if _FS_MINIMIZE <= 1
01310 static
01311 void get_fileinfo (        /* No return code */
01312     eDIR *dj,            /* Pointer to the directory object */
01313     FILINFO *fno         /* Pointer to the file information to be filled */
01314 )
01315 {
01316     int i;
01317     BYTE c, nt, *dir;
01318     char *p;
01319 
01320 
01321     p = fno->fname;
01322     if (dj->sect) {
01323         dir = dj->dir;
01324         nt = dir[DIR_NTres];        /* NT flag */
01325         for (i = 0; i < 8; i++) {    /* Copy name body */
01326             c = dir[i];
01327             if (c == ' ') break;
01328             if (c == 0x05) c = 0xE5;
01329             if (_USE_LFN && (nt & NS_BODY) && IsUpper(c)) c += 0x20;
01330             *p++ = c;
01331         }
01332         if (dir[8] != ' ') {        /* Copy name extension */
01333             *p++ = '.';
01334             for (i = 8; i < 11; i++) {
01335                 c = dir[i];
01336                 if (c == ' ') break;
01337                 if (_USE_LFN && (nt & NS_EXT) && IsUpper(c)) c += 0x20;
01338                 *p++ = c;
01339             }
01340         }
01341         fno->fattrib = dir[DIR_Attr];                /* Attribute */
01342         fno->fsize = LD_DWORD(dir+DIR_FileSize);    /* Size */
01343         fno->fdate = LD_WORD(dir+DIR_WrtDate);        /* Date */
01344         fno->ftime = LD_WORD(dir+DIR_WrtTime);        /* Time */
01345     }
01346     *p = 0;
01347 
01348 #if _USE_LFN
01349     if (fno->lfname) {
01350         XCHAR *tp = fno->lfname;
01351         WCHAR w, *lfn;
01352 
01353         i = 0;
01354         if (dj->sect && dj->lfn_idx != 0xFFFF) {/* Get LFN if available */
01355             lfn = dj->lfn;
01356             while ((w = *lfn++) != 0) {            /* Get an LFN char */
01357 #if !_LFN_UNICODE
01358                 w = ff_convert(w, 0);            /* Unicode -> OEM conversion */
01359                 if (!w) { i = 0; break; }        /* Could not convert, no LFN */
01360                 if (_DF1S && w >= 0x100)        /* Put 1st byte if it is a DBC */
01361                     tp[i++] = (XCHAR)(w >> 8);
01362 #endif
01363                 if (i >= fno->lfsize - 1) { i = 0; break; }    /* Buffer overrun, no LFN */
01364                 tp[i++] = (XCHAR)w;
01365             }
01366         }
01367         tp[i] = 0;    /* Terminator */
01368     }
01369 #endif
01370 }
01371 #endif /* _FS_MINIMIZE <= 1 */
01372 
01373 
01374 
01375 
01376 /*-----------------------------------------------------------------------*/
01377 /* Follow a file path                                                    */
01378 /*-----------------------------------------------------------------------*/
01379 
01380 static
01381 FRESULT follow_path (    /* FR_OK(0): successful, !=0: error code */
01382     eDIR *dj,            /* Directory object to return last directory and found object */
01383     const XCHAR *path    /* Full-path string to find a file or directory */
01384 )
01385 {
01386     FRESULT res;
01387     BYTE *dir, last;
01388 
01389 
01390     while (!_USE_LFN && *path == ' ') path++;    /* Skip leading spaces */
01391 #if _FS_RPATH
01392     if (*path == '/' || *path == '\\') { /* There is a heading separator */
01393         path++;    dj->sclust = 0;        /* Strip it and start from the root dir */
01394     } else {                            /* No heading saparator */
01395         dj->sclust = dj->fs->cdir;    /* Start from the current dir */
01396     }
01397 #else
01398     if (*path == '/' || *path == '\\')    /* Strip heading separator if exist */
01399         path++;
01400     dj->sclust = 0;                        /* Start from the root dir */
01401 #endif
01402 
01403     if ((UINT)*path < ' ') {            /* Null path means the start directory itself */
01404         res = dir_seek(dj, 0);
01405         dj->dir = NULL;
01406 
01407     } else {                            /* Follow path */
01408         for (;;) {
01409             res = create_name(dj, &path);    /* Get a segment */
01410             if (res != FR_OK) break;
01411             res = dir_find(dj);                /* Find it */
01412             last = *(dj->fn+NS) & NS_LAST;
01413             if (res != FR_OK) {                /* Could not find the object */
01414                 if (res == FR_NO_FILE && !last)
01415                     res = FR_NO_PATH;
01416                 break;
01417             }
01418             if (last) break;                /* Last segment match. Function completed. */
01419             dir = dj->dir;                    /* There is next segment. Follow the sub directory */
01420             if (!(dir[DIR_Attr] & AM_DIR)) { /* Cannot follow because it is a file */
01421                 res = FR_NO_PATH; break;
01422             }
01423             dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
01424         }
01425     }
01426 
01427     return res;
01428 }
01429 
01430 
01431 
01432 
01433 /*-----------------------------------------------------------------------*/
01434 /* Load boot record and check if it is an FAT boot record                */
01435 /*-----------------------------------------------------------------------*/
01436 
01437 static
01438 BYTE check_fs (    /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record, 3:Error */
01439     FATFS *fs,    /* File system object */
01440     DWORD sect    /* Sector# (lba) to check if it is an FAT boot record or not */
01441 )
01442 {
01443     if (disk_read(fs->drive, fs->win, sect, 1) != RES_OK)    /* Load boot record */
01444         return 3;
01445     if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55)        /* Check record signature (always placed at offset 510 even if the sector size is >512) */
01446         return 2;
01447 
01448     if ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146)    /* Check "FAT" string */
01449         return 0;
01450     if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146)
01451         return 0;
01452 
01453     return 1;
01454 }
01455 
01456 
01457 
01458 
01459 /*-----------------------------------------------------------------------*/
01460 /* Make sure that the file system is valid                               */
01461 /*-----------------------------------------------------------------------*/
01462 
01463 
01464 FRESULT chk_mounted (    /* FR_OK(0): successful, !=0: any error occured */
01465     const XCHAR **path,    /* Pointer to pointer to the path name (drive number) */
01466     FATFS **rfs,        /* Pointer to pointer to the found file system object */
01467     BYTE chk_wp            /* !=0: Check media write protection for write access */
01468 )
01469 {
01470     BYTE fmt, *tbl;
01471     UINT vol;
01472     DSTATUS stat;
01473     DWORD bsect, fsize, tsect, mclst;
01474     const XCHAR *p = *path;
01475     FATFS *fs;
01476 
01477     /* Get logical drive number from the path name */
01478     vol = p[0] - '0';                /* Is there a drive number? */
01479     if (vol <= 9 && p[1] == ':') {    /* Found a drive number, get and strip it */
01480         p += 2; *path = p;            /* Return pointer to the path name */
01481     } else {                        /* No drive number is given */
01482 #if _FS_RPATH
01483         vol = Drive;                /* Use current drive */
01484 #else
01485         vol = 0;                    /* Use drive 0 */
01486 #endif
01487     }
01488 
01489     /* Check if the logical drive is valid or not */
01490     if (vol >= _DRIVES)             /* Is the drive number valid? */
01491         return FR_INVALID_DRIVE;
01492     *rfs = fs = FatFs[vol];            /* Returen pointer to the corresponding file system object */
01493     if (!fs) return FR_NOT_ENABLED;    /* Is the file system object available? */
01494 
01495     ENTER_FF(fs);                    /* Lock file system */
01496 
01497     if (fs->fs_type) {                /* If the logical drive has been mounted */
01498         stat = disk_status(fs->drive);
01499         if (!(stat & STA_NOINIT)) {    /* and the physical drive is kept initialized (has not been changed), */
01500 #if !_FS_READONLY
01501             if (chk_wp && (stat & STA_PROTECT))    /* Check write protection if needed */
01502                 return FR_WRITE_PROTECTED;
01503 #endif
01504             return FR_OK;            /* The file system object is valid */
01505         }
01506     }
01507 
01508     /* The logical drive must be mounted. Following code attempts to mount the volume */
01509 
01510     fs->fs_type = 0;                    /* Clear the file system object */
01511     fs->drive = (BYTE)LD2PD(vol);        /* Bind the logical drive and a physical drive */
01512     stat = disk_initialize(fs->drive);    /* Initialize low level disk I/O layer */
01513     if (stat & STA_NOINIT)                /* Check if the drive is ready */
01514         return FR_NOT_READY;
01515 #if _MAX_SS != 512                        /* Get disk sector size if needed */
01516     if (disk_ioctl(fs->drive, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS)
01517         return FR_NO_FILESYSTEM;
01518 #endif
01519 #if !_FS_READONLY
01520     if (chk_wp && (stat & STA_PROTECT))    /* Check disk write protection if needed */
01521         return FR_WRITE_PROTECTED;
01522 #endif
01523     /* Search FAT partition on the drive */
01524     fmt = check_fs(fs, bsect = 0);        /* Check sector 0 as an SFD format */
01525     if (fmt == 1) {                        /* Not an FAT boot record, it may be patitioned */
01526         /* Check a partition listed in top of the partition table */
01527         tbl = &fs->win[MBR_Table + LD2PT(vol) * 16];    /* Partition table */
01528         if (tbl[4]) {                                    /* Is the partition existing? */
01529             bsect = LD_DWORD(&tbl[8]);                    /* Partition offset in LBA */
01530             fmt = check_fs(fs, bsect);                    /* Check the partition */
01531         }
01532     }
01533     if (fmt == 3) return FR_DISK_ERR;
01534     if (fmt || LD_WORD(fs->win+BPB_BytsPerSec) != SS(fs))    /* No valid FAT patition is found */
01535         return FR_NO_FILESYSTEM;
01536 
01537     /* Initialize the file system object */
01538     fsize = LD_WORD(fs->win+BPB_FATSz16);                /* Number of sectors per FAT */
01539     if (!fsize) fsize = LD_DWORD(fs->win+BPB_FATSz32);
01540     fs->sects_fat = fsize;
01541     fs->n_fats = fs->win[BPB_NumFATs];                    /* Number of FAT copies */
01542     fsize *= fs->n_fats;                                /* (Number of sectors in FAT area) */
01543     fs->fatbase = bsect + LD_WORD(fs->win+BPB_RsvdSecCnt); /* FAT start sector (lba) */
01544     fs->csize = fs->win[BPB_SecPerClus];                /* Number of sectors per cluster */
01545     fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt);    /* Nmuber of root directory entries */
01546     tsect = LD_WORD(fs->win+BPB_TotSec16);                /* Number of sectors on the volume */
01547     if (!tsect) tsect = LD_DWORD(fs->win+BPB_TotSec32);
01548     fs->max_clust = mclst = (tsect                        /* Last cluster# + 1 (Number of clusters + 2) */
01549         - LD_WORD(fs->win+BPB_RsvdSecCnt) - fsize - fs->n_rootdir / (SS(fs)/32)
01550         ) / fs->csize + 2;
01551 
01552     fmt = FS_FAT12;                                        /* Determine the FAT sub type */
01553     if (mclst >= 0xFF7) fmt = FS_FAT16;                    /* Number of clusters >= 0xFF5 */
01554     if (mclst >= 0xFFF7) fmt = FS_FAT32;                /* Number of clusters >= 0xFFF5 */
01555 
01556     if (fmt == FS_FAT32)
01557         fs->dirbase = LD_DWORD(fs->win+BPB_RootClus);    /* Root directory start cluster */
01558     else
01559         fs->dirbase = fs->fatbase + fsize;                /* Root directory start sector (lba) */
01560     fs->database = fs->fatbase + fsize + fs->n_rootdir / (SS(fs)/32);    /* Data start sector (lba) */
01561 
01562 #if !_FS_READONLY
01563     /* Initialize allocation information */
01564     fs->free_clust = 0xFFFFFFFF;
01565     fs->wflag = 0;
01566     /* Get fsinfo if needed */
01567     if (fmt == FS_FAT32) {
01568          fs->fsi_flag = 0;
01569         fs->fsi_sector = bsect + LD_WORD(fs->win+BPB_FSInfo);
01570         if (disk_read(fs->drive, fs->win, fs->fsi_sector, 1) == RES_OK &&
01571             LD_WORD(fs->win+BS_55AA) == 0xAA55 &&
01572             LD_DWORD(fs->win+FSI_LeadSig) == 0x41615252 &&
01573             LD_DWORD(fs->win+FSI_StrucSig) == 0x61417272) {
01574             fs->last_clust = LD_DWORD(fs->win+FSI_Nxt_Free);
01575             fs->free_clust = LD_DWORD(fs->win+FSI_Free_Count);
01576         }
01577     }
01578 #endif
01579     fs->fs_type = fmt;        /* FAT sub-type */
01580     fs->winsect = 0;        /* Invalidate sector cache */
01581 #if _FS_RPATH
01582     fs->cdir = 0;            /* Current directory (root dir) */
01583 #endif
01584     fs->id = ++Fsid;        /* File system mount ID */
01585 
01586     return FR_OK;
01587 }
01588 
01589 
01590 
01591 
01592 /*-----------------------------------------------------------------------*/
01593 /* Check if the file/dir object is valid or not                          */
01594 /*-----------------------------------------------------------------------*/
01595 
01596 static
01597 FRESULT validate (    /* FR_OK(0): The object is valid, !=0: Invalid */
01598     FATFS *fs,        /* Pointer to the file system object */
01599     WORD id            /* Member id of the target object to be checked */
01600 )
01601 {
01602     if (!fs || !fs->fs_type || fs->id != id)
01603         return FR_INVALID_OBJECT;
01604 
01605     ENTER_FF(fs);        /* Lock file system */
01606 
01607     if (disk_status(fs->drive) & STA_NOINIT)
01608         return FR_NOT_READY;
01609 
01610     return FR_OK;
01611 }
01612 
01613 
01614 
01615 
01616 /*--------------------------------------------------------------------------
01617 
01618    Public Functions
01619 
01620 --------------------------------------------------------------------------*/
01621 
01622 
01623 
01624 /*-----------------------------------------------------------------------*/
01625 /* Mount/Unmount a Locical Drive                                         */
01626 /*-----------------------------------------------------------------------*/
01627 
01628 FRESULT f_mount (
01629     BYTE vol,        /* Logical drive number to be mounted/unmounted */
01630     FATFS *fs        /* Pointer to new file system object (NULL for unmount)*/
01631 )
01632 {
01633     FATFS *rfs;
01634 
01635 
01636     if (vol >= _DRIVES)                /* Check if the drive number is valid */
01637         return FR_INVALID_DRIVE;
01638     rfs = FatFs[vol];                /* Get current fs object */
01639 
01640     if (rfs) {
01641 #if _FS_REENTRANT                    /* Discard sync object of the current volume */
01642         if (!ff_del_syncobj(rfs->sobj)) return FR_INT_ERR;
01643 #endif
01644         rfs->fs_type = 0;            /* Clear old fs object */
01645     }
01646 
01647     if (fs) {
01648         fs->fs_type = 0;            /* Clear new fs object */
01649 #if _FS_REENTRANT                    /* Create sync object for the new volume */
01650         if (!ff_cre_syncobj(vol, &fs->sobj)) return FR_INT_ERR;
01651 #endif
01652     }
01653     FatFs[vol] = fs;                /* Register new fs object */
01654 
01655     return FR_OK;
01656 }
01657 
01658 
01659 
01660 
01661 /*-----------------------------------------------------------------------*/
01662 /* Open or Create a File                                                 */
01663 /*-----------------------------------------------------------------------*/
01664 
01665 FRESULT f_open (
01666     FIL *fp,            /* Pointer to the blank file object */
01667     const XCHAR *path,    /* Pointer to the file name */
01668     BYTE mode            /* Access mode and file open mode flags */
01669 )
01670 {
01671     FRESULT res;
01672     eDIR dj;
01673     NAMEBUF(sfn, lfn);
01674     BYTE *dir;
01675 
01676 
01677     fp->fs = NULL;        /* Clear file object */
01678 #if !_FS_READONLY
01679     mode &= (FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW);
01680     res = chk_mounted(&path, &dj.fs, (BYTE)(mode & (FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)));
01681 #else
01682     mode &= FA_READ;
01683     res = chk_mounted(&path, &dj.fs, 0);
01684 #endif
01685     if (res != FR_OK) LEAVE_FF(dj.fs, res);
01686     INITBUF(dj, sfn, lfn);
01687     res = follow_path(&dj, path);    /* Follow the file path */
01688 
01689 #if !_FS_READONLY
01690     /* Create or Open a file */
01691     if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) {
01692         DWORD ps, cl;
01693 
01694         if (res != FR_OK) {            /* No file, create new */
01695             if (res == FR_NO_FILE)    /* There is no file to open, create a new entry */
01696                 res = dir_register(&dj);
01697             if (res != FR_OK) LEAVE_FF(dj.fs, res);
01698             mode |= FA_CREATE_ALWAYS;
01699             dir = dj.dir;            /* Created entry (SFN entry) */
01700         }
01701         else {                        /* Any object is already existing */
01702             if (mode & FA_CREATE_NEW)            /* Cannot create new */
01703                 LEAVE_FF(dj.fs, FR_EXIST);
01704             dir = dj.dir;
01705             if (!dir || (dir[DIR_Attr] & (AM_RDO | AM_DIR)))    /* Cannot overwrite it (R/O or DIR) */
01706                 LEAVE_FF(dj.fs, FR_DENIED);
01707             if (mode & FA_CREATE_ALWAYS) {        /* Resize it to zero on over write mode */
01708                 cl = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);    /* Get start cluster */
01709                 ST_WORD(dir+DIR_FstClusHI, 0);    /* cluster = 0 */
01710                 ST_WORD(dir+DIR_FstClusLO, 0);
01711                 ST_DWORD(dir+DIR_FileSize, 0);    /* size = 0 */
01712                 dj.fs->wflag = 1;
01713                 ps = dj.fs->winsect;            /* Remove the cluster chain */
01714                 if (cl) {
01715                     res = remove_chain(dj.fs, cl);
01716                     if (res) LEAVE_FF(dj.fs, res);
01717                     dj.fs->last_clust = cl - 1;    /* Reuse the cluster hole */
01718                 }
01719                 res = move_window(dj.fs, ps);
01720                 if (res != FR_OK) LEAVE_FF(dj.fs, res);
01721             }
01722         }
01723         if (mode & FA_CREATE_ALWAYS) {
01724             dir[DIR_Attr] = 0;                    /* Reset attribute */
01725             ps = get_fattime();
01726             ST_DWORD(dir+DIR_CrtTime, ps);        /* Created time */
01727             dj.fs->wflag = 1;
01728             mode |= FA__WRITTEN;                /* Set file changed flag */
01729         }
01730     }
01731     /* Open an existing file */
01732     else {
01733 #endif /* !_FS_READONLY */
01734         if (res != FR_OK) LEAVE_FF(dj.fs, res);    /* Follow failed */
01735         dir = dj.dir;
01736         if (!dir || (dir[DIR_Attr] & AM_DIR))    /* It is a directory */
01737             LEAVE_FF(dj.fs, FR_NO_FILE);
01738 #if !_FS_READONLY
01739         if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */
01740             LEAVE_FF(dj.fs, FR_DENIED);
01741     }
01742     fp->dir_sect = dj.fs->winsect;        /* Pointer to the directory entry */
01743     fp->dir_ptr = dj.dir;
01744 #endif
01745     fp->flag = mode;                    /* File access mode */
01746     fp->org_clust =                        /* File start cluster */
01747         ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
01748     fp->fsize = LD_DWORD(dir+DIR_FileSize);    /* File size */
01749     fp->fptr = 0; fp->csect = 255;        /* File pointer */
01750     fp->dsect = 0;
01751     fp->fs = dj.fs; fp->id = dj.fs->id;    /* Owner file system object of the file */
01752 
01753     LEAVE_FF(dj.fs, FR_OK);
01754 }
01755 
01756 
01757 
01758 
01759 /*-----------------------------------------------------------------------*/
01760 /* Read File                                                             */
01761 /*-----------------------------------------------------------------------*/
01762 
01763 FRESULT f_read (
01764     FIL *fp,         /* Pointer to the file object */
01765     void *buff,        /* Pointer to data buffer */
01766     UINT btr,        /* Number of bytes to read */
01767     UINT *br        /* Pointer to number of bytes read */
01768 )
01769 {
01770     FRESULT res;
01771     DWORD clst, sect, remain;
01772     UINT rcnt, cc;
01773     BYTE *rbuff = (BYTE *)buff;
01774 
01775 
01776     *br = 0;    /* Initialize bytes read */
01777 
01778     res = validate(fp->fs, fp->id);                    /* Check validity of the object */
01779     if (res != FR_OK) LEAVE_FF(fp->fs, res);
01780     if (fp->flag & FA__ERROR)                        /* Check abort flag */
01781         LEAVE_FF(fp->fs, FR_INT_ERR);
01782     if (!(fp->flag & FA_READ))                         /* Check access mode */
01783         LEAVE_FF(fp->fs, FR_DENIED);
01784     remain = fp->fsize - fp->fptr;
01785     if (btr > remain) btr = (UINT)remain;            /* Truncate btr by remaining bytes */
01786 
01787     for ( ;  btr;                                    /* Repeat until all data transferred */
01788         rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) {
01789         if ((fp->fptr % SS(fp->fs)) == 0) {            /* On the sector boundary? */
01790             if (fp->csect >= fp->fs->csize) {        /* On the cluster boundary? */
01791                 clst = (fp->fptr == 0) ?            /* On the top of the file? */
01792                     fp->org_clust : get_fat(fp->fs, fp->curr_clust);
01793                 if (clst <= 1) ABORT(fp->fs, FR_INT_ERR);
01794                 if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
01795                 fp->curr_clust = clst;                /* Update current cluster */
01796                 fp->csect = 0;                        /* Reset sector offset in the cluster */
01797             }
01798             sect = clust2sect(fp->fs, fp->curr_clust);    /* Get current sector */
01799             if (!sect) ABORT(fp->fs, FR_INT_ERR);
01800             sect += fp->csect;
01801             cc = btr / SS(fp->fs);                    /* When remaining bytes >= sector size, */
01802             if (cc) {                                /* Read maximum contiguous sectors directly */
01803                 if (fp->csect + cc > fp->fs->csize)    /* Clip at cluster boundary */
01804                     cc = fp->fs->csize - fp->csect;
01805                 if (disk_read(fp->fs->drive, rbuff, sect, (BYTE)cc) != RES_OK)
01806                     ABORT(fp->fs, FR_DISK_ERR);
01807 #if !_FS_READONLY && _FS_MINIMIZE <= 2
01808 #if _FS_TINY
01809                 if (fp->fs->wflag && fp->fs->winsect - sect < cc)        /* Replace one of the read sectors with cached data if it contains a dirty sector */
01810                     mem_cpy(rbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), fp->fs->win, SS(fp->fs));
01811 #else
01812                 if ((fp->flag & FA__DIRTY) && fp->dsect - sect < cc)    /* Replace one of the read sectors with cached data if it contains a dirty sector */
01813                     mem_cpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs));
01814 #endif
01815 #endif
01816                 fp->csect += (BYTE)cc;                /* Next sector address in the cluster */
01817                 rcnt = SS(fp->fs) * cc;                /* Number of bytes transferred */
01818                 continue;
01819             }
01820 #if !_FS_TINY
01821 #if !_FS_READONLY
01822             if (fp->flag & FA__DIRTY) {            /* Write sector I/O buffer if needed */
01823                 if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK)
01824                     ABORT(fp->fs, FR_DISK_ERR);
01825                 fp->flag &= ~FA__DIRTY;
01826             }
01827 #endif
01828             if (fp->dsect != sect) {            /* Fill sector buffer with file data */
01829                 if (disk_read(fp->fs->drive, fp->buf, sect, 1) != RES_OK)
01830                     ABORT(fp->fs, FR_DISK_ERR);
01831             }
01832 #endif
01833             fp->dsect = sect;
01834             fp->csect++;                            /* Next sector address in the cluster */
01835         }
01836         rcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs));    /* Get partial sector data from sector buffer */
01837         if (rcnt > btr) rcnt = btr;
01838 #if _FS_TINY
01839         if (move_window(fp->fs, fp->dsect))            /* Move sector window */
01840             ABORT(fp->fs, FR_DISK_ERR);
01841         mem_cpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt);    /* Pick partial sector */
01842 #else
01843         mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt);    /* Pick partial sector */
01844 #endif
01845     }
01846 
01847     LEAVE_FF(fp->fs, FR_OK);
01848 }
01849 
01850 
01851 
01852 
01853 #if !_FS_READONLY
01854 /*-----------------------------------------------------------------------*/
01855 /* Write File                                                            */
01856 /*-----------------------------------------------------------------------*/
01857 
01858 FRESULT f_write (
01859     FIL *fp,            /* Pointer to the file object */
01860     const void *buff,    /* Pointer to the data to be written */
01861     UINT btw,            /* Number of bytes to write */
01862     UINT *bw            /* Pointer to number of bytes written */
01863 )
01864 {
01865     FRESULT res;
01866     DWORD clst, sect;
01867     UINT wcnt, cc;
01868     const BYTE *wbuff = (const BYTE *)buff;
01869 
01870 
01871     *bw = 0;    /* Initialize bytes written */
01872 
01873     res = validate(fp->fs, fp->id);                    /* Check validity of the object */
01874     if (res != FR_OK) LEAVE_FF(fp->fs, res);
01875     if (fp->flag & FA__ERROR)                        /* Check abort flag */
01876         LEAVE_FF(fp->fs, FR_INT_ERR);
01877     if (!(fp->flag & FA_WRITE))                        /* Check access mode */
01878         LEAVE_FF(fp->fs, FR_DENIED);
01879     if (fp->fsize + btw < fp->fsize) btw = 0;        /* File size cannot reach 4GB */
01880 
01881     for ( ;  btw;                                    /* Repeat until all data transferred */
01882         wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) {
01883         if ((fp->fptr % SS(fp->fs)) == 0) {            /* On the sector boundary? */
01884             if (fp->csect >= fp->fs->csize) {        /* On the cluster boundary? */
01885                 if (fp->fptr == 0) {                /* On the top of the file? */
01886                     clst = fp->org_clust;            /* Follow from the origin */
01887                     if (clst == 0)                    /* When there is no cluster chain, */
01888                         fp->org_clust = clst = create_chain(fp->fs, 0);    /* Create a new cluster chain */
01889                 } else {                            /* Middle or end of the file */
01890                     clst = create_chain(fp->fs, fp->curr_clust);            /* Follow or streach cluster chain */
01891                 }
01892                 if (clst == 0) break;                /* Could not allocate a new cluster (disk full) */
01893                 if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
01894                 if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
01895                 fp->curr_clust = clst;                /* Update current cluster */
01896                 fp->csect = 0;                        /* Reset sector address in the cluster */
01897             }
01898 #if _FS_TINY
01899             if (fp->fs->winsect == fp->dsect && move_window(fp->fs, 0))    /* Write back data buffer prior to following direct transfer */
01900                 ABORT(fp->fs, FR_DISK_ERR);
01901 #else
01902             if (fp->flag & FA__DIRTY) {        /* Write back data buffer prior to following direct transfer */
01903                 if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK)
01904                     ABORT(fp->fs, FR_DISK_ERR);
01905                 fp->flag &= ~FA__DIRTY;
01906             }
01907 #endif
01908             sect = clust2sect(fp->fs, fp->curr_clust);    /* Get current sector */
01909             if (!sect) ABORT(fp->fs, FR_INT_ERR);
01910             sect += fp->csect;
01911             cc = btw / SS(fp->fs);                    /* When remaining bytes >= sector size, */
01912             if (cc) {                                /* Write maximum contiguous sectors directly */
01913                 if (fp->csect + cc > fp->fs->csize)    /* Clip at cluster boundary */
01914                     cc = fp->fs->csize - fp->csect;
01915                 if (disk_write(fp->fs->drive, wbuff, sect, (BYTE)cc) != RES_OK)
01916                     ABORT(fp->fs, FR_DISK_ERR);
01917 #if _FS_TINY
01918                 if (fp->fs->winsect - sect < cc) {    /* Refill sector cache if it gets dirty by the direct write */
01919                     mem_cpy(fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs));
01920                     fp->fs->wflag = 0;
01921                 }
01922 #else
01923                 if (fp->dsect - sect < cc) {        /* Refill sector cache if it gets dirty by the direct write */
01924                     mem_cpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs));
01925                     fp->flag &= ~FA__DIRTY;
01926                 }
01927 #endif
01928                 fp->csect += (BYTE)cc;                /* Next sector address in the cluster */
01929                 wcnt = SS(fp->fs) * cc;                /* Number of bytes transferred */
01930                 continue;
01931             }
01932 #if _FS_TINY
01933             if (fp->fptr >= fp->fsize) {            /* Avoid silly buffer filling at growing edge */
01934                 if (move_window(fp->fs, 0)) ABORT(fp->fs, FR_DISK_ERR);
01935                 fp->fs->winsect = sect;
01936             }
01937 #else
01938             if (fp->dsect != sect) {                /* Fill sector buffer with file data */
01939                 if (fp->fptr < fp->fsize &&
01940                     disk_read(fp->fs->drive, fp->buf, sect, 1) != RES_OK)
01941                         ABORT(fp->fs, FR_DISK_ERR);
01942             }
01943 #endif
01944             fp->dsect = sect;
01945             fp->csect++;                            /* Next sector address in the cluster */
01946         }
01947         wcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs));    /* Put partial sector into file I/O buffer */
01948         if (wcnt > btw) wcnt = btw;
01949 #if _FS_TINY
01950         if (move_window(fp->fs, fp->dsect))            /* Move sector window */
01951             ABORT(fp->fs, FR_DISK_ERR);
01952         mem_cpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt);    /* Fit partial sector */
01953         fp->fs->wflag = 1;
01954 #else
01955         mem_cpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt);    /* Fit partial sector */
01956         fp->flag |= FA__DIRTY;
01957 #endif
01958     }
01959 
01960     if (fp->fptr > fp->fsize) fp->fsize = fp->fptr;    /* Update file size if needed */
01961     fp->flag |= FA__WRITTEN;                        /* Set file changed flag */
01962 
01963     LEAVE_FF(fp->fs, FR_OK);
01964 }
01965 
01966 
01967 
01968 
01969 /*-----------------------------------------------------------------------*/
01970 /* Synchronize the File Object                                           */
01971 /*-----------------------------------------------------------------------*/
01972 
01973 FRESULT f_sync (
01974     FIL *fp        /* Pointer to the file object */
01975 )
01976 {
01977     FRESULT res;
01978     DWORD tim;
01979     BYTE *dir;
01980 
01981 
01982     res = validate(fp->fs, fp->id);        /* Check validity of the object */
01983     if (res == FR_OK) {
01984         if (fp->flag & FA__WRITTEN) {    /* Has the file been written? */
01985 #if !_FS_TINY    /* Write-back dirty buffer */
01986             if (fp->flag & FA__DIRTY) {
01987                 if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK)
01988                     LEAVE_FF(fp->fs, FR_DISK_ERR);
01989                 fp->flag &= ~FA__DIRTY;
01990             }
01991 #endif
01992             /* Update the directory entry */
01993             res = move_window(fp->fs, fp->dir_sect);
01994             if (res == FR_OK) {
01995                 dir = fp->dir_ptr;
01996                 dir[DIR_Attr] |= AM_ARC;                    /* Set archive bit */
01997                 ST_DWORD(dir+DIR_FileSize, fp->fsize);        /* Update file size */
01998                 ST_WORD(dir+DIR_FstClusLO, fp->org_clust);    /* Update start cluster */
01999                 ST_WORD(dir+DIR_FstClusHI, fp->org_clust >> 16);
02000                 tim = get_fattime();            /* Updated time */
02001                 ST_DWORD(dir+DIR_WrtTime, tim);
02002                 fp->flag &= ~FA__WRITTEN;
02003                 fp->fs->wflag = 1;
02004                 res = sync(fp->fs);
02005             }
02006         }
02007     }
02008 
02009     LEAVE_FF(fp->fs, res);
02010 }
02011 
02012 #endif /* !_FS_READONLY */
02013 
02014 
02015 
02016 
02017 /*-----------------------------------------------------------------------*/
02018 /* Close File                                                            */
02019 /*-----------------------------------------------------------------------*/
02020 
02021 FRESULT f_close (
02022     FIL *fp        /* Pointer to the file object to be closed */
02023 )
02024 {
02025     FRESULT res;
02026 
02027 
02028 #if _FS_READONLY
02029     res = validate(fp->fs, fp->id);
02030     if (res == FR_OK) fp->fs = NULL;
02031     LEAVE_FF(fp->fs, res);
02032 #else
02033     res = f_sync(fp);
02034     if (res == FR_OK) fp->fs = NULL;
02035     return res;
02036 #endif
02037 }
02038 
02039 
02040 
02041 
02042 /*-----------------------------------------------------------------------*/
02043 /* Change Current Drive/Directory                                        */
02044 /*-----------------------------------------------------------------------*/
02045 
02046 #if _FS_RPATH
02047 
02048 FRESULT f_chdrive (
02049     BYTE drv        /* Drive number */
02050 )
02051 {
02052     if (drv >= _DRIVES) return FR_INVALID_DRIVE;
02053 
02054     Drive = drv;
02055 
02056     return FR_OK;
02057 }
02058 
02059 
02060 
02061 
02062 FRESULT f_chdir (
02063     const XCHAR *path    /* Pointer to the directory path */
02064 )
02065 {
02066     FRESULT res;
02067     DIR dj;
02068     NAMEBUF(sfn, lfn);
02069     BYTE *dir;
02070 
02071 
02072     res = chk_mounted(&path, &dj.fs, 0);
02073     if (res == FR_OK) {
02074         INITBUF(dj, sfn, lfn);
02075         res = follow_path(&dj, path);        /* Follow the file path */
02076         if (res == FR_OK) {                    /* Follow completed */
02077             dir = dj.dir;                    /* Pointer to the entry */
02078             if (!dir) {
02079                 dj.fs->cdir = 0;            /* No entry (root dir) */
02080             } else {
02081                 if (dir[DIR_Attr] & AM_DIR)    /* Reached to the dir */
02082                     dj.fs->cdir = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
02083                 else
02084                     res = FR_NO_PATH;        /* Could not reach the dir (it is a file) */
02085             }
02086         }
02087         if (res == FR_NO_FILE) res = FR_NO_PATH;
02088     }
02089 
02090     LEAVE_FF(dj.fs, res);
02091 }
02092 
02093 #endif
02094 
02095 
02096 
02097 #if _FS_MINIMIZE <= 2
02098 /*-----------------------------------------------------------------------*/
02099 /* Seek File R/W Pointer                                                 */
02100 /*-----------------------------------------------------------------------*/
02101 
02102 FRESULT f_lseek (
02103     FIL *fp,        /* Pointer to the file object */
02104     DWORD ofs        /* File pointer from top of file */
02105 )
02106 {
02107     FRESULT res;
02108     DWORD clst, bcs, nsect, ifptr;
02109 
02110 
02111     res = validate(fp->fs, fp->id);        /* Check validity of the object */
02112     if (res != FR_OK) LEAVE_FF(fp->fs, res);
02113     if (fp->flag & FA__ERROR)            /* Check abort flag */
02114         LEAVE_FF(fp->fs, FR_INT_ERR);
02115 //    if (ofs > fp->fsize                    /* In read-only mode, clip offset with the file size */
02116 //#if !_FS_READONLY /* LAJ: Always clip offset with file size */
02117 //         && !(fp->flag & FA_WRITE)
02118 //#endif
02119     if (ofs > fp->fsize)
02120         ofs = fp->fsize; /* LAJ: Always clip offset with file size */
02121 
02122     ifptr = fp->fptr;
02123     fp->fptr = nsect = 0; fp->csect = 255;
02124     if (ofs > 0) {
02125         bcs = (DWORD)fp->fs->csize * SS(fp->fs);    /* Cluster size (byte) */
02126         if (ifptr > 0 &&
02127             (ofs - 1) / bcs >= (ifptr - 1) / bcs) {    /* When seek to same or following cluster, */
02128             fp->fptr = (ifptr - 1) & ~(bcs - 1);    /* start from the current cluster */
02129             ofs -= fp->fptr;
02130             clst = fp->curr_clust;
02131         } else {                                    /* When seek to back cluster, */
02132             clst = fp->org_clust;                    /* start from the first cluster */
02133 #if !_FS_READONLY
02134             if (clst == 0) {                        /* If no cluster chain, create a new chain */
02135                 clst = create_chain(fp->fs, 0);
02136                 if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
02137                 if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
02138                 fp->org_clust = clst;
02139             }
02140 #endif
02141             fp->curr_clust = clst;
02142         }
02143         if (clst != 0) {
02144             while (ofs > bcs) {                        /* Cluster following loop */
02145 #if !_FS_READONLY
02146                 if (fp->flag & FA_WRITE) {            /* Check if in write mode or not */
02147                     clst = create_chain(fp->fs, clst);    /* Force streached if in write mode */
02148                     if (clst == 0) {                /* When disk gets full, clip file size */
02149                         ofs = bcs; break;
02150                     }
02151                 } else
02152 #endif
02153                     clst = get_fat(fp->fs, clst);    /* Follow cluster chain if not in write mode */
02154                 if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
02155                 if (clst <= 1 || clst >= fp->fs->max_clust) ABORT(fp->fs, FR_INT_ERR);
02156                 fp->curr_clust = clst;
02157                 fp->fptr += bcs;
02158                 ofs -= bcs;
02159             }
02160             fp->fptr += ofs;
02161             fp->csect = (BYTE)(ofs / SS(fp->fs));    /* Sector offset in the cluster */
02162             if (ofs % SS(fp->fs)) {
02163                 nsect = clust2sect(fp->fs, clst);    /* Current sector */
02164                 if (!nsect) ABORT(fp->fs, FR_INT_ERR);
02165                 nsect += fp->csect;
02166                 fp->csect++;
02167             }
02168         }
02169     }
02170     if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) {
02171 #if !_FS_TINY
02172 #if !_FS_READONLY
02173         if (fp->flag & FA__DIRTY) {            /* Write-back dirty buffer if needed */
02174             if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK)
02175                 ABORT(fp->fs, FR_DISK_ERR);
02176             fp->flag &= ~FA__DIRTY;
02177         }
02178 #endif
02179         if (disk_read(fp->fs->drive, fp->buf, nsect, 1) != RES_OK)
02180             ABORT(fp->fs, FR_DISK_ERR);
02181 #endif
02182         fp->dsect = nsect;
02183     }
02184 #if !_FS_READONLY
02185     if (fp->fptr > fp->fsize) {            /* Set changed flag if the file size is extended */
02186         fp->fsize = fp->fptr;
02187         fp->flag |= FA__WRITTEN;
02188     }
02189 #endif
02190 
02191     LEAVE_FF(fp->fs, res);
02192 }
02193 
02194 
02195 
02196 
02197 #if _FS_MINIMIZE <= 1
02198 /*-----------------------------------------------------------------------*/
02199 /* Create a Directroy Object                                             */
02200 /*-----------------------------------------------------------------------*/
02201 
02202 FRESULT f_opendir (
02203     eDIR *dj,            /* Pointer to directory object to create */
02204     const XCHAR *path    /* Pointer to the directory path */
02205 )
02206 {
02207     FRESULT res;
02208     NAMEBUF(sfn, lfn);
02209     BYTE *dir;
02210 
02211 
02212     res = chk_mounted(&path, &dj->fs, 0);
02213     if (res == FR_OK) {
02214         INITBUF((*dj), sfn, lfn);
02215         res = follow_path(dj, path);            /* Follow the path to the directory */
02216         if (res == FR_OK) {                        /* Follow completed */
02217             dir = dj->dir;
02218             if (dir) {                            /* It is not the root dir */
02219                 if (dir[DIR_Attr] & AM_DIR) {    /* The object is a directory */
02220                     dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
02221                 } else {                        /* The object is not a directory */
02222                     res = FR_NO_PATH;
02223                 }
02224             }
02225             if (res == FR_OK) {
02226                 dj->id = dj->fs->id;
02227                 res = dir_seek(dj, 0);            /* Rewind dir */
02228             }
02229         }
02230         if (res == FR_NO_FILE) res = FR_NO_PATH;
02231     }
02232 
02233     LEAVE_FF(dj->fs, res);
02234 }
02235 
02236 
02237 
02238 
02239 /*-----------------------------------------------------------------------*/
02240 /* Read Directory Entry in Sequense                                      */
02241 /*-----------------------------------------------------------------------*/
02242 
02243 FRESULT f_readdir (
02244     eDIR *dj,            /* Pointer to the open directory object */
02245     FILINFO *fno        /* Pointer to file information to return */
02246 )
02247 {
02248     FRESULT res;
02249     NAMEBUF(sfn, lfn);
02250 
02251 
02252     res = validate(dj->fs, dj->id);            /* Check validity of the object */
02253     if (res == FR_OK) {
02254         INITBUF((*dj), sfn, lfn);
02255         if (!fno) {
02256             res = dir_seek(dj, 0);
02257         } else {
02258             res = dir_read(dj);
02259             if (res == FR_NO_FILE) {
02260                 dj->sect = 0;
02261                 res = FR_OK;
02262             }
02263             if (res == FR_OK) {                /* A valid entry is found */
02264                 get_fileinfo(dj, fno);        /* Get the object information */
02265                 res = dir_next(dj, FALSE);    /* Increment index for next */
02266                 if (res == FR_NO_FILE) {
02267                     dj->sect = 0;
02268                     res = FR_OK;
02269                 }
02270             }
02271         }
02272     }
02273 
02274     LEAVE_FF(dj->fs, res);
02275 }
02276 
02277 
02278 
02279 #if _FS_MINIMIZE == 0
02280 /*-----------------------------------------------------------------------*/
02281 /* Get File Status                                                       */
02282 /*-----------------------------------------------------------------------*/
02283 
02284 FRESULT f_stat (
02285     const XCHAR *path,    /* Pointer to the file path */
02286     FILINFO *fno        /* Pointer to file information to return */
02287 )
02288 {
02289     FRESULT res;
02290     eDIR dj;
02291     NAMEBUF(sfn, lfn);
02292 
02293 
02294     res = chk_mounted(&path, &dj.fs, 0);
02295     if (res == FR_OK) {
02296         INITBUF(dj, sfn, lfn);
02297         res = follow_path(&dj, path);    /* Follow the file path */
02298         if (res == FR_OK) {                /* Follwo completed */
02299             if (dj.dir)    /* Found an object */
02300                 get_fileinfo(&dj, fno);
02301             else        /* It is root dir */
02302                 res = FR_INVALID_NAME;
02303         }
02304     }
02305 
02306     LEAVE_FF(dj.fs, res);
02307 }
02308 
02309 
02310 
02311 #if !_FS_READONLY
02312 /*-----------------------------------------------------------------------*/
02313 /* Get Number of Free Clusters                                           */
02314 /*-----------------------------------------------------------------------*/
02315 
02316 FRESULT f_getfree (
02317     const XCHAR *path,    /* Pointer to the logical drive number (root dir) */
02318     DWORD *nclst,        /* Pointer to the variable to return number of free clusters */
02319     FATFS **fatfs        /* Pointer to pointer to corresponding file system object to return */
02320 )
02321 {
02322     FRESULT res;
02323     DWORD n, clst, sect, stat;
02324     UINT i;
02325     BYTE fat, *p;
02326 
02327 
02328     /* Get drive number */
02329     res = chk_mounted(&path, fatfs, 0);
02330     if (res != FR_OK) LEAVE_FF(*fatfs, res);
02331 
02332     /* If number of free cluster is valid, return it without cluster scan. */
02333     if ((*fatfs)->free_clust <= (*fatfs)->max_clust - 2) {
02334         *nclst = (*fatfs)->free_clust;
02335         LEAVE_FF(*fatfs, FR_OK);
02336     }
02337 
02338     /* Get number of free clusters */
02339     fat = (*fatfs)->fs_type;
02340     n = 0;
02341     if (fat == FS_FAT12) {
02342         clst = 2;
02343         do {
02344             stat = get_fat(*fatfs, clst);
02345             if (stat == 0xFFFFFFFF) LEAVE_FF(*fatfs, FR_DISK_ERR);
02346             if (stat == 1) LEAVE_FF(*fatfs, FR_INT_ERR);
02347             if (stat == 0) n++;
02348         } while (++clst < (*fatfs)->max_clust);
02349     } else {
02350         clst = (*fatfs)->max_clust;
02351         sect = (*fatfs)->fatbase;
02352         i = 0; p = 0;
02353         do {
02354             if (!i) {
02355                 res = move_window(*fatfs, sect++);
02356                 if (res != FR_OK)
02357                     LEAVE_FF(*fatfs, res);
02358                 p = (*fatfs)->win;
02359                 i = SS(*fatfs);
02360             }
02361             if (fat == FS_FAT16) {
02362                 if (LD_WORD(p) == 0) n++;
02363                 p += 2; i -= 2;
02364             } else {
02365                 if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) n++;
02366                 p += 4; i -= 4;
02367             }
02368         } while (--clst);
02369     }
02370     (*fatfs)->free_clust = n;
02371     if (fat == FS_FAT32) (*fatfs)->fsi_flag = 1;
02372     *nclst = n;
02373 
02374     LEAVE_FF(*fatfs, FR_OK);
02375 }
02376 
02377 
02378 
02379 
02380 /*-----------------------------------------------------------------------*/
02381 /* Truncate File                                                         */
02382 /*-----------------------------------------------------------------------*/
02383 
02384 FRESULT f_truncate (
02385     FIL *fp        /* Pointer to the file object */
02386 )
02387 {
02388     FRESULT res;
02389     DWORD ncl;
02390 
02391 
02392     res = validate(fp->fs, fp->id);        /* Check validity of the object */
02393     if (res != FR_OK) LEAVE_FF(fp->fs, res);
02394     if (fp->flag & FA__ERROR)            /* Check abort flag */
02395         LEAVE_FF(fp->fs, FR_INT_ERR);
02396     if (!(fp->flag & FA_WRITE))            /* Check access mode */
02397         LEAVE_FF(fp->fs, FR_DENIED);
02398 
02399     if (fp->fsize > fp->fptr) {
02400         fp->fsize = fp->fptr;    /* Set file size to current R/W point */
02401         fp->flag |= FA__WRITTEN;
02402         if (fp->fptr == 0) {    /* When set file size to zero, remove entire cluster chain */
02403             res = remove_chain(fp->fs, fp->org_clust);
02404             fp->org_clust = 0;
02405         } else {                /* When truncate a part of the file, remove remaining clusters */
02406             ncl = get_fat(fp->fs, fp->curr_clust);
02407             res = FR_OK;
02408             if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR;
02409             if (ncl == 1) res = FR_INT_ERR;
02410             if (res == FR_OK && ncl < fp->fs->max_clust) {
02411                 res = put_fat(fp->fs, fp->curr_clust, 0x0FFFFFFF);
02412                 if (res == FR_OK) res = remove_chain(fp->fs, ncl);
02413             }
02414         }
02415     }
02416     if (res != FR_OK) fp->flag |= FA__ERROR;
02417 
02418     LEAVE_FF(fp->fs, res);
02419 }
02420 
02421 
02422 
02423 
02424 /*-----------------------------------------------------------------------*/
02425 /* Delete a File or Directory                                            */
02426 /*-----------------------------------------------------------------------*/
02427 
02428 FRESULT f_unlink (
02429     const XCHAR *path        /* Pointer to the file or directory path */
02430 )
02431 {
02432     FRESULT res;
02433     eDIR dj, sdj;
02434     NAMEBUF(sfn, lfn);
02435     BYTE *dir;
02436     DWORD dclst;
02437 
02438 
02439     res = chk_mounted(&path, &dj.fs, 1);
02440     if (res != FR_OK) LEAVE_FF(dj.fs, res);
02441 
02442     INITBUF(dj, sfn, lfn);
02443     res = follow_path(&dj, path);            /* Follow the file path */
02444     if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
02445         res = FR_INVALID_NAME;
02446     if (res != FR_OK) LEAVE_FF(dj.fs, res); /* Follow failed */
02447 
02448     dir = dj.dir;
02449     if (!dir)                                /* Is it the root directory? */
02450         LEAVE_FF(dj.fs, FR_INVALID_NAME);
02451     if (dir[DIR_Attr] & AM_RDO)                /* Is it a R/O object? */
02452         LEAVE_FF(dj.fs, FR_DENIED);
02453     dclst = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
02454 
02455     if (dir[DIR_Attr] & AM_DIR) {            /* It is a sub-directory */
02456         if (dclst < 2) LEAVE_FF(dj.fs, FR_INT_ERR);
02457         mem_cpy(&sdj, &dj, sizeof(eDIR));    /* Check if the sub-dir is empty or not */
02458         sdj.sclust = dclst;
02459         res = dir_seek(&sdj, 2);
02460         if (res != FR_OK) LEAVE_FF(dj.fs, res);
02461         res = dir_read(&sdj);
02462         if (res == FR_OK) res = FR_DENIED;    /* Not empty sub-dir */
02463         if (res != FR_NO_FILE) LEAVE_FF(dj.fs, res);
02464     }
02465 
02466     res = dir_remove(&dj);                    /* Remove directory entry */
02467     if (res == FR_OK) {
02468         if (dclst)
02469             res = remove_chain(dj.fs, dclst);    /* Remove the cluster chain */
02470         if (res == FR_OK) res = sync(dj.fs);
02471     }
02472 
02473     LEAVE_FF(dj.fs, res);
02474 }
02475 
02476 
02477 
02478 
02479 /*-----------------------------------------------------------------------*/
02480 /* Create a Directory                                                    */
02481 /*-----------------------------------------------------------------------*/
02482 
02483 FRESULT f_mkdir (
02484     const XCHAR *path        /* Pointer to the directory path */
02485 )
02486 {
02487     FRESULT res;
02488     eDIR dj;
02489     NAMEBUF(sfn, lfn);
02490     BYTE *dir, n;
02491     DWORD dsect, dclst, pclst, tim;
02492 
02493 
02494     res = chk_mounted(&path, &dj.fs, 1);
02495     if (res != FR_OK) LEAVE_FF(dj.fs, res);
02496 
02497     INITBUF(dj, sfn, lfn);
02498     res = follow_path(&dj, path);            /* Follow the file path */
02499     if (res == FR_OK) res = FR_EXIST;        /* Any file or directory is already existing */
02500     if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NS] & NS_DOT))
02501         res = FR_INVALID_NAME;
02502     if (res != FR_NO_FILE)                    /* Any error occured */
02503         LEAVE_FF(dj.fs, res);
02504 
02505     dclst = create_chain(dj.fs, 0);            /* Allocate a new cluster for new directory table */
02506     res = FR_OK;
02507     if (dclst == 0) res = FR_DENIED;
02508     if (dclst == 1) res = FR_INT_ERR;
02509     if (dclst == 0xFFFFFFFF) res = FR_DISK_ERR;
02510     if (res == FR_OK)
02511         res = move_window(dj.fs, 0);
02512     if (res != FR_OK) LEAVE_FF(dj.fs, res);
02513     dsect = clust2sect(dj.fs, dclst);
02514 
02515     dir = dj.fs->win;                        /* Initialize the new directory table */
02516     mem_set(dir, 0, SS(dj.fs));
02517     mem_set(dir+DIR_Name, ' ', 8+3);        /* Create "." entry */
02518     dir[DIR_Name] = '.';
02519     dir[DIR_Attr] = AM_DIR;
02520     tim = get_fattime();
02521     ST_DWORD(dir+DIR_WrtTime, tim);
02522     ST_WORD(dir+DIR_FstClusLO, dclst);
02523     ST_WORD(dir+DIR_FstClusHI, dclst >> 16);
02524     mem_cpy(dir+32, dir, 32);             /* Create ".." entry */
02525     dir[33] = '.';
02526     pclst = dj.sclust;
02527     if (dj.fs->fs_type == FS_FAT32 && pclst == dj.fs->dirbase)
02528         pclst = 0;
02529     ST_WORD(dir+32+DIR_FstClusLO, pclst);
02530     ST_WORD(dir+32+DIR_FstClusHI, pclst >> 16);
02531     for (n = 0; n < dj.fs->csize; n++) {    /* Write dot entries and clear left sectors */
02532         dj.fs->winsect = dsect++;
02533         dj.fs->wflag = 1;
02534         res = move_window(dj.fs, 0);
02535         if (res) LEAVE_FF(dj.fs, res);
02536         mem_set(dir, 0, SS(dj.fs));
02537     }
02538 
02539     res = dir_register(&dj);
02540     if (res != FR_OK) {
02541         remove_chain(dj.fs, dclst);
02542     } else {
02543         dir = dj.dir;
02544         dir[DIR_Attr] = AM_DIR;                    /* Attribute */
02545         ST_DWORD(dir+DIR_WrtTime, tim);            /* Crated time */
02546         ST_WORD(dir+DIR_FstClusLO, dclst);        /* Table start cluster */
02547         ST_WORD(dir+DIR_FstClusHI, dclst >> 16);
02548         dj.fs->wflag = 1;
02549         res = sync(dj.fs);
02550     }
02551 
02552     LEAVE_FF(dj.fs, res);
02553 }
02554 
02555 
02556 
02557 
02558 /*-----------------------------------------------------------------------*/
02559 /* Change File Attribute                                                 */
02560 /*-----------------------------------------------------------------------*/
02561 
02562 FRESULT f_chmod (
02563     const XCHAR *path,    /* Pointer to the file path */
02564     BYTE value,            /* Attribute bits */
02565     BYTE mask            /* Attribute mask to change */
02566 )
02567 {
02568     FRESULT res;
02569     eDIR dj;
02570     NAMEBUF(sfn, lfn);
02571     BYTE *dir;
02572 
02573 
02574     res = chk_mounted(&path, &dj.fs, 1);
02575     if (res == FR_OK) {
02576         INITBUF(dj, sfn, lfn);
02577         res = follow_path(&dj, path);        /* Follow the file path */
02578         if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
02579             res = FR_INVALID_NAME;
02580         if (res == FR_OK) {
02581             dir = dj.dir;
02582             if (!dir) {                        /* Is it a root directory? */
02583                 res = FR_INVALID_NAME;
02584             } else {                        /* File or sub directory */
02585                 mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC;    /* Valid attribute mask */
02586                 dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask);    /* Apply attribute change */
02587                 dj.fs->wflag = 1;
02588                 res = sync(dj.fs);
02589             }
02590         }
02591     }
02592 
02593     LEAVE_FF(dj.fs, res);
02594 }
02595 
02596 
02597 
02598 
02599 /*-----------------------------------------------------------------------*/
02600 /* Change Timestamp                                                      */
02601 /*-----------------------------------------------------------------------*/
02602 
02603 FRESULT f_utime (
02604     const XCHAR *path,    /* Pointer to the file/directory name */
02605     const FILINFO *fno    /* Pointer to the timestamp to be set */
02606 )
02607 {
02608     FRESULT res;
02609     eDIR dj;
02610     NAMEBUF(sfn, lfn);
02611     BYTE *dir;
02612 
02613 
02614     res = chk_mounted(&path, &dj.fs, 1);
02615     if (res == FR_OK) {
02616         INITBUF(dj, sfn, lfn);
02617         res = follow_path(&dj, path);    /* Follow the file path */
02618         if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
02619             res = FR_INVALID_NAME;
02620         if (res == FR_OK) {
02621             dir = dj.dir;
02622             if (!dir) {                /* Root directory */
02623                 res = FR_INVALID_NAME;
02624             } else {                /* File or sub-directory */
02625                 ST_WORD(dir+DIR_WrtTime, fno->ftime);
02626                 ST_WORD(dir+DIR_WrtDate, fno->fdate);
02627                 dj.fs->wflag = 1;
02628                 res = sync(dj.fs);
02629             }
02630         }
02631     }
02632 
02633     LEAVE_FF(dj.fs, res);
02634 }
02635 
02636 
02637 
02638 
02639 /*-----------------------------------------------------------------------*/
02640 /* Rename File/Directory                                                 */
02641 /*-----------------------------------------------------------------------*/
02642 
02643 FRESULT f_rename (
02644     const XCHAR *path_old,    /* Pointer to the old name */
02645     const XCHAR *path_new    /* Pointer to the new name */
02646 )
02647 {
02648     FRESULT res;
02649     eDIR dj_old, dj_new;
02650     NAMEBUF(sfn, lfn);
02651     BYTE buf[21], *dir;
02652     DWORD dw;
02653 
02654 
02655     INITBUF(dj_old, sfn, lfn);
02656     res = chk_mounted(&path_old, &dj_old.fs, 1);
02657     if (res == FR_OK) {
02658         dj_new.fs = dj_old.fs;
02659         res = follow_path(&dj_old, path_old);    /* Check old object */
02660         if (_FS_RPATH && res == FR_OK && (dj_old.fn[NS] & NS_DOT))
02661             res = FR_INVALID_NAME;
02662     }
02663     if (res != FR_OK) LEAVE_FF(dj_old.fs, res);    /* The old object is not found */
02664 
02665     if (!dj_old.dir) LEAVE_FF(dj_old.fs, FR_NO_FILE);    /* Is root dir? */
02666     mem_cpy(buf, dj_old.dir+DIR_Attr, 21);        /* Save the object information */
02667 
02668     mem_cpy(&dj_new, &dj_old, sizeof(eDIR));
02669     res = follow_path(&dj_new, path_new);        /* Check new object */
02670     if (res == FR_OK) res = FR_EXIST;            /* The new object name is already existing */
02671     if (res == FR_NO_FILE) {                     /* Is it a valid path and no name collision? */
02672         res = dir_register(&dj_new);            /* Register the new object */
02673         if (res == FR_OK) {
02674             dir = dj_new.dir;                    /* Copy object information into new entry */
02675             mem_cpy(dir+13, buf+2, 19);
02676             dir[DIR_Attr] = buf[0] | AM_ARC;
02677             dj_old.fs->wflag = 1;
02678             if (dir[DIR_Attr] & AM_DIR) {        /* Update .. entry in the directory if needed */
02679                 dw = clust2sect(dj_new.fs, (DWORD)LD_WORD(dir+DIR_FstClusHI) | LD_WORD(dir+DIR_FstClusLO));
02680                 if (!dw) {
02681                     res = FR_INT_ERR;
02682                 } else {
02683                     res = move_window(dj_new.fs, dw);
02684                     dir = dj_new.fs->win+32;
02685                     if (res == FR_OK && dir[1] == '.') {
02686                         dw = (dj_new.fs->fs_type == FS_FAT32 && dj_new.sclust == dj_new.fs->dirbase) ? 0 : dj_new.sclust;
02687                         ST_WORD(dir+DIR_FstClusLO, dw);
02688                         ST_WORD(dir+DIR_FstClusHI, dw >> 16);
02689                         dj_new.fs->wflag = 1;
02690                     }
02691                 }
02692             }
02693             if (res == FR_OK) {
02694                 res = dir_remove(&dj_old);            /* Remove old entry */
02695                 if (res == FR_OK)
02696                     res = sync(dj_old.fs);
02697             }
02698         }
02699     }
02700 
02701     LEAVE_FF(dj_old.fs, res);
02702 }
02703 
02704 #endif /* !_FS_READONLY */
02705 #endif /* _FS_MINIMIZE == 0 */
02706 #endif /* _FS_MINIMIZE <= 1 */
02707 #endif /* _FS_MINIMIZE <= 2 */
02708 
02709 
02710 
02711 /*-----------------------------------------------------------------------*/
02712 /* Forward data to the stream directly (Available on only _FS_TINY cfg)  */
02713 /*-----------------------------------------------------------------------*/
02714 #if _USE_FORWARD && _FS_TINY
02715 
02716 FRESULT f_forward (
02717     FIL *fp,                         /* Pointer to the file object */
02718     UINT (*func)(const BYTE*,UINT),    /* Pointer to the streaming function */
02719     UINT btr,                        /* Number of bytes to forward */
02720     UINT *bf                        /* Pointer to number of bytes forwarded */
02721 )
02722 {
02723     FRESULT res;
02724     DWORD remain, clst, sect;
02725     UINT rcnt;
02726 
02727 
02728     *bf = 0;
02729 
02730     res = validate(fp->fs, fp->id);                    /* Check validity of the object */
02731     if (res != FR_OK) LEAVE_FF(fp->fs, res);
02732     if (fp->flag & FA__ERROR)                        /* Check error flag */
02733         LEAVE_FF(fp->fs, FR_INT_ERR);
02734     if (!(fp->flag & FA_READ))                        /* Check access mode */
02735         LEAVE_FF(fp->fs, FR_DENIED);
02736 
02737     remain = fp->fsize - fp->fptr;
02738     if (btr > remain) btr = (UINT)remain;            /* Truncate btr by remaining bytes */
02739 
02740     for ( ;  btr && (*func)(NULL, 0);                /* Repeat until all data transferred or stream becomes busy */
02741         fp->fptr += rcnt, *bf += rcnt, btr -= rcnt) {
02742         if ((fp->fptr % SS(fp->fs)) == 0) {            /* On the sector boundary? */
02743             if (fp->csect >= fp->fs->csize) {        /* On the cluster boundary? */
02744                 clst = (fp->fptr == 0) ?            /* On the top of the file? */
02745                     fp->org_clust : get_fat(fp->fs, fp->curr_clust);
02746                 if (clst <= 1) ABORT(fp->fs, FR_INT_ERR);
02747                 if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
02748                 fp->curr_clust = clst;                /* Update current cluster */
02749                 fp->csect = 0;                        /* Reset sector address in the cluster */
02750             }
02751             fp->csect++;                            /* Next sector address in the cluster */
02752         }
02753         sect = clust2sect(fp->fs, fp->curr_clust);    /* Get current data sector */
02754         if (!sect) ABORT(fp->fs, FR_INT_ERR);
02755         sect += fp->csect - 1;
02756         if (move_window(fp->fs, sect))                /* Move sector window */
02757             ABORT(fp->fs, FR_DISK_ERR);
02758         fp->dsect = sect;
02759         rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs));    /* Forward data from sector window */
02760         if (rcnt > btr) rcnt = btr;
02761         rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt);
02762         if (!rcnt) ABORT(fp->fs, FR_INT_ERR);
02763     }
02764 
02765     LEAVE_FF(fp->fs, FR_OK);
02766 }
02767 #endif /* _USE_FORWARD */
02768 
02769 
02770 
02771 #if _USE_MKFS && !_FS_READONLY
02772 /*-----------------------------------------------------------------------*/
02773 /* Create File System on the Drive                                       */
02774 /*-----------------------------------------------------------------------*/
02775 #define N_ROOTDIR    512            /* Multiple of 32 and <= 2048 */
02776 #define N_FATS        1            /* 1 or 2 */
02777 #define MAX_SECTOR    131072000UL    /* Maximum partition size */
02778 #define MIN_SECTOR    2000UL        /* Minimum partition size */
02779 
02780 
02781 FRESULT f_mkfs (
02782     BYTE drv,            /* Logical drive number */
02783     BYTE partition,        /* Partitioning rule 0:FDISK, 1:SFD */
02784     WORD allocsize        /* Allocation unit size [bytes] */
02785 )
02786 {
02787     static const DWORD sstbl[] = { 2048000, 1024000, 512000, 256000, 128000, 64000, 32000, 16000, 8000, 4000,   0 };
02788     static const WORD cstbl[] =  {   32768,   16384,   8192,   4096,   2048, 16384,  8192,  4096, 2048, 1024, 512 };
02789     BYTE fmt, m, *tbl;
02790     DWORD b_part, b_fat, b_dir, b_data;        /* Area offset (LBA) */
02791     DWORD n_part, n_rsv, n_fat, n_dir;        /* Area size */
02792     DWORD n_clst, d, n;
02793     WORD as;
02794     FATFS *fs;
02795     DSTATUS stat;
02796 
02797 
02798     /* Check validity of the parameters */
02799     if (drv >= _DRIVES) return FR_INVALID_DRIVE;
02800     if (partition >= 2) return FR_MKFS_ABORTED;
02801 
02802     /* Check mounted drive and clear work area */
02803     fs = FatFs[drv];
02804     if (!fs) return FR_NOT_ENABLED;
02805     fs->fs_type = 0;
02806     drv = LD2PD(drv);
02807 
02808     /* Get disk statics */
02809     stat = disk_initialize(drv);
02810     if (stat & STA_NOINIT) return FR_NOT_READY;
02811     if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
02812 #if _MAX_SS != 512                        /* Get disk sector size */
02813     if (disk_ioctl(drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK
02814         || SS(fs) > _MAX_SS)
02815         return FR_MKFS_ABORTED;
02816 #endif
02817     if (disk_ioctl(drv, GET_SECTOR_COUNT, &n_part) != RES_OK || n_part < MIN_SECTOR)
02818         return FR_MKFS_ABORTED;
02819     if (n_part > MAX_SECTOR) n_part = MAX_SECTOR;
02820     b_part = (!partition) ? 63 : 0;        /* Boot sector */
02821     n_part -= b_part;
02822     for (d = 512; d <= 32768U && d != allocsize; d <<= 1) ;    /* Check validity of the allocation unit size */
02823     if (d != allocsize) allocsize = 0;
02824     if (!allocsize) {                    /* Auto selection of cluster size */
02825         d = n_part;
02826         for (as = SS(fs); as > 512U; as >>= 1) d >>= 1;
02827         for (n = 0; d < sstbl[n]; n++) ;
02828         allocsize = cstbl[n];
02829     }
02830     if (allocsize < SS(fs)) allocsize = SS(fs);
02831 
02832     allocsize /= SS(fs);        /* Number of sectors per cluster */
02833 
02834     /* Pre-compute number of clusters and FAT type */
02835     n_clst = n_part / allocsize;
02836     fmt = FS_FAT12;
02837     if (n_clst >= 0xFF5) fmt = FS_FAT16;
02838     if (n_clst >= 0xFFF5) fmt = FS_FAT32;
02839 
02840     /* Determine offset and size of FAT structure */
02841     switch (fmt) {
02842     case FS_FAT12:
02843         n_fat = ((n_clst * 3 + 1) / 2 + 3 + SS(fs) - 1) / SS(fs);
02844         n_rsv = 1 + partition;
02845         n_dir = N_ROOTDIR * 32 / SS(fs);
02846         break;
02847     case FS_FAT16:
02848         n_fat = ((n_clst * 2) + 4 + SS(fs) - 1) / SS(fs);
02849         n_rsv = 1 + partition;
02850         n_dir = N_ROOTDIR * 32 / SS(fs);
02851         break;
02852     default:
02853         n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs);
02854         n_rsv = 33 - partition;
02855         n_dir = 0;
02856     }
02857     b_fat = b_part + n_rsv;            /* FATs start sector */
02858     b_dir = b_fat + n_fat * N_FATS;    /* Directory start sector */
02859     b_data = b_dir + n_dir;            /* Data start sector */
02860 
02861     /* Align data start sector to erase block boundary (for flash memory media) */
02862     if (disk_ioctl(drv, GET_BLOCK_SIZE, &n) != RES_OK) return FR_MKFS_ABORTED;
02863     n = (b_data + n - 1) & ~(n - 1);
02864     n_fat += (n - b_data) / N_FATS;
02865     /* b_dir and b_data are no longer used below */
02866 
02867     /* Determine number of cluster and final check of validity of the FAT type */
02868     n_clst = (n_part - n_rsv - n_fat * N_FATS - n_dir) / allocsize;
02869     if (   (fmt == FS_FAT16 && n_clst < 0xFF5)
02870         || (fmt == FS_FAT32 && n_clst < 0xFFF5))
02871         return FR_MKFS_ABORTED;
02872 
02873     /* Create partition table if needed */
02874     if (!partition) {
02875         DWORD n_disk = b_part + n_part;
02876 
02877         mem_set(fs->win, 0, SS(fs));
02878         tbl = fs->win+MBR_Table;
02879         ST_DWORD(tbl, 0x00010180);        /* Partition start in CHS */
02880         if (n_disk < 63UL * 255 * 1024) {    /* Partition end in CHS */
02881             n_disk = n_disk / 63 / 255;
02882             tbl[7] = (BYTE)n_disk;
02883             tbl[6] = (BYTE)((n_disk >> 2) | 63);
02884         } else {
02885             ST_WORD(&tbl[6], 0xFFFF);
02886         }
02887         tbl[5] = 254;
02888         if (fmt != FS_FAT32)            /* System ID */
02889             tbl[4] = (n_part < 0x10000) ? 0x04 : 0x06;
02890         else
02891             tbl[4] = 0x0c;
02892         ST_DWORD(tbl+8, 63);            /* Partition start in LBA */
02893         ST_DWORD(tbl+12, n_part);        /* Partition size in LBA */
02894         ST_WORD(tbl+64, 0xAA55);        /* Signature */
02895         if (disk_write(drv, fs->win, 0, 1) != RES_OK)
02896             return FR_DISK_ERR;
02897         partition = 0xF8;
02898     } else {
02899         partition = 0xF0;
02900     }
02901 
02902     /* Create boot record */
02903     tbl = fs->win;                                /* Clear buffer */
02904     mem_set(tbl, 0, SS(fs));
02905     ST_DWORD(tbl+BS_jmpBoot, 0x90FEEB);            /* Boot code (jmp $, nop) */
02906     ST_WORD(tbl+BPB_BytsPerSec, SS(fs));        /* Sector size */
02907     tbl[BPB_SecPerClus] = (BYTE)allocsize;        /* Sectors per cluster */
02908     ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv);            /* Reserved sectors */
02909     tbl[BPB_NumFATs] = N_FATS;                    /* Number of FATs */
02910     ST_WORD(tbl+BPB_RootEntCnt, SS(fs) / 32 * n_dir); /* Number of rootdir entries */
02911     if (n_part < 0x10000) {                        /* Number of total sectors */
02912         ST_WORD(tbl+BPB_TotSec16, n_part);
02913     } else {
02914         ST_DWORD(tbl+BPB_TotSec32, n_part);
02915     }
02916     tbl[BPB_Media] = partition;                    /* Media descripter */
02917     ST_WORD(tbl+BPB_SecPerTrk, 63);                /* Number of sectors per track */
02918     ST_WORD(tbl+BPB_NumHeads, 255);                /* Number of heads */
02919     ST_DWORD(tbl+BPB_HiddSec, b_part);            /* Hidden sectors */
02920     n = get_fattime();                            /* Use current time as a VSN */
02921     if (fmt != FS_FAT32) {
02922         ST_DWORD(tbl+BS_VolID, n);                /* Volume serial number */
02923         ST_WORD(tbl+BPB_FATSz16, n_fat);        /* Number of secters per FAT */
02924         tbl[BS_DrvNum] = 0x80;                    /* Drive number */
02925         tbl[BS_BootSig] = 0x29;                    /* Extended boot signature */
02926         mem_cpy(tbl+BS_VolLab, "NO NAME    FAT     ", 19);    /* Volume lavel, FAT signature */
02927     } else {
02928         ST_DWORD(tbl+BS_VolID32, n);            /* Volume serial number */
02929         ST_DWORD(tbl+BPB_FATSz32, n_fat);        /* Number of secters per FAT */
02930         ST_DWORD(tbl+BPB_RootClus, 2);            /* Root directory cluster (2) */
02931         ST_WORD(tbl+BPB_FSInfo, 1);                /* FSInfo record offset (bs+1) */
02932         ST_WORD(tbl+BPB_BkBootSec, 6);            /* Backup boot record offset (bs+6) */
02933         tbl[BS_DrvNum32] = 0x80;                /* Drive number */
02934         tbl[BS_BootSig32] = 0x29;                /* Extended boot signature */
02935         mem_cpy(tbl+BS_VolLab32, "NO NAME    FAT32   ", 19);    /* Volume lavel, FAT signature */
02936     }
02937     ST_WORD(tbl+BS_55AA, 0xAA55);                /* Signature */
02938     if (SS(fs) > 512U) {
02939         ST_WORD(tbl+SS(fs)-2, 0xAA55);
02940     }
02941     if (disk_write(drv, tbl, b_part+0, 1) != RES_OK)
02942         return FR_DISK_ERR;
02943     if (fmt == FS_FAT32)
02944         disk_write(drv, tbl, b_part+6, 1);
02945 
02946     /* Initialize FAT area */
02947     for (m = 0; m < N_FATS; m++) {
02948         mem_set(tbl, 0, SS(fs));        /* 1st sector of the FAT  */
02949         if (fmt != FS_FAT32) {
02950             n = (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00;
02951             n |= partition;
02952             ST_DWORD(tbl, n);                /* Reserve cluster #0-1 (FAT12/16) */
02953         } else {
02954             ST_DWORD(tbl+0, 0xFFFFFFF8);    /* Reserve cluster #0-1 (FAT32) */
02955             ST_DWORD(tbl+4, 0xFFFFFFFF);
02956             ST_DWORD(tbl+8, 0x0FFFFFFF);    /* Reserve cluster #2 for root dir */
02957         }
02958         if (disk_write(drv, tbl, b_fat++, 1) != RES_OK)
02959             return FR_DISK_ERR;
02960         mem_set(tbl, 0, SS(fs));        /* Following FAT entries are filled by zero */
02961         for (n = 1; n < n_fat; n++) {
02962             if (disk_write(drv, tbl, b_fat++, 1) != RES_OK)
02963                 return FR_DISK_ERR;
02964         }
02965     }
02966 
02967     /* Initialize Root directory */
02968     m = (BYTE)((fmt == FS_FAT32) ? allocsize : n_dir);
02969     do {
02970         if (disk_write(drv, tbl, b_fat++, 1) != RES_OK)
02971             return FR_DISK_ERR;
02972     } while (--m);
02973 
02974     /* Create FSInfo record if needed */
02975     if (fmt == FS_FAT32) {
02976         ST_WORD(tbl+BS_55AA, 0xAA55);
02977         ST_DWORD(tbl+FSI_LeadSig, 0x41615252);
02978         ST_DWORD(tbl+FSI_StrucSig, 0x61417272);
02979         ST_DWORD(tbl+FSI_Free_Count, n_clst - 1);
02980         ST_DWORD(tbl+FSI_Nxt_Free, 0xFFFFFFFF);
02981         disk_write(drv, tbl, b_part+1, 1);
02982         disk_write(drv, tbl, b_part+7, 1);
02983     }
02984 
02985     return (disk_ioctl(drv, CTRL_SYNC, (void*)NULL) == RES_OK) ? FR_OK : FR_DISK_ERR;
02986 }
02987 
02988 #endif /* _USE_MKFS && !_FS_READONLY */
02989 
02990 
02991 
02992 
02993 #if _USE_STRFUNC
02994 /*-----------------------------------------------------------------------*/
02995 /* Get a string from the file                                            */
02996 /*-----------------------------------------------------------------------*/
02997 char* f_gets (
02998     char* buff,    /* Pointer to the string buffer to read */
02999     int len,    /* Size of string buffer */
03000     FIL* fil    /* Pointer to the file object */
03001 )
03002 {
03003     int i = 0;
03004     char *p = buff;
03005     UINT rc;
03006 
03007 
03008     while (i < len - 1) {            /* Read bytes until buffer gets filled */
03009         f_read(fil, p, 1, &rc);
03010         if (rc != 1) break;            /* Break when no data to read */
03011 #if _USE_STRFUNC >= 2
03012         if (*p == '\r') continue;    /* Strip '\r' */
03013 #endif
03014         i++;
03015         if (*p++ == '\n') break;    /* Break when reached end of line */
03016     }
03017     *p = 0;
03018     return i ? buff : NULL;            /* When no data read (eof or error), return with error. */
03019 }
03020 
03021 
03022 
03023 #if !_FS_READONLY
03024 #include <stdarg.h>
03025 /*-----------------------------------------------------------------------*/
03026 /* Put a character to the file                                           */
03027 /*-----------------------------------------------------------------------*/
03028 int f_putc (
03029     int chr,    /* A character to be output */
03030     FIL* fil    /* Ponter to the file object */
03031 )
03032 {
03033     UINT bw;
03034     char c;
03035 
03036 
03037 #if _USE_STRFUNC >= 2
03038     if (chr == '\n') f_putc ('\r', fil);    /* LF -> CRLF conversion */
03039 #endif
03040     if (!fil) {    /* Special value may be used to switch the destination to any other device */
03041     /*    put_console(chr);    */
03042         return chr;
03043     }
03044     c = (char)chr;
03045     f_write(fil, &c, 1, &bw);    /* Write a byte to the file */
03046     return bw ? chr : EOF;        /* Return the result */
03047 }
03048 
03049 
03050 
03051 
03052 /*-----------------------------------------------------------------------*/
03053 /* Put a string to the file                                              */
03054 /*-----------------------------------------------------------------------*/
03055 int f_puts (
03056     const char* str,    /* Pointer to the string to be output */
03057     FIL* fil            /* Pointer to the file object */
03058 )
03059 {
03060     int n;
03061 
03062 
03063     for (n = 0; *str; str++, n++) {
03064         if (f_putc(*str, fil) == EOF) return EOF;
03065     }
03066     return n;
03067 }
03068 
03069 
03070 
03071 
03072 /*-----------------------------------------------------------------------*/
03073 /* Put a formatted string to the file                                    */
03074 /*-----------------------------------------------------------------------*/
03075 int f_printf (
03076     FIL* fil,            /* Pointer to the file object */
03077     const char* str,    /* Pointer to the format string */
03078     ...                    /* Optional arguments... */
03079 )
03080 {
03081     va_list arp;
03082     UCHAR c, f, r;
03083     ULONG val;
03084     char s[16];
03085     int i, w, res, cc;
03086 
03087 
03088     va_start(arp, str);
03089 
03090     for (cc = res = 0; cc != EOF; res += cc) {
03091         c = *str++;
03092         if (c == 0) break;            /* End of string */
03093         if (c != '%') {                /* Non escape cahracter */
03094             cc = f_putc(c, fil);
03095             if (cc != EOF) cc = 1;
03096             continue;
03097         }
03098         w = f = 0;
03099         c = *str++;
03100         if (c == '0') {                /* Flag: '0' padding */
03101             f = 1; c = *str++;
03102         }
03103         while (c >= '0' && c <= '9') {    /* Precision */
03104             w = w * 10 + (c - '0');
03105             c = *str++;
03106         }
03107         if (c == 'l') {                /* Prefix: Size is long int */
03108             f |= 2; c = *str++;
03109         }
03110         if (c == 's') {                /* Type is string */
03111             cc = f_puts(va_arg(arp, char*), fil);
03112             continue;
03113         }
03114         if (c == 'c') {                /* Type is character */
03115             cc = f_putc(va_arg(arp, int), fil);
03116             if (cc != EOF) cc = 1;
03117             continue;
03118         }
03119         r = 0;
03120         if (c == 'd') r = 10;        /* Type is signed decimal */
03121         if (c == 'u') r = 10;        /* Type is unsigned decimal */
03122         if (c == 'X') r = 16;        /* Type is unsigned hexdecimal */
03123         if (r == 0) break;            /* Unknown type */
03124         if (f & 2) {                /* Get the value */
03125             val = (ULONG)va_arg(arp, long);
03126         } else {
03127             val = (c == 'd') ? (ULONG)(long)va_arg(arp, int) : (ULONG)va_arg(arp, unsigned int);
03128         }
03129         /* Put numeral string */
03130         if (c == 'd') {
03131             if (val & 0x80000000) {
03132                 val = 0 - val;
03133                 f |= 4;
03134             }
03135         }
03136         i = sizeof(s) - 1; s[i] = 0;
03137         do {
03138             c = (UCHAR)(val % r + '0');
03139             if (c > '9') c += 7;
03140             s[--i] = c;
03141             val /= r;
03142         } while (i && val);
03143         if (i && (f & 4)) s[--i] = '-';
03144         w = sizeof(s) - 1 - w;
03145         while (i && i > w) s[--i] = (f & 1) ? '0' : ' ';
03146         cc = f_puts(&s[i], fil);
03147     }
03148 
03149     va_end(arp);
03150     return (cc == EOF) ? cc : res;
03151 }
03152 
03153 #endif /* !_FS_READONLY */
03154 #endif /* _USE_STRFUNC */