/*--------------------------------------------------------------------------// FatFs - Tiny FAT file system module R0.04b (C)ChaN, 2007/---------------------------------------------------------------------------// The FatFs module is an experimenal project to implement FAT file system to/ cheap microcontrollers. This is a free software and is opened for education,/ research and development under license policy of following trems.// Copyright (C) 2007, ChaN, all right reserved.// * The FatFs module is a free software and there is no warranty./ * You can use, modify and/or redistribute it for personal, non-profit or/ profit use without any restriction under your responsibility./ * Redistributions of source code must retain the above copyright notice.//---------------------------------------------------------------------------// Feb 26, 2006 R0.00 Prototype./ Apr 29, 2006 R0.01 First stable version./ Jun 01, 2006 R0.02 Added FAT12 support./ Removed unbuffered mode./ Fixed a problem on small (<32M) patition./ Jun 10, 2006 R0.02a Added a configuration option (_FS_MINIMUM)./ Sep 22, 2006 R0.03 Added f_rename()./ Changed option _FS_MINIMUM to _FS_MINIMIZE./ Dec 09, 2006 R0.03a Improved cluster scan algolithm to write files fast./ Feb 04, 2007 R0.04 Added FAT32 supprt./ Changed some interfaces incidental to FatFs./ Changed f_mountdrv() to f_mount()./ Apr 01, 2007 R0.04a Added a capability of extending file size to f_lseek()./ Added minimization level 3./ Fixed a problem in FAT32 support./ xxx xx, 2007 R0.04b Added a configuration option _USE_NTFLAG./ Added FSInfo support./ Fixed some problems corresponds to FAT32 support./ Fixed DBCS name can result FR_INVALID_NAME./ Fixed short seek (<= csize) collapses the file object./---------------------------------------------------------------------------*/#include <string.h>#include "tff.h" /* Tiny-FatFs declarations */#include "diskio.h" /* Include file for user provided disk functions */staticFATFS *FatFs; /* Pointer to the file system objects (logical drive) */staticWORD fsid; /* File system mount ID *//*-------------------------------------------------------------------------Module Private Functions-------------------------------------------------------------------------*//*-----------------------------------------------------------------------*//* Change window offset *//*-----------------------------------------------------------------------*/staticBOOL move_window ( /* TRUE: successful, FALSE: failed */DWORD sector /* Sector number to make apperance in the FatFs->win */) /* Move to zero only writes back dirty window */{DWORD wsect;FATFS *fs = FatFs;wsect = fs->winsect;if (wsect != sector) { /* Changed current window */#if !_FS_READONLYBYTE n;if (fs->winflag) { /* Write back dirty window if needed */if (disk_write(0, fs->win, wsect, 1) != RES_OK)return FALSE;fs->winflag = 0;if (wsect < (fs->fatbase + fs->sects_fat)) { /* In FAT area */for (n = fs->n_fats; n >= 2; n--) { /* Refrect the change to all FAT copies */wsect += fs->sects_fat;disk_write(0, fs->win, wsect, 1);}}}#endifif (sector) {if (disk_read(0, fs->win, sector, 1) != RES_OK)return FALSE;fs->winsect = sector;}}return TRUE;}/*-----------------------------------------------------------------------*//* Clean-up cached data *//*-----------------------------------------------------------------------*/#if !_FS_READONLYstaticFRESULT sync (void) /* FR_OK: successful, FR_RW_ERROR: failed */{FATFS *fs = FatFs;fs->winflag = 1;if (!move_window(0)) return FR_RW_ERROR;#if _USE_FSINFOif (fs->fs_type == FS_FAT32 && fs->fsi_flag) { /* Update FSInfo sector if needed */fs->winsect = 0;memset(fs->win, 0, 512);ST_WORD(&fs->win[BS_55AA], 0xAA55);ST_DWORD(&fs->win[FSI_LeadSig], 0x41615252);ST_DWORD(&fs->win[FSI_StrucSig], 0x61417272);ST_DWORD(&fs->win[FSI_Free_Count], fs->free_clust);ST_DWORD(&fs->win[FSI_Nxt_Free], fs->last_clust);disk_write(0, fs->win, fs->fsi_sector, 1);fs->fsi_flag = 0;}#endifif (disk_ioctl(0, CTRL_SYNC, NULL) != RES_OK) return FR_RW_ERROR;return FR_OK;}#endif/*-----------------------------------------------------------------------*//* Get a cluster status *//*-----------------------------------------------------------------------*/staticCLUST get_cluster ( /* 0,>=2: successful, 1: failed */CLUST clust /* Cluster# to get the link information */){WORD wc, bc;DWORD fatsect;FATFS *fs = FatFs;if (clust >= 2 && clust < fs->max_clust) { /* Valid cluster# */fatsect = fs->fatbase;switch (fs->fs_type) {case FS_FAT12 :bc = (WORD)clust * 3 / 2;if (!move_window(fatsect + bc / 512)) break;wc = fs->win[bc % 512]; bc++;if (!move_window(fatsect + bc / 512)) break;wc |= (WORD)fs->win[bc % 512] << 8;return (clust & 1) ? (wc >> 4) : (wc & 0xFFF);case FS_FAT16 :if (!move_window(fatsect + clust / 256)) break;return LD_WORD(&fs->win[((WORD)clust * 2) % 512]);#if _FAT32case FS_FAT32 :if (!move_window(fatsect + clust / 128)) break;return LD_DWORD(&fs->win[((WORD)clust * 4) % 512]) & 0x0FFFFFFF;#endif}}return 1; /* There is no cluster information, or an error occured */}/*-----------------------------------------------------------------------*//* Change a cluster status *//*-----------------------------------------------------------------------*/#if !_FS_READONLYstaticBOOL put_cluster ( /* TRUE: successful, FALSE: failed */CLUST clust, /* Cluster# to change */CLUST val /* New value to mark the cluster */){WORD bc;BYTE *p;DWORD fatsect;FATFS *fs = FatFs;fatsect = fs->fatbase;switch (fs->fs_type) {case FS_FAT12 :bc = (WORD)clust * 3 / 2;if (!move_window(fatsect + bc / 512)) return FALSE;p = &fs->win[bc % 512];*p = (clust & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val;bc++;fs->winflag = 1;if (!move_window(fatsect + bc / 512)) return FALSE;p = &fs->win[bc % 512];*p = (clust & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F));break;case FS_FAT16 :if (!move_window(fatsect + clust / 256)) return FALSE;ST_WORD(&fs->win[((WORD)clust * 2) % 512], (WORD)val);break;#if _FAT32case FS_FAT32 :if (!move_window(fatsect + clust / 128)) return FALSE;ST_DWORD(&fs->win[((WORD)clust * 4) % 512], val);break;#endifdefault :return FALSE;}fs->winflag = 1;return TRUE;}#endif /* !_FS_READONLY *//*-----------------------------------------------------------------------*//* Remove a cluster chain *//*-----------------------------------------------------------------------*/#if !_FS_READONLYstaticBOOL remove_chain ( /* TRUE: successful, FALSE: failed */CLUST clust /* Cluster# to remove chain from */){CLUST nxt;FATFS *fs = FatFs;while (clust >= 2 && clust < fs->max_clust) {nxt = get_cluster(clust);if (nxt == 1) return FALSE;if (!put_cluster(clust, 0)) return FALSE;if (fs->free_clust != (CLUST)0xFFFFFFFF) {fs->free_clust++;#if _USE_FSINFOfs->fsi_flag = 1;#endif}clust = nxt;}return TRUE;}#endif/*-----------------------------------------------------------------------*//* Stretch or create a cluster chain *//*-----------------------------------------------------------------------*/#if !_FS_READONLYstaticCLUST create_chain ( /* 0: no free cluster, 1: error, >=2: new cluster number */CLUST clust /* Cluster# to stretch, 0 means create new */){CLUST cstat, ncl, scl, mcl;FATFS *fs = FatFs;mcl = fs->max_clust;if (clust == 0) { /* Create new chain */scl = fs->last_clust; /* Get last allocated cluster */if (scl < 2 || scl >= mcl) scl = 1;}else { /* Stretch existing chain */cstat = get_cluster(clust); /* Check the cluster status */if (cstat < 2) return 1; /* It is an invalid cluster */if (cstat < mcl) return cstat; /* It is already followed by next cluster */scl = clust;}ncl = scl; /* Start cluster */for (;;) {ncl++; /* Next cluster */if (ncl >= mcl) { /* Wrap around */ncl = 2;if (ncl > scl) return 0; /* No free custer */}cstat = get_cluster(ncl); /* Get the cluster status */if (cstat == 0) break; /* Found a free cluster */if (cstat == 1) return 1; /* Any error occured */if (ncl == scl) return 0; /* No free custer */}if (!put_cluster(ncl, (CLUST)0x0FFFFFFF)) return 1; /* Mark the new cluster "in use" */if (clust && !put_cluster(clust, ncl)) return 1; /* Link it to previous one if needed */fs->last_clust = ncl; /* Update fsinfo */if (fs->free_clust != (CLUST)0xFFFFFFFF) {fs->free_clust--;#if _USE_FSINFOfs->fsi_flag = 1;#endif}return ncl; /* Return new cluster number */}#endif /* !_FS_READONLY *//*-----------------------------------------------------------------------*//* Get sector# from cluster# *//*-----------------------------------------------------------------------*/staticDWORD clust2sect ( /* !=0: sector number, 0: failed - invalid cluster# */CLUST clust /* Cluster# to be converted */){FATFS *fs = FatFs;clust -= 2;if (clust >= (fs->max_clust - 2)) return 0; /* Invalid cluster# */return (DWORD)clust * fs->sects_clust + fs->database;}/*-----------------------------------------------------------------------*//* Move directory pointer to next *//*-----------------------------------------------------------------------*/staticBOOL next_dir_entry ( /* TRUE: successful, FALSE: could not move next */DIR *dirobj /* Pointer to directory object */){CLUST clust;WORD idx;FATFS *fs = FatFs;idx = dirobj->index + 1;if ((idx & 15) == 0) { /* Table sector changed? */dirobj->sect++; /* Next sector */if (!dirobj->clust) { /* In static table */if (idx >= fs->n_rootdir) return FALSE; /* Reached to end of table */} else { /* In dynamic table */if (((idx / 16) & (fs->sects_clust - 1)) == 0) { /* Cluster changed? */clust = get_cluster(dirobj->clust); /* Get next cluster */if (clust < 2 || clust >= fs->max_clust) /* Reached to end of table */return FALSE;dirobj->clust = clust; /* Initialize for new cluster */dirobj->sect = clust2sect(clust);}}}dirobj->index = idx; /* Lower 4 bit of dirobj->index indicates offset in dirobj->sect */return TRUE;}/*-----------------------------------------------------------------------*//* Get file status from directory entry *//*-----------------------------------------------------------------------*/#if _FS_MINIMIZE <= 1staticvoid get_fileinfo ( /* No return code */FILINFO *finfo, /* Ptr to store the File Information */const BYTE *dir /* Ptr to the directory entry */){BYTE n, c, a;char *p;p = &finfo->fname[0];a = _USE_NTFLAG ? dir[DIR_NTres] : 0; /* NT flag */for (n = 0; n < 8; n++) { /* Convert file name (body) */c = dir[n];if (c == ' ') break;if (c == 0x05) c = 0xE5;if (a & 0x08 && c >= 'A' && c <= 'Z') c += 0x20;*p++ = c;}if (dir[8] != ' ') { /* Convert file name (extension) */*p++ = '.';for (n = 8; n < 11; n++) {c = dir[n];if (c == ' ') break;if (a & 0x10 && c >= 'A' && c <= 'Z') c += 0x20;*p++ = c;}}*p = '\0';finfo->fattrib = dir[DIR_Attr]; /* Attribute */finfo->fsize = LD_DWORD(&dir[DIR_FileSize]); /* Size */finfo->fdate = LD_WORD(&dir[DIR_WrtDate]); /* Date */finfo->ftime = LD_WORD(&dir[DIR_WrtTime]); /* Time */}#endif /* _FS_MINIMIZE <= 1 *//*-----------------------------------------------------------------------*//* Pick a paragraph and create the name in format of directory entry *//*-----------------------------------------------------------------------*/staticchar make_dirfile ( /* 1: error - detected an invalid format, '\0'or'/': next character */const char **path, /* Pointer to the file path pointer */char *dirname /* Pointer to directory name buffer {Name(8), Ext(3), NT flag(1)} */){BYTE n, t, c, a, b;memset(dirname, ' ', 8+3); /* Fill buffer with spaces */a = 0; b = 0x18; /* NT flag */n = 0; t = 8;for (;;) {c = *(*path)++;if (c == '\0' || c == '/') { /* Reached to end of str or directory separator */if (n == 0) break;dirname[11] = _USE_NTFLAG ? (a & b) : 0;return c;}if (c <= ' ' || c == 0x7F) break; /* Reject invisible chars */if (c == '.') {if (!(a & 1) && n >= 1 && n <= 8) { /* Enter extension part */n = 8; t = 11; continue;}break;}if (_USE_SJIS &&((c >= 0x81 && c <= 0x9F) || /* Accept S-JIS code */(c >= 0xE0 && c <= 0xFC))) {if (n == 0 && c == 0xE5) /* Change heading \xE5 to \x05 */c = 0x05;a ^= 1; goto md_l2;}if (c == '"') break; /* Reject " */if (c <= ')') goto md_l1; /* Accept ! # $ % & ' ( ) */if (c <= ',') break; /* Reject * + , */if (c <= '9') goto md_l1; /* Accept - 0-9 */if (c <= '?') break; /* Reject : ; < = > ? */if (!(a & 1)) { /* These checks are not applied to S-JIS 2nd byte */if (c == '|') break; /* Reject | */if (c >= '[' && c <= ']') break;/* Reject [ \ ] */if (_USE_NTFLAG && c >= 'A' && c <= 'Z')(t == 8) ? (b &= ~0x08) : (b &= ~0x10);if (c >= 'a' && c <= 'z') { /* Convert to upper case */c -= 0x20;if (_USE_NTFLAG) (t == 8) ? (a |= 0x08) : (a |= 0x10);}}md_l1:a &= ~1;md_l2:if (n >= t) break;dirname[n++] = c;}return 1;}/*-----------------------------------------------------------------------*//* Trace a file path *//*-----------------------------------------------------------------------*/staticFRESULT trace_path ( /* FR_OK(0): successful, !=0: error code */DIR *dirobj, /* Pointer to directory object to return last directory */char *fn, /* Pointer to last segment name to return */const char *path, /* Full-path string to trace a file or directory */BYTE **dir /* Directory pointer in Win[] to retutn */){CLUST clust;char ds;BYTE *dptr = NULL;FATFS *fs = FatFs;/* Initialize directory object */clust = fs->dirbase;#if _FAT32if (fs->fs_type == FS_FAT32) {dirobj->clust = dirobj->sclust = clust;dirobj->sect = clust2sect(clust);} else#endif{dirobj->clust = dirobj->sclust = 0;dirobj->sect = clust;}dirobj->index = 0;dirobj->fs = fs;if (*path == '\0') { /* Null path means the root directory */*dir = NULL; return FR_OK;}for (;;) {ds = make_dirfile(&path, fn); /* Get a paragraph into fn[] */if (ds == 1) return FR_INVALID_NAME;for (;;) {if (!move_window(dirobj->sect)) return FR_RW_ERROR;dptr = &fs->win[(dirobj->index & 15) * 32]; /* Pointer to the directory entry */if (dptr[DIR_Name] == 0) /* Has it reached to end of dir? */return !ds ? FR_NO_FILE : FR_NO_PATH;if (dptr[DIR_Name] != 0xE5 /* Matched? */&& !(dptr[DIR_Attr] & AM_VOL)&& !memcmp(&dptr[DIR_Name], fn, 8+3) ) break;if (!next_dir_entry(dirobj)) /* Next directory pointer */return !ds ? FR_NO_FILE : FR_NO_PATH;}if (!ds) { *dir = dptr; return FR_OK; } /* Matched with end of path */if (!(dptr[DIR_Attr] & AM_DIR)) return FR_NO_PATH; /* Cannot trace because it is a file */clust = /* Get cluster# of the directory */#if _FAT32((DWORD)LD_WORD(&dptr[DIR_FstClusHI]) << 16) |#endifLD_WORD(&dptr[DIR_FstClusLO]);dirobj->clust = dirobj->sclust = clust; /* Restart scannig with the new directory */dirobj->sect = clust2sect(clust);dirobj->index = 2;}}/*-----------------------------------------------------------------------*//* Reserve a directory entry *//*-----------------------------------------------------------------------*/#if !_FS_READONLYstaticFRESULT reserve_direntry ( /* FR_OK: successful, FR_DENIED: no free entry, FR_RW_ERROR: a disk error occured */DIR *dirobj, /* Target directory to create new entry */BYTE **dir /* Pointer to pointer to created entry to retutn */){CLUST clust;DWORD sector;BYTE c, n, *dptr;FATFS *fs = FatFs;/* Re-initialize directory object */clust = dirobj->sclust;if (clust) { /* Dyanmic directory table */dirobj->clust = clust;dirobj->sect = clust2sect(clust);} else { /* Static directory table */dirobj->sect = fs->dirbase;}dirobj->index = 0;do {if (!move_window(dirobj->sect)) return FR_RW_ERROR;dptr = &fs->win[(dirobj->index & 15) * 32]; /* Pointer to the directory entry */c = dptr[DIR_Name];if (c == 0 || c == 0xE5) { /* Found an empty entry! */*dir = dptr; return FR_OK;}} while (next_dir_entry(dirobj)); /* Next directory pointer *//* Reached to end of the directory table *//* Abort when static table or could not stretch dynamic table */if (!clust || !(clust = create_chain(dirobj->clust))) return FR_DENIED;if (clust == 1 || !move_window(0)) return FR_RW_ERROR;fs->winsect = sector = clust2sect(clust); /* Cleanup the expanded table */memset(fs->win, 0, 512);for (n = fs->sects_clust; n; n--) {if (disk_write(0, fs->win, sector, 1) != RES_OK)return FR_RW_ERROR;sector++;}fs->winflag = 1;*dir = fs->win;return FR_OK;}#endif /* !_FS_READONLY *//*-----------------------------------------------------------------------*//* Load boot record and check if it is a FAT boot record *//*-----------------------------------------------------------------------*/staticBYTE check_fs ( /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record or error */DWORD sect /* Sector# to check if it is a FAT boot record or not */){FATFS *fs = FatFs;if (disk_read(0, fs->win, sect, 1) != RES_OK) /* Load boot record */return 2;if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55) /* Check record signature */return 2;if (!memcmp(&fs->win[BS_FilSysType], "FAT", 3)) /* Check FAT signature */return 0;#if _FAT32if (!memcmp(&fs->win[BS_FilSysType32], "FAT32", 5) && !(fs->win[BPB_ExtFlags] & 0x80))return 0;#endifreturn 1;}/*-----------------------------------------------------------------------*//* Make sure that the file system is valid *//*-----------------------------------------------------------------------*/staticFRESULT auto_mount ( /* FR_OK(0): successful, !=0: any error occured */const char **path, /* Pointer to pointer to the path name (drive number) */BYTE chk_wp /* !=0: Check media write protection for wrinting fuctions */){BYTE fmt;DSTATUS stat;DWORD bootsect, fatsize, totalsect, maxclust;const char *p = *path;FATFS *fs = FatFs;while (*p == ' ') p++; /* Strip leading spaces */if (*p == '/') p++; /* Strip heading slash */*path = p; /* Return pointer to the path name *//* Is the file system object registered? */if (!fs) return FR_NOT_ENABLED;/* Chekck if the logical drive has been mounted or not */if (fs->fs_type) {stat = disk_status(0);if (!(stat & STA_NOINIT)) { /* If the physical drive is kept initialized */#if !_FS_READONLYif (chk_wp && (stat & STA_PROTECT)) /* Check write protection if needed */return FR_WRITE_PROTECTED;#endifreturn FR_OK; /* The file system object is valid */}}/* The logical drive has not been mounted, following code attempts to mount the logical drive */memset(fs, 0, sizeof(FATFS)); /* Clean-up the file system object */stat = disk_initialize(0); /* Initialize low level disk I/O layer */if (stat & STA_NOINIT) /* Check if the drive is ready */return FR_NOT_READY;#if !_FS_READONLYif (chk_wp && (stat & STA_PROTECT)) /* Check write protection if needed */return FR_WRITE_PROTECTED;#endif/* Search FAT partition on the drive */fmt = check_fs(bootsect = 0); /* Check sector 0 as an SFD format */if (fmt == 1) { /* Not a FAT boot record, it may be patitioned *//* Check a partition listed in top of the partition table */if (fs->win[MBR_Table+4]) { /* Is the 1st partition existing? */bootsect = LD_DWORD(&fs->win[MBR_Table+8]); /* Partition offset in LBA */fmt = check_fs(bootsect); /* Check the partition */}}if (fmt || LD_WORD(&fs->win[BPB_BytsPerSec]) != 512) /* No valid FAT patition is found */return FR_NO_FILESYSTEM;/* Initialize the file system object */fatsize = LD_WORD(&fs->win[BPB_FATSz16]); /* Number of sectors per FAT */if (!fatsize) fatsize = LD_DWORD(&fs->win[BPB_FATSz32]);fs->sects_fat = (CLUST)fatsize;fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FAT copies */fatsize *= fs->n_fats; /* (Number of sectors in FAT area) */fs->fatbase = bootsect + LD_WORD(&fs->win[BPB_RsvdSecCnt]); /* FAT start sector (lba) */fs->sects_clust = fs->win[BPB_SecPerClus]; /* Number of sectors per cluster */fs->n_rootdir = LD_WORD(&fs->win[BPB_RootEntCnt]); /* Nmuber of root directory entries */totalsect = LD_WORD(&fs->win[BPB_TotSec16]); /* Number of sectors on the file system */if (!totalsect) totalsect = LD_DWORD(&fs->win[BPB_TotSec32]);fs->max_clust = maxclust = (totalsect /* Last cluster# + 1 */- LD_WORD(&fs->win[BPB_RsvdSecCnt]) - fatsize - fs->n_rootdir / 16) / fs->sects_clust + 2;fmt = FS_FAT12; /* Determine the FAT sub type */if (maxclust > 0xFF7) fmt = FS_FAT16;if (maxclust > 0xFFF7)#if !_FAT32return FR_NO_FILESYSTEM;#elsefmt = FS_FAT32;if (fmt == FS_FAT32)fs->dirbase = LD_DWORD(&fs->win[BPB_RootClus]); /* Root directory start cluster */else#endiffs->dirbase = fs->fatbase + fatsize; /* Root directory start sector (lba) */fs->database = fs->fatbase + fatsize + fs->n_rootdir / 16; /* Data start sector (lba) */fs->fs_type = fmt; /* FAT sub-type */#if !_FS_READONLYfs->free_clust = (CLUST)0xFFFFFFFF;#if _USE_FSINFO/* Load fsinfo sector if needed */if (fmt == FS_FAT32) {fs->fsi_sector = bootsect + LD_WORD(&fs->win[BPB_FSInfo]);if (disk_read(0, fs->win, fs->fsi_sector, 1) == RES_OK &&LD_WORD(&fs->win[BS_55AA]) == 0xAA55 &&LD_DWORD(&fs->win[FSI_LeadSig]) == 0x41615252 &&LD_DWORD(&fs->win[FSI_StrucSig]) == 0x61417272) {fs->last_clust = LD_DWORD(&fs->win[FSI_Nxt_Free]);fs->free_clust = LD_DWORD(&fs->win[FSI_Free_Count]);}}#endif#endiffs->id = ++fsid; /* File system mount ID */return FR_OK;}/*-----------------------------------------------------------------------*//* Check if the file/dir object is valid or not *//*-----------------------------------------------------------------------*/staticFRESULT validate ( /* FR_OK(0): The id is valid, !=0: Not valid */const FATFS *fs, /* Pointer to the file system object */WORD id /* id member of the target object to be checked */){if (!fs || fs->id != id)return FR_INVALID_OBJECT;if (disk_status(0) & STA_NOINIT)return FR_NOT_READY;return FR_OK;}/*--------------------------------------------------------------------------Public Functions--------------------------------------------------------------------------*//*-----------------------------------------------------------------------*//* Mount/Unmount a Locical Drive *//*-----------------------------------------------------------------------*/FRESULT f_mount (BYTE drv, /* Logical drive number to be mounted/unmounted */FATFS *fs /* Pointer to new file system object (NULL for unmount)*/){FATFS *fsobj;if (drv) return FR_INVALID_DRIVE;fsobj = FatFs;FatFs = fs;if (fsobj) memset(fsobj, 0, sizeof(FATFS));if (fs) memset(fs, 0, sizeof(FATFS));return FR_OK;}/*-----------------------------------------------------------------------*//* Open or Create a File *//*-----------------------------------------------------------------------*/FRESULT f_open (FIL *fp, /* Pointer to the blank file object */const char *path, /* Pointer to the file name */BYTE mode /* Access mode and file open mode flags */){FRESULT res;BYTE *dir;DIR dirobj;char fn[8+3+1];FATFS *fs = FatFs;fp->fs = NULL;#if !_FS_READONLYmode &= (FA_READ|FA_WRITE|FA_CREATE_ALWAYS|FA_OPEN_ALWAYS|FA_CREATE_NEW);res = auto_mount(&path, (BYTE)(mode & (FA_WRITE|FA_CREATE_ALWAYS|FA_OPEN_ALWAYS|FA_CREATE_NEW)));#elsemode &= FA_READ;res = auto_mount(&path, 0);#endifif (res != FR_OK) return res;/* Trace the file path */res = trace_path(&dirobj, fn, path, &dir); /* Trace the file path */#if !_FS_READONLY/* Create or Open a File */if (mode & (FA_CREATE_ALWAYS|FA_OPEN_ALWAYS|FA_CREATE_NEW)) {CLUST rs;DWORD dw;if (res != FR_OK) { /* No file, create new */if (res != FR_NO_FILE) return res;res = reserve_direntry(&dirobj, &dir);if (res != FR_OK) return res;memset(dir, 0, 32); /* Initialize the new entry */memcpy(&dir[DIR_Name], fn, 8+3);dir[DIR_NTres] = fn[11];mode |= FA_CREATE_ALWAYS;} else { /* Any object is already existing */if (mode & FA_CREATE_NEW) /* Cannot create new */return FR_EXIST;if (dir == NULL || (dir[DIR_Attr] & (AM_RDO|AM_DIR))) /* Cannot overwrite (R/O or DIR) */return FR_DENIED;if (mode & FA_CREATE_ALWAYS) { /* Resize it to zero */#if _FAT32rs = ((DWORD)LD_WORD(&dir[DIR_FstClusHI]) << 16) | LD_WORD(&dir[DIR_FstClusLO]);ST_WORD(&dir[DIR_FstClusHI], 0);#elsers = LD_WORD(&dir[DIR_FstClusLO]);#endifST_WORD(&dir[DIR_FstClusLO], 0); /* cluster = 0 */ST_DWORD(&dir[DIR_FileSize], 0); /* size = 0 */fs->winflag = 1;dw = fs->winsect; /* Remove the cluster chain */if (!remove_chain(rs) || !move_window(dw))return FR_RW_ERROR;fs->last_clust = rs - 1; /* Reuse the cluster hole */}}if (mode & FA_CREATE_ALWAYS) {dir[DIR_Attr] = AM_ARC; /* New attribute */dw = get_fattime();ST_DWORD(&dir[DIR_WrtTime], dw); /* Updated time */ST_DWORD(&dir[DIR_CrtTime], dw); /* Created time */fs->winflag = 1;}}/* Open a File */else {#endif /* !_FS_READONLY */if (res != FR_OK) return res; /* Trace failed */if (dir == NULL || (dir[DIR_Attr] & AM_DIR)) /* It is a directory */return FR_NO_FILE;#if !_FS_READONLYif ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */return FR_DENIED;}fp->dir_sect = fs->winsect; /* Pointer to the directory entry */fp->dir_ptr = dir;#endiffp->flag = mode; /* File access mode */fp->org_clust = /* File start cluster */#if _FAT32((DWORD)LD_WORD(&dir[DIR_FstClusHI]) << 16) |#endifLD_WORD(&dir[DIR_FstClusLO]);fp->fsize = LD_DWORD(&dir[DIR_FileSize]); /* File size */fp->fptr = 0; /* File ptr */fp->sect_clust = 1; /* Sector counter */fp->fs = fs; fp->id = fs->id; /* Owner file system object of the file */return FR_OK;}/*-----------------------------------------------------------------------*//* Read File *//*-----------------------------------------------------------------------*/FRESULT f_read (FIL *fp, /* Pointer to the file object */void *buff, /* Pointer to data buffer */WORD btr, /* Number of bytes to read */WORD *br /* Pointer to number of bytes read */){DWORD sect, remain;WORD rcnt;CLUST clust;BYTE cc, *rbuff = buff;FRESULT res;FATFS *fs = fp->fs;*br = 0;res = validate(fs, fp->id); /* Check validity of the object */if (res) return res;if (fp->flag & FA__ERROR) return FR_RW_ERROR; /* Check error flag */if (!(fp->flag & FA_READ)) return FR_DENIED; /* Check access mode */remain = fp->fsize - fp->fptr;if (btr > remain) btr = (WORD)remain; /* Truncate read count by number of bytes left */for ( ; btr; /* Repeat until all data transferred */rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) {if ((fp->fptr % 512) == 0) { /* On the sector boundary */if (--fp->sect_clust) { /* Decrement left sector counter */sect = fp->curr_sect + 1; /* Get current sector */} else { /* On the cluster boundary, get next cluster */clust = (fp->fptr == 0) ?fp->org_clust : get_cluster(fp->curr_clust);if (clust < 2 || clust >= fs->max_clust)goto fr_error;fp->curr_clust = clust; /* Current cluster */sect = clust2sect(clust); /* Get current sector */fp->sect_clust = fs->sects_clust; /* Re-initialize the left sector counter */}fp->curr_sect = sect; /* Update current sector */cc = btr / 512; /* When left bytes >= 512, */if (cc) { /* Read maximum contiguous sectors directly */if (cc > fp->sect_clust) cc = fp->sect_clust;if (disk_read(0, rbuff, sect, cc) != RES_OK)goto fr_error;fp->sect_clust -= cc - 1;fp->curr_sect += cc - 1;rcnt = cc * 512; continue;}}if (!move_window(fp->curr_sect)) goto fr_error; /* Move sector window */rcnt = 512 - (WORD)(fp->fptr % 512); /* Copy fractional bytes from sector window */if (rcnt > btr) rcnt = btr;memcpy(rbuff, &fs->win[(WORD)fp->fptr % 512], rcnt);}return FR_OK;fr_error: /* Abort this function due to an unrecoverable error */fp->flag |= FA__ERROR;return FR_RW_ERROR;}#if !_FS_READONLY/*-----------------------------------------------------------------------*//* Write File *//*-----------------------------------------------------------------------*/FRESULT f_write (FIL *fp, /* Pointer to the file object */const void *buff, /* Pointer to the data to be written */WORD btw, /* Number of bytes to write */WORD *bw /* Pointer to number of bytes written */){DWORD sect;WORD wcnt;CLUST clust;BYTE cc;FRESULT res;const BYTE *wbuff = buff;FATFS *fs = fp->fs;*bw = 0;res = validate(fs, fp->id); /* Check validity of the object */if (res) return res;if (fp->flag & FA__ERROR) return FR_RW_ERROR; /* Check error flag */if (!(fp->flag & FA_WRITE)) return FR_DENIED; /* Check access mode */if (fp->fsize + btw < fp->fsize) return FR_OK; /* File size cannot reach 4GB */for ( ; btw; /* Repeat until all data transferred */wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) {if ((fp->fptr % 512) == 0) { /* On the sector boundary */if (--(fp->sect_clust)) { /* Decrement left sector counter */sect = fp->curr_sect + 1; /* Get current sector */} else { /* On the cluster boundary, get next cluster */if (fp->fptr == 0) { /* Is top of the file */clust = fp->org_clust;if (clust == 0) /* No cluster is created yet */fp->org_clust = clust = create_chain(0); /* Create a new cluster chain */} else { /* Middle or end of file */clust = create_chain(fp->curr_clust); /* Trace or streach cluster chain */}if (clust == 0) break; /* Disk full */if (clust == 1 || clust >= fs->max_clust) goto fw_error;fp->curr_clust = clust; /* Current cluster */sect = clust2sect(clust); /* Get current sector */fp->sect_clust = fs->sects_clust; /* Re-initialize the left sector counter */}fp->curr_sect = sect; /* Update current sector */cc = btw / 512; /* When left bytes >= 512, */if (cc) { /* Write maximum contiguous sectors directly */if (cc > fp->sect_clust) cc = fp->sect_clust;if (disk_write(0, wbuff, sect, cc) != RES_OK)goto fw_error;fp->sect_clust -= cc - 1;fp->curr_sect += cc - 1;wcnt = cc * 512; continue;}if (fp->fptr >= fp->fsize) { /* Flush R/W window if needed */if (!move_window(0)) goto fw_error;fs->winsect = fp->curr_sect;}}if (!move_window(fp->curr_sect)) /* Move sector window */goto fw_error;wcnt = 512 - (WORD)(fp->fptr % 512); /* Copy fractional bytes bytes to sector window */if (wcnt > btw) wcnt = btw;memcpy(&fs->win[(WORD)fp->fptr % 512], wbuff, wcnt);fs->winflag = 1;}if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */fp->flag |= FA__WRITTEN; /* Set file changed flag */return FR_OK;fw_error: /* Abort this function due to an unrecoverable error */fp->flag |= FA__ERROR;return FR_RW_ERROR;}/*-----------------------------------------------------------------------*//* Synchronize between File and Disk *//*-----------------------------------------------------------------------*/FRESULT f_sync (FIL *fp /* Pointer to the file object */){DWORD tim;BYTE *dir;FRESULT res;FATFS *fs = fp->fs;res = validate(fs, fp->id); /* Check validity of the object */if (res == FR_OK) {if (fp->flag & FA__WRITTEN) { /* Has the file been written? *//* Update the directory entry */if (!move_window(fp->dir_sect))return FR_RW_ERROR;dir = fp->dir_ptr;dir[DIR_Attr] |= AM_ARC; /* Set archive bit */ST_DWORD(&dir[DIR_FileSize], fp->fsize); /* Update file size */ST_WORD(&dir[DIR_FstClusLO], fp->org_clust); /* Update start cluster */#if _FAT32ST_WORD(&dir[DIR_FstClusHI], fp->org_clust >> 16);#endiftim = get_fattime(); /* Updated time */ST_DWORD(&dir[DIR_WrtTime], tim);fp->flag &= ~FA__WRITTEN;res = sync();}}return res;}#endif /* !_FS_READONLY *//*-----------------------------------------------------------------------*//* Close File *//*-----------------------------------------------------------------------*/FRESULT f_close (FIL *fp /* Pointer to the file object to be closed */){FRESULT res;#if !_FS_READONLYres = f_sync(fp);#elseres = validate(fp->fs, fp->id);#endifif (res == FR_OK)fp->fs = NULL;return res;}#if _FS_MINIMIZE <= 2/*-----------------------------------------------------------------------*//* Seek File Pointer *//*-----------------------------------------------------------------------*/FRESULT f_lseek (FIL *fp, /* Pointer to the file object */DWORD ofs /* File pointer from top of file */){CLUST clust;DWORD csize;BYTE csect;FRESULT res;FATFS *fs = fp->fs;res = validate(fs, fp->id); /* Check validity of the object */if (res) return res;if (fp->flag & FA__ERROR) return FR_RW_ERROR;#if !_FS_READONLYif (ofs > fp->fsize && !(fp->flag & FA_WRITE))#elseif (ofs > fp->fsize)#endifofs = fp->fsize;fp->fptr = 0; fp->sect_clust = 1; /* Set file R/W pointer to top of the file *//* Move file R/W pointer if needed */if (ofs) {clust = fp->org_clust; /* Get start cluster */#if !_FS_READONLYif (!clust) { /* If the file does not have a cluster chain, create new cluster chain */clust = create_chain(0);if (clust == 1) goto fk_error;fp->org_clust = clust;}#endifif (clust) { /* If the file has a cluster chain, it can be followed */csize = (DWORD)fs->sects_clust * 512; /* Cluster size in unit of byte */for (;;) { /* Loop to skip leading clusters */fp->curr_clust = clust; /* Update current cluster */if (ofs <= csize) break;#if !_FS_READONLYif (fp->flag & FA_WRITE) /* Check if in write mode or not */clust = create_chain(clust); /* Force streached if in write mode */else#endifclust = get_cluster(clust); /* Only follow cluster chain if not in write mode */if (clust == 0) { /* Stop if could not follow the cluster chain */ofs = csize; break;}if (clust == 1 || clust >= fs->max_clust) goto fk_error;fp->fptr += csize; /* Update R/W pointer */ofs -= csize;}csect = (BYTE)((ofs - 1) / 512); /* Sector offset in the cluster */fp->curr_sect = clust2sect(clust) + csect; /* Current sector */fp->sect_clust = fs->sects_clust - csect; /* Left sector counter in the cluster */fp->fptr += ofs; /* Update file R/W pointer */}}#if !_FS_READONLYif ((fp->flag & FA_WRITE) && fp->fptr > fp->fsize) { /* Set updated flag if in write mode */fp->fsize = fp->fptr;fp->flag |= FA__WRITTEN;}#endifreturn FR_OK;fk_error: /* Abort this function due to an unrecoverable error */fp->flag |= FA__ERROR;return FR_RW_ERROR;}#if _FS_MINIMIZE <= 1/*-----------------------------------------------------------------------*//* Open a directroy *//*-----------------------------------------------------------------------*/FRESULT f_opendir (DIR *dirobj, /* Pointer to directory object to create */const char *path /* Pointer to the directory path */){BYTE *dir;char fn[8+3+1];FRESULT res;FATFS *fs = FatFs;res = auto_mount(&path, 0);if (res != FR_OK) return res;res = trace_path(dirobj, fn, path, &dir); /* Trace the directory path */if (res == FR_OK) { /* Trace completed */if (dir != NULL) { /* It is not the root dir */if (dir[DIR_Attr] & AM_DIR) { /* The entry is a directory */dirobj->clust =#if _FAT32((DWORD)LD_WORD(&dir[DIR_FstClusHI]) << 16) |#endifLD_WORD(&dir[DIR_FstClusLO]);dirobj->sect = clust2sect(dirobj->clust);dirobj->index = 2;} else { /* The entry is not a directory */res = FR_NO_FILE;}}dirobj->id = fs->id;}return res;}/*-----------------------------------------------------------------------*//* Read Directory Entry in Sequense *//*-----------------------------------------------------------------------*/FRESULT f_readdir (DIR *dirobj, /* Pointer to the directory object */FILINFO *finfo /* Pointer to file information to return */){BYTE *dir, c;FRESULT res;FATFS *fs = dirobj->fs;res = validate(fs, dirobj->id); /* Check validity of the object */if (res) return res;finfo->fname[0] = 0;while (dirobj->sect) {if (!move_window(dirobj->sect))return FR_RW_ERROR;dir = &fs->win[(dirobj->index & 15) * 32]; /* pointer to the directory entry */c = dir[DIR_Name];if (c == 0) break; /* Has it reached to end of dir? */if (c != 0xE5 && !(dir[DIR_Attr] & AM_VOL)) /* Is it a valid entry? */get_fileinfo(finfo, dir);if (!next_dir_entry(dirobj)) dirobj->sect = 0; /* Next entry */if (finfo->fname[0]) break; /* Found valid entry */}return FR_OK;}#if _FS_MINIMIZE == 0/*-----------------------------------------------------------------------*//* Get File Status *//*-----------------------------------------------------------------------*/FRESULT f_stat (const char *path, /* Pointer to the file path */FILINFO *finfo /* Pointer to file information to return */){BYTE *dir;char fn[8+3+1];FRESULT res;DIR dirobj;res = auto_mount(&path, 0);if (res != FR_OK) return res;res = trace_path(&dirobj, fn, path, &dir); /* Trace the file path */if (res == FR_OK) { /* Trace completed */if (dir) /* Found an object */get_fileinfo(finfo, dir);else /* It is root dir */res = FR_INVALID_NAME;}return res;}#if !_FS_READONLY/*-----------------------------------------------------------------------*//* Get Number of Free Clusters *//*-----------------------------------------------------------------------*/FRESULT f_getfree (const char *drv, /* Logical drive number */DWORD *nclust, /* Pointer to the double word to return number of free clusters */FATFS **fatfs /* Pointer to pointer to the file system object to return */){DWORD n, sect;CLUST clust;BYTE fat, f, *p;FRESULT res;FATFS *fs;/* Get drive number */res = auto_mount(&drv, 0);if (res != FR_OK) return res;*fatfs = fs = FatFs;/* If number of free cluster is valid, return it without cluster scan. */if (fs->free_clust <= fs->max_clust - 2) {*nclust = fs->free_clust;return FR_OK;}/* Count number of free clusters */fat = fs->fs_type;n = 0;if (fat == FS_FAT12) {clust = 2;do {if ((WORD)get_cluster(clust) == 0) n++;} while (++clust < fs->max_clust);} else {clust = fs->max_clust;sect = fs->fatbase;f = 0; p = 0;do {if (!f) {if (!move_window(sect++)) return FR_RW_ERROR;p = fs->win;}if (!_FAT32 || fat == FS_FAT16) {if (LD_WORD(p) == 0) n++;p += 2; f += 1;} else {if (LD_DWORD(p) == 0) n++;p += 4; f += 2;}} while (--clust);}fs->free_clust = n;#if _USE_FSINFOif (fat == FS_FAT32) fs->fsi_flag = 1;#endif*nclust = n;return FR_OK;}/*-----------------------------------------------------------------------*//* Delete a File or a Directory *//*-----------------------------------------------------------------------*/FRESULT f_unlink (const char *path /* Pointer to the file or directory path */){BYTE *dir, *sdir;DWORD dsect;char fn[8+3+1];CLUST dclust;FRESULT res;DIR dirobj;FATFS *fs = FatFs;res = auto_mount(&path, 1);if (res != FR_OK) return res;res = trace_path(&dirobj, fn, path, &dir); /* Trace the file path */if (res != FR_OK) return res; /* Trace failed */if (dir == NULL) return FR_INVALID_NAME; /* It is the root directory */if (dir[DIR_Attr] & AM_RDO) return FR_DENIED; /* It is a R/O object */dsect = fs->winsect;dclust =#if _FAT32((DWORD)LD_WORD(&dir[DIR_FstClusHI]) << 16) |#endifLD_WORD(&dir[DIR_FstClusLO]);if (dir[DIR_Attr] & AM_DIR) { /* It is a sub-directory */dirobj.clust = dclust; /* Check if the sub-dir is empty or not */dirobj.sect = clust2sect(dclust);dirobj.index = 2;do {if (!move_window(dirobj.sect)) return FR_RW_ERROR;sdir = &fs->win[(dirobj.index & 15) * 32];if (sdir[DIR_Name] == 0) break;if (sdir[DIR_Name] != 0xE5 && !(sdir[DIR_Attr] & AM_VOL))return FR_DENIED; /* The directory is not empty */} while (next_dir_entry(&dirobj));}if (!move_window(dsect)) return FR_RW_ERROR; /* Mark the directory entry 'deleted' */dir[DIR_Name] = 0xE5;fs->winflag = 1;if (!remove_chain(dclust)) return FR_RW_ERROR; /* Remove the cluster chain */return sync();}/*-----------------------------------------------------------------------*//* Create a Directory *//*-----------------------------------------------------------------------*/FRESULT f_mkdir (const char *path /* Pointer to the directory path */){BYTE *dir, *fw, n;char fn[8+3+1];DWORD sect, dsect, tim;CLUST dclust, pclust;FRESULT res;DIR dirobj;FATFS *fs = FatFs;res = auto_mount(&path, 1);if (res != FR_OK) return res;res = trace_path(&dirobj, fn, path, &dir); /* Trace the file path */if (res == FR_OK) return FR_EXIST; /* Any file or directory is already existing */if (res != FR_NO_FILE) return res;res = reserve_direntry(&dirobj, &dir); /* Reserve a directory entry */if (res != FR_OK) return res;sect = fs->winsect;dclust = create_chain(0); /* Allocate a cluster for new directory table */if (dclust == 1) return FR_RW_ERROR;dsect = clust2sect(dclust);if (!dsect) return FR_DENIED;if (!move_window(dsect)) return FR_RW_ERROR;fw = fs->win;memset(fw, 0, 512); /* Clear the directory table */for (n = 1; n < fs->sects_clust; n++) {if (disk_write(0, fw, ++dsect, 1) != RES_OK)return FR_RW_ERROR;}memset(&fw[DIR_Name], ' ', 8+3); /* Create "." entry */fw[DIR_Name] = '.';fw[DIR_Attr] = AM_DIR;tim = get_fattime();ST_DWORD(&fw[DIR_WrtTime], tim);memcpy(&fw[32], &fw[0], 32); fw[33] = '.'; /* Create ".." entry */pclust = dirobj.sclust;#if _FAT32ST_WORD(&fw[ DIR_FstClusHI], dclust >> 16);if (fs->fs_type == FS_FAT32 && pclust == fs->dirbase) pclust = 0;ST_WORD(&fw[32+DIR_FstClusHI], pclust >> 16);#endifST_WORD(&fw[ DIR_FstClusLO], dclust);ST_WORD(&fw[32+DIR_FstClusLO], pclust);fs->winflag = 1;if (!move_window(sect)) return FR_RW_ERROR;memset(&dir[0], 0, 32); /* Clean-up the new entry */memcpy(&dir[DIR_Name], fn, 8+3); /* Name */dir[DIR_NTres] = fn[11];dir[DIR_Attr] = AM_DIR; /* Attribute */ST_DWORD(&dir[DIR_WrtTime], tim); /* Crated time */ST_WORD(&dir[DIR_FstClusLO], dclust); /* Table start cluster */#if _FAT32ST_WORD(&dir[DIR_FstClusHI], dclust >> 16);#endifreturn sync();}/*-----------------------------------------------------------------------*//* Change File Attribute *//*-----------------------------------------------------------------------*/FRESULT f_chmod (const char *path, /* Pointer to the file path */BYTE value, /* Attribute bits */BYTE mask /* Attribute mask to change */){FRESULT res;BYTE *dir;DIR dirobj;char fn[8+3+1];res = auto_mount(&path, 1);if (res == FR_OK) {res = trace_path(&dirobj, fn, path, &dir); /* Trace the file path */if (res == FR_OK) { /* Trace completed */if (dir == NULL) {res = FR_INVALID_NAME;} else {mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */res = sync();}}}return res;}/*-----------------------------------------------------------------------*//* Rename File/Directory *//*-----------------------------------------------------------------------*/FRESULT f_rename (const char *path_old, /* Pointer to the old name */const char *path_new /* Pointer to the new name */){FRESULT res;DWORD sect_old;BYTE *dir_old, *dir_new, direntry[32-11];DIR dirobj;char fn[8+3+1];FATFS *fs = FatFs;res = auto_mount(&path_old, 1);if (res != FR_OK) return res;res = trace_path(&dirobj, fn, path_old, &dir_old); /* Check old object */if (res != FR_OK) return res; /* The old object is not found */if (!dir_old) return FR_NO_FILE;sect_old = fs->winsect; /* Save the object information */memcpy(direntry, &dir_old[11], 32-11);res = trace_path(&dirobj, fn, path_new, &dir_new); /* Check new object */if (res == FR_OK) return FR_EXIST; /* The new object name is already existing */if (res != FR_NO_FILE) return res; /* Is there no old name? */res = reserve_direntry(&dirobj, &dir_new); /* Reserve a directory entry */if (res != FR_OK) return res;memcpy(&dir_new[DIR_Attr], direntry, 32-11); /* Create new entry */memcpy(&dir_new[DIR_Name], fn, 8+3);dir_new[DIR_NTres] = fn[11];fs->winflag = 1;if (!move_window(sect_old)) return FR_RW_ERROR; /* Remove old entry */dir_old[DIR_Name] = 0xE5;return sync();}#endif /* !_FS_READONLY */#endif /* _FS_MINIMIZE == 0 */#endif /* _FS_MINIMIZE <= 1 */#endif /* _FS_MINIMIZE <= 2 */