From 1c137199ac4750e03dc242809d10ab918438f02c Mon Sep 17 00:00:00 2001 From: dhewg Date: Thu, 9 Apr 2009 14:20:12 +0200 Subject: [PATCH] Update to FatFs R0.07 --- diskio.c | 37 +- diskio.h | 7 +- ff.c | 2472 ++++++++++++++++++++++++++++++++++++----------------- ff.h | 437 +++++++--- integer.h | 7 + lcd.c | 208 ----- lcd.h | 11 - 7 files changed, 2031 insertions(+), 1148 deletions(-) delete mode 100644 lcd.c delete mode 100644 lcd.h diff --git a/diskio.c b/diskio.c index 56cfd86..3dcb975 100644 --- a/diskio.c +++ b/diskio.c @@ -6,8 +6,12 @@ /*-----------------------------------------------------------------------*/ #include "diskio.h" +#include "string.h" #include "sdhc.h" -#include + +#ifndef MEM2_BSS +#define MEM2_BSS +#endif static sdhci_t sdhci; static u8 buffer[512] MEM2_BSS ALIGNED(32); @@ -23,7 +27,8 @@ DSTATUS disk_initialize ( sd_init(&sdhci, 0); ret = sd_mount(&sdhci); - if(ret < 0) + + if (ret < 0) return STA_NOINIT; else return disk_status(drv); @@ -38,8 +43,9 @@ DSTATUS disk_status ( BYTE drv /* Physical drive nmuber (0..) */ ) { - if(sd_inserted(&sdhci) == 0) + if (sd_inserted(&sdhci) == 0) return STA_NODISK; + return 0; } @@ -59,15 +65,15 @@ DRESULT disk_read ( DRESULT res; res = RES_OK; - for(i = 0; i < count; i++) - { - if(sd_read(&sdhci, sector + i, 1, buffer) != 0) - { + for (i = 0; i < count; i++) { + if (sd_read(&sdhci, sector + i, 1, buffer) != 0) { res = RES_ERROR; break; } + memcpy(buff + i * 512, buffer, 512); } + return res; } @@ -77,7 +83,6 @@ DRESULT disk_read ( /* Write Sector(s) */ #if _READONLY == 0 - DRESULT disk_write ( BYTE drv, /* Physical drive nmuber (0..) */ const BYTE *buff, /* Data to be written */ @@ -89,15 +94,15 @@ DRESULT disk_write ( DRESULT res; res = RES_OK; - for(i = 0; i < count; i++) - { + for (i = 0; i < count; i++) { memcpy(buffer, buff + i * 512, 512); - if(sd_write(&sdhci, sector + i, 1, buffer) != 0) - { + + if(sd_write(&sdhci, sector + i, 1, buffer) != 0) { res = RES_ERROR; break; } } + return res; } #endif /* _READONLY */ @@ -113,13 +118,9 @@ DRESULT disk_ioctl ( void *buff /* Buffer to send/receive control data */ ) { - if(ctrl == CTRL_SYNC) + if (ctrl == CTRL_SYNC) return RES_OK; + return RES_PARERR; } -DWORD get_fattime (void) -{ - return 0; // TODO -} - diff --git a/diskio.h b/diskio.h index 1d5f818..aa9f381 100644 --- a/diskio.h +++ b/diskio.h @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------- -/ Low level disk interface modlue include file R0.06 (C)ChaN, 2007 +/ Low level disk interface modlue include file R0.07 (C)ChaN, 2009 /-----------------------------------------------------------------------*/ #ifndef _DISKIO @@ -26,6 +26,7 @@ typedef enum { /*---------------------------------------*/ /* Prototypes for disk control functions */ +BOOL assign_drives (int argc, char *argv[]); DSTATUS disk_initialize (BYTE); DSTATUS disk_status (BYTE); DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE); @@ -33,8 +34,6 @@ DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE); DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE); #endif DRESULT disk_ioctl (BYTE, BYTE, void*); -void disk_timerproc (void); - @@ -48,7 +47,7 @@ void disk_timerproc (void); /* Command code for disk_ioctrl() */ /* Generic command */ -#define CTRL_SYNC 0 /* Mandatory for read/write configuration */ +#define CTRL_SYNC 0 /* Mandatory for write functions */ #define GET_SECTOR_COUNT 1 /* Mandatory for only f_mkfs() */ #define GET_SECTOR_SIZE 2 #define GET_BLOCK_SIZE 3 /* Mandatory for only f_mkfs() */ diff --git a/ff.c b/ff.c index ab28a3d..a66cb6d 100644 --- a/ff.c +++ b/ff.c @@ -1,15 +1,15 @@ /*----------------------------------------------------------------------------/ -/ FatFs - FAT file system module R0.06 (C)ChaN, 2008 +/ FatFs - FAT file system module R0.07 (C)ChaN, 2009 /-----------------------------------------------------------------------------/ -/ 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. +/ FatFs module is an open source software to implement FAT file system to +/ small embedded systems. This is a free software and is opened for education, +/ research and commecial developments under license policy of following trems. / -/ Copyright (C) 2008, ChaN, all right reserved. +/ Copyright (C) 2009, 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 -/ commercial use without restriction under your responsibility. +/ * The FatFs module is a free software and there is NO WARRANTY. +/ * No restriction on use. You can use, modify and redistribute it for +/ personal, non-profit or commercial use UNDER YOUR RESPONSIBILITY. / * Redistributions of source code must retain the above copyright notice. / /-----------------------------------------------------------------------------/ @@ -52,11 +52,117 @@ / Apr 01,'08 R0.06 Added fputc(), fputs(), fprintf() and fgets(). / Improved performance of f_lseek() on moving to the same / or following cluster. +/ +/ Apr 01,'09 R0.07 Merged Tiny-FatFs as a buffer configuration option. +/ Added long file name support. +/ Added multiple code page support. +/ Added re-entrancy for multitask operation. +/ Added auto cluster size selection to f_mkfs(). +/ Added rewind option to f_readdir(). +/ Changed result code of critical errors. +/ Renamed string functions to avoid name collision. /---------------------------------------------------------------------------*/ -#include -#include "ff.h" /* FatFs declarations */ -#include "diskio.h" /* Include file for user provided disk functions */ +#include "ff.h" /* FatFs configurations and declarations */ +#include "diskio.h" /* Declarations of low level disk I/O functions */ + + +/*-------------------------------------------------------------------------- + + Module Private Definitions + +---------------------------------------------------------------------------*/ + +#if _EXCLUDE_LIB +static +void MemCpy (void* dst, const void* src, int cnt) { + char *d = (char*)dst; + const char *s = (const char *)src; + while (cnt--) *d++ = *s++; +} + +static +void MemSet (void* dst, int val, int cnt) { + char *d = (char*)dst; + while (cnt--) *d++ = val; +} + +static +int MemCmp (const void* dst, const void* src, int cnt) { + const char *d = (const char *)dst, *s = (const char *)src; + int r = 0; + while (cnt-- && !(r = *d++ - *s++)); + return r; +} + +static +char *StrChr (const char* str, int chr) { + while (*str && *str != chr) str++; + return (*str == chr) ? (char*)str : 0; +} + +#else +#include "string.h" +#define MemCpy(x,y,z) memcpy(x,y,z) +#define MemCmp(x,y,z) memcmp(x,y,z) +#define MemSet(x,y,z) memset(x,y,z) +#define StrChr(x,y) strchr(x,y) + +#endif + +#ifndef NULL +#define NULL 0 +#endif + + +#if _FS_REENTRANT +#if _USE_LFN == 1 +#error Static LFN work area must not be used in re-entrant configuration. +#endif +#define ENTER_FF(fs) { if (!lock_fs(fs)) return FR_TIMEOUT; } +#define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; } + +#else +#define ENTER_FF(fs) +#define LEAVE_FF(fs, res) return res + +#endif + + +#define ABORT(fs, res) { fp->flag |= FA__ERROR; LEAVE_FF(fs, res); } + + + + +/*-------------------------------------------------------------------------- + + Module Private Work Area + +---------------------------------------------------------------------------*/ + +static +FATFS *FatFs[_DRIVES]; /* Pointer to the file system objects (logical drives) */ +static +WORD Fsid; /* File system mount ID */ + + +#if _USE_LFN == 1 /* LFN with static LFN working buffer */ +static +WORD LfnBuf[_MAX_LFN + 1]; +#define NAMEBUF(sp,lp) BYTE sp[12]; WCHAR *lp = LfnBuf +#define INITBUF(dj,sp,lp) dj.fn = sp; dj.lfn = lp + +#elif _USE_LFN > 1 /* LFN with dynamic LFN working buffer */ +#define NAMEBUF(sp,lp) BYTE sp[12]; WCHAR lbuf[_MAX_LFN + 1], *lp = lbuf +#define INITBUF(dj,sp,lp) dj.fn = sp; dj.lfn = lp + +#else /* No LFN */ +#define NAMEBUF(sp,lp) BYTE sp[12] +#define INITBUF(dj,sp,lp) dj.fn = sp + +#endif + + /*-------------------------------------------------------------------------- @@ -65,10 +171,33 @@ ---------------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------*/ +/* Request/Release grant to access the fs object (Platform dependent) */ +/*-----------------------------------------------------------------------*/ +#if _FS_REENTRANT static -FATFS *FatFs[_DRIVES]; /* Pointer to the file system objects (logical drives) */ +BOOL lock_fs ( + FATFS *fs /* File system object */ +) +{ + return (WaitForSingleObject(fs->h_mutex, _TIMEOUT) == WAIT_OBJECT_0) ? TRUE : FALSE; +} + + static -WORD fsid; /* File system mount ID */ +void unlock_fs ( + FATFS *fs, /* File system object */ + FRESULT res /* Result code to be returned */ +) +{ + if (res != FR_NOT_ENABLED && + res != FR_INVALID_DRIVE && + ree != FR_INVALID_OBJECT && + res != FR_TIMEOUT) { + ReleaseMutex(fs->h_mutex); + } +} +#endif @@ -77,7 +206,7 @@ WORD fsid; /* File system mount ID */ /*-----------------------------------------------------------------------*/ static -BOOL move_window ( /* TRUE: successful, FALSE: failed */ +FRESULT move_window ( FATFS *fs, /* File system object */ DWORD sector /* Sector number to make apperance in the fs->win[] */ ) /* Move to zero only writes back dirty window */ @@ -88,13 +217,13 @@ BOOL move_window ( /* TRUE: successful, FALSE: failed */ wsect = fs->winsect; if (wsect != sector) { /* Changed current window */ #if !_FS_READONLY - BYTE n; - if (fs->winflag) { /* Write back dirty window if needed */ + if (fs->wflag) { /* Write back dirty window if needed */ if (disk_write(fs->drive, fs->win, wsect, 1) != RES_OK) - return FALSE; - fs->winflag = 0; + return FR_DISK_ERR; + fs->wflag = 0; if (wsect < (fs->fatbase + fs->sects_fat)) { /* In FAT area */ - for (n = fs->n_fats; n >= 2; n--) { /* Refrect the change to FAT copy */ + BYTE nf; + for (nf = fs->n_fats; nf >= 2; nf--) { /* Refrect the change to FAT copy */ wsect += fs->sects_fat; disk_write(fs->drive, fs->win, wsect, 1); } @@ -103,11 +232,12 @@ BOOL move_window ( /* TRUE: successful, FALSE: failed */ #endif if (sector) { if (disk_read(fs->drive, fs->win, sector, 1) != RES_OK) - return FALSE; + return FR_DISK_ERR; fs->winsect = sector; } } - return TRUE; + + return FR_OK; } @@ -116,33 +246,35 @@ BOOL move_window ( /* TRUE: successful, FALSE: failed */ /*-----------------------------------------------------------------------*/ /* Clean-up cached data */ /*-----------------------------------------------------------------------*/ - #if !_FS_READONLY static -FRESULT sync ( /* FR_OK: successful, FR_RW_ERROR: failed */ +FRESULT sync ( /* FR_OK: successful, FR_DISK_ERR: failed */ FATFS *fs /* File system object */ ) { - fs->winflag = 1; - if (!move_window(fs, 0)) return FR_RW_ERROR; -#if _USE_FSINFO - /* Update FSInfo sector if needed */ - if (fs->fs_type == FS_FAT32 && fs->fsi_flag) { - 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(fs->drive, fs->win, fs->fsi_sector, 1); - fs->fsi_flag = 0; + FRESULT res; + + + res = move_window(fs, 0); + if (res == FR_OK) { + /* Update FSInfo sector if needed */ + if (fs->fs_type == FS_FAT32 && fs->fsi_flag) { + 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(fs->drive, fs->win, fs->fsi_sector, 1); + fs->fsi_flag = 0; + } + /* Make sure that no pending write process in the physical drive */ + if (disk_ioctl(fs->drive, CTRL_SYNC, (void*)NULL) != RES_OK) + res = FR_DISK_ERR; } -#endif - /* Make sure that no pending write process in the physical drive */ - if (disk_ioctl(fs->drive, CTRL_SYNC, NULL) != RES_OK) - return FR_RW_ERROR; - return FR_OK; + + return res; } #endif @@ -154,37 +286,38 @@ FRESULT sync ( /* FR_OK: successful, FR_RW_ERROR: failed */ /*-----------------------------------------------------------------------*/ static -DWORD get_cluster ( /* 0,>=2: successful, 1: failed */ +DWORD get_cluster ( /* 0xFFFFFFFF:Disk error, 1:Interal error, Else:Cluster status */ FATFS *fs, /* File system object */ - DWORD clust /* Cluster# to get the link information */ + DWORD clst /* Cluster# to get the link information */ ) { WORD wc, bc; - DWORD fatsect; + DWORD fsect; - if (clust >= 2 && clust < fs->max_clust) { /* Is it a valid cluster#? */ - fatsect = fs->fatbase; - switch (fs->fs_type) { - case FS_FAT12 : - bc = (WORD)clust * 3 / 2; - if (!move_window(fs, fatsect + (bc / SS(fs)))) break; - wc = fs->win[bc & (SS(fs) - 1)]; bc++; - if (!move_window(fs, fatsect + (bc / SS(fs)))) break; - wc |= (WORD)fs->win[bc & (SS(fs) - 1)] << 8; - return (clust & 1) ? (wc >> 4) : (wc & 0xFFF); + if (clst < 2 || clst >= fs->max_clust) /* Check cluster address range */ + return 1; - case FS_FAT16 : - if (!move_window(fs, fatsect + (clust / (SS(fs) / 2)))) break; - return LD_WORD(&fs->win[((WORD)clust * 2) & (SS(fs) - 1)]); + fsect = fs->fatbase; + switch (fs->fs_type) { + case FS_FAT12 : + bc = (WORD)clst * 3 / 2; + if (move_window(fs, fsect + (bc / SS(fs)))) break; + wc = fs->win[bc & (SS(fs) - 1)]; bc++; + if (move_window(fs, fsect + (bc / SS(fs)))) break; + wc |= (WORD)fs->win[bc & (SS(fs) - 1)] << 8; + return (clst & 1) ? (wc >> 4) : (wc & 0xFFF); - case FS_FAT32 : - if (!move_window(fs, fatsect + (clust / (SS(fs) / 4)))) break; - return LD_DWORD(&fs->win[((WORD)clust * 4) & (SS(fs) - 1)]) & 0x0FFFFFFF; - } + case FS_FAT16 : + if (move_window(fs, fsect + (clst / (SS(fs) / 2)))) break; + return LD_WORD(&fs->win[((WORD)clst * 2) & (SS(fs) - 1)]); + + case FS_FAT32 : + if (move_window(fs, fsect + (clst / (SS(fs) / 4)))) break; + return LD_DWORD(&fs->win[((WORD)clst * 4) & (SS(fs) - 1)]) & 0x0FFFFFFF; } - return 1; /* Out of cluster range, or an error occured */ + return 0xFFFFFFFF; /* An error occured at the disk I/O layer */ } @@ -193,49 +326,59 @@ DWORD get_cluster ( /* 0,>=2: successful, 1: failed */ /*-----------------------------------------------------------------------*/ /* Change a cluster status */ /*-----------------------------------------------------------------------*/ - #if !_FS_READONLY static -BOOL put_cluster ( /* TRUE: successful, FALSE: failed */ +FRESULT put_cluster ( FATFS *fs, /* File system object */ - DWORD clust, /* Cluster# to change (must be 2 to fs->max_clust-1) */ + DWORD clst, /* Cluster# to be changed (must be 2 to fs->max_clust-1) */ DWORD val /* New value to mark the cluster */ ) { WORD bc; BYTE *p; - DWORD fatsect; + DWORD fsect; + FRESULT res; - fatsect = fs->fatbase; - switch (fs->fs_type) { - case FS_FAT12 : - bc = (WORD)clust * 3 / 2; - if (!move_window(fs, fatsect + (bc / SS(fs)))) return FALSE; - p = &fs->win[bc & (SS(fs) - 1)]; - *p = (clust & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; - bc++; - fs->winflag = 1; - if (!move_window(fs, fatsect + (bc / SS(fs)))) return FALSE; - p = &fs->win[bc & (SS(fs) - 1)]; - *p = (clust & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); - break; + if (clst < 2 || clst >= fs->max_clust) { /* Check cluster address range */ + res = FR_INT_ERR; - case FS_FAT16 : - if (!move_window(fs, fatsect + (clust / (SS(fs) / 2)))) return FALSE; - ST_WORD(&fs->win[((WORD)clust * 2) & (SS(fs) - 1)], (WORD)val); - break; + } else { + fsect = fs->fatbase; + switch (fs->fs_type) { + case FS_FAT12 : + bc = (WORD)clst * 3 / 2; + res = move_window(fs, fsect + (bc / SS(fs))); + if (res != FR_OK) break; + p = &fs->win[bc & (SS(fs) - 1)]; + *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; + bc++; + fs->wflag = 1; + res = move_window(fs, fsect + (bc / SS(fs))); + if (res != FR_OK) break; + p = &fs->win[bc & (SS(fs) - 1)]; + *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); + break; - case FS_FAT32 : - if (!move_window(fs, fatsect + (clust / (SS(fs) / 4)))) return FALSE; - ST_DWORD(&fs->win[((WORD)clust * 4) & (SS(fs) - 1)], val); - break; + case FS_FAT16 : + res = move_window(fs, fsect + (clst / (SS(fs) / 2))); + if (res != FR_OK) break; + ST_WORD(&fs->win[((WORD)clst * 2) & (SS(fs) - 1)], (WORD)val); + break; - default : - return FALSE; + case FS_FAT32 : + res = move_window(fs, fsect + (clst / (SS(fs) / 4))); + if (res != FR_OK) break; + ST_DWORD(&fs->win[((WORD)clst * 4) & (SS(fs) - 1)], val); + break; + + default : + res = FR_INT_ERR; + } + fs->wflag = 1; } - fs->winflag = 1; - return TRUE; + + return res; } #endif /* !_FS_READONLY */ @@ -245,30 +388,38 @@ BOOL put_cluster ( /* TRUE: successful, FALSE: failed */ /*-----------------------------------------------------------------------*/ /* Remove a cluster chain */ /*-----------------------------------------------------------------------*/ - #if !_FS_READONLY static -BOOL remove_chain ( /* TRUE: successful, FALSE: failed */ - FATFS *fs, /* File system object */ - DWORD clust /* Cluster# to remove chain from */ +FRESULT remove_chain ( + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to remove chain from */ ) { + FRESULT res; DWORD nxt; - while (clust >= 2 && clust < fs->max_clust) { - nxt = get_cluster(fs, clust); - if (nxt == 1) return FALSE; - if (!put_cluster(fs, clust, 0)) return FALSE; - if (fs->free_clust != 0xFFFFFFFF) { - fs->free_clust++; -#if _USE_FSINFO - fs->fsi_flag = 1; -#endif + if (clst < 2 || clst >= fs->max_clust) { /* Check cluster address range */ + res = FR_INT_ERR; + + } else { + res = FR_OK; + while (clst < fs->max_clust) { /* Not a last link? */ + nxt = get_cluster(fs, clst); /* Get cluster status */ + if (nxt == 0) break; /* Empty cluster? */ + if (nxt == 1) { res = FR_INT_ERR; break; } /* Internal error? */ + if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } /* Disk error? */ + res = put_cluster(fs, clst, 0); /* Mark the cluster "empty" */ + if (res != FR_OK) break; + if (fs->free_clust != 0xFFFFFFFF) { /* Update FSInfo */ + fs->free_clust++; + fs->fsi_flag = 1; + } + clst = nxt; /* Next cluster */ } - clust = nxt; } - return TRUE; + + return res; } #endif @@ -278,26 +429,26 @@ BOOL remove_chain ( /* TRUE: successful, FALSE: failed */ /*-----------------------------------------------------------------------*/ /* Stretch or create a cluster chain */ /*-----------------------------------------------------------------------*/ - #if !_FS_READONLY static -DWORD create_chain ( /* 0: No free cluster, 1: Error, >=2: New cluster number */ +DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ FATFS *fs, /* File system object */ - DWORD clust /* Cluster# to stretch, 0 means create new */ + DWORD clst /* Cluster# to stretch. 0 means create a new chain. */ ) { - DWORD cstat, ncl, scl, mcl = fs->max_clust; + DWORD cs, ncl, scl, mcl; - if (clust == 0) { /* Create new chain */ + mcl = fs->max_clust; + if (clst == 0) { /* Create new chain */ scl = fs->last_clust; /* Get suggested start point */ if (scl == 0 || scl >= mcl) scl = 1; } else { /* Stretch existing chain */ - cstat = get_cluster(fs, 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; + cs = get_cluster(fs, clst); /* Check the cluster status */ + if (cs < 2) return 1; /* It is an invalid cluster */ + if (cs < mcl) return cs; /* It is already followed by next cluster */ + scl = clst; } ncl = scl; /* Start cluster */ @@ -307,21 +458,24 @@ DWORD create_chain ( /* 0: No free cluster, 1: Error, >=2: New cluster number */ ncl = 2; if (ncl > scl) return 0; /* No free custer */ } - cstat = get_cluster(fs, ncl); /* Get the cluster status */ - if (cstat == 0) break; /* Found a free cluster */ - if (cstat == 1) return 1; /* Any error occured */ + cs = get_cluster(fs, ncl); /* Get the cluster status */ + if (cs == 0) break; /* Found a free cluster */ + if (cs == 0xFFFFFFFF || cs == 1)/* An error occured */ + return cs; if (ncl == scl) return 0; /* No free custer */ } - if (!put_cluster(fs, ncl, 0x0FFFFFFF)) return 1; /* Mark the new cluster "in use" */ - if (clust != 0 && !put_cluster(fs, clust, ncl)) return 1; /* Link it to previous one if needed */ + if (put_cluster(fs, ncl, 0x0FFFFFFF)) /* Mark the new cluster "in use" */ + return 0xFFFFFFFF; + if (clst != 0) { /* Link it to previous one if needed */ + if (put_cluster(fs, clst, ncl)) + return 0xFFFFFFFF; + } - fs->last_clust = ncl; /* Update fsinfo */ + fs->last_clust = ncl; /* Update FSINFO */ if (fs->free_clust != 0xFFFFFFFF) { fs->free_clust--; -#if _USE_FSINFO fs->fsi_flag = 1; -#endif } return ncl; /* Return new cluster number */ @@ -338,91 +492,772 @@ DWORD create_chain ( /* 0: No free cluster, 1: Error, >=2: New cluster number */ static DWORD clust2sect ( /* !=0: sector number, 0: failed - invalid cluster# */ FATFS *fs, /* File system object */ - DWORD clust /* Cluster# to be converted */ + DWORD clst /* Cluster# to be converted */ ) { - clust -= 2; - if (clust >= (fs->max_clust - 2)) return 0; /* Invalid cluster# */ - return clust * fs->csize + fs->database; + clst -= 2; + if (clst >= (fs->max_clust - 2)) return 0; /* Invalid cluster# */ + return clst * fs->csize + fs->database; } /*-----------------------------------------------------------------------*/ -/* Move directory pointer to next */ +/* Seek directory index */ /*-----------------------------------------------------------------------*/ static -BOOL next_dir_entry ( /* TRUE: successful, FALSE: could not move next */ - DIR *dj /* Pointer to directory object */ +FRESULT dir_seek ( + DIR *dj, /* Pointer to directory object */ + WORD idx /* Directory index number */ ) { - DWORD clust; - WORD idx; + DWORD clst; + WORD ic; - idx = dj->index + 1; - if ((idx & ((SS(dj->fs) - 1) / 32)) == 0) { /* Table sector changed? */ - dj->sect++; /* Next sector */ - if (dj->clust == 0) { /* In static table */ - if (idx >= dj->fs->n_rootdir) return FALSE; /* Reached to end of table */ - } else { /* In dynamic table */ - if (((idx / (SS(dj->fs) / 32)) & (dj->fs->csize - 1)) == 0) { /* Cluster changed? */ - clust = get_cluster(dj->fs, dj->clust); /* Get next cluster */ - if (clust < 2 || clust >= dj->fs->max_clust) /* Reached to end of table */ - return FALSE; - dj->clust = clust; /* Initialize for new cluster */ - dj->sect = clust2sect(dj->fs, clust); + dj->index = idx; + clst = dj->sclust; + if (clst == 1 || clst >= dj->fs->max_clust) /* Check start cluster range */ + return FR_INT_ERR; + + if (clst == 0) { /* Static table */ + if (idx >= dj->fs->n_rootdir) /* Index is out of range */ + return FR_INT_ERR; + dj->sect = dj->fs->dirbase + idx / (SS(dj->fs) / 32); + } + else { /* Dynamic table */ + ic = SS(dj->fs) / 32 * dj->fs->csize; /* Indexes per cluster */ + while (idx >= ic) { /* Follow cluster chain */ + clst = get_cluster(dj->fs, clst); /* Get next cluster */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst < 2 || clst >= dj->fs->max_clust) /* Reached to end of table or int error */ + return FR_INT_ERR; + idx -= ic; + } + dj->clust = clst; + dj->sect = clust2sect(dj->fs, clst) + idx / (SS(dj->fs) / 32); + } + dj->dir = dj->fs->win + (idx % (SS(dj->fs) / 32)) * 32; + + return FR_OK; /* Seek succeeded */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Move directory index next */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_next ( /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not streach */ + DIR *dj, /* Pointer to directory object */ + BOOL streach /* FALSE: Do not streach table, TRUE: Streach table if needed */ +) +{ + DWORD clst; + WORD i; + + + i = dj->index + 1; + if (!i || !dj->sect) /* Report EOT when index has reached 65535 */ + return FR_NO_FILE; + + if (!(i % (SS(dj->fs) / 32))) { /* Sector changed? */ + dj->sect++; /* Next sector */ + + if (dj->sclust == 0) { /* Static table */ + if (i >= dj->fs->n_rootdir) /* Report EOT when end of table */ + return FR_NO_FILE; + } + else { /* Dynamic table */ + if (((i / (SS(dj->fs) / 32)) & (dj->fs->csize - 1)) == 0) { /* Cluster changed? */ + clst = get_cluster(dj->fs, dj->clust); /* Get next cluster */ + if (clst <= 1) return FR_INT_ERR; + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; + if (clst >= dj->fs->max_clust) { /* When it reached end of dinamic table */ +#if !_FS_READONLY + BYTE c; + if (!streach) return FR_NO_FILE; /* When do not streach, report EOT */ + clst = create_chain(dj->fs, dj->clust); /* Streach cluster chain */ + if (clst == 0) return FR_DENIED; /* No free cluster */ + if (clst == 1) return FR_INT_ERR; + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; + /* Clean-up streached table */ + if (move_window(dj->fs, 0)) return FR_DISK_ERR; /* Flush active window */ + MemSet(dj->fs->win, 0, SS(fs)); /* Clear window buffer */ + dj->fs->winsect = clust2sect(dj->fs, clst); /* Cluster start sector */ + for (c = 0; c < dj->fs->csize; c++) { /* Fill the new cluster with 0 */ + dj->fs->wflag = 1; + if (move_window(dj->fs, 0)) return FR_DISK_ERR; + dj->fs->winsect++; + } + dj->fs->winsect -= c; /* Rewind window address */ +#else + return FR_NO_FILE; /* Report EOT */ +#endif + } + dj->clust = clst; /* Initialize data for new cluster */ + dj->sect = clust2sect(dj->fs, clst); } } } - dj->index = idx; /* Lower several bits of dj->index indicates offset in dj->sect */ + + dj->index = i; + dj->dir = dj->fs->win + (i % (SS(dj->fs) / 32)) * 32; + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Test/Pick/Fit an LFN segment from/to directory entry */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +static +const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* Offset of LFN chars in the directory entry */ + + +static +BOOL test_lfn ( /* TRUE:Matched, FALSE:Not matched */ + WCHAR *lfnbuf, /* Pointer to the LFN to be compared */ + BYTE *dir /* Pointer to the directory entry containing a part of LFN */ +) +{ + int i, s; + WCHAR wc1, wc2; + + + i = ((dir[LDIR_Ord] & 0xBF) - 1) * 13; /* Offset in the LFN buffer */ + s = 0; + do { + if (i >= _MAX_LFN) return FALSE; /* Out of buffer range? */ + wc1 = LD_WORD(dir+LfnOfs[s]); /* Get both characters to compare */ + wc2 = lfnbuf[i++]; + if (IsLower(wc1)) wc1 -= 0x20; /* Compare it (ignore case) */ + if (IsLower(wc2)) wc2 -= 0x20; + if (wc1 != wc2) return FALSE; + } while (++s < 13 && wc1); /* Repeat until last char or a NUL char is processed */ + + return TRUE; /* The LFN entry matched */ +} + + + +static +BOOL pick_lfn ( /* TRUE:Succeeded, FALSE:Buffer overflow */ + WCHAR *lfnbuf, /* Pointer to the Unicode-LFN buffer */ + BYTE *dir /* Pointer to the directory entry */ +) +{ + int i, s; + WCHAR wchr; + + + i = ((dir[LDIR_Ord] & 0xBF) - 1) * 13; /* Offset in the LFN buffer */ + s = 0; + do { + wchr = LD_WORD(dir+LfnOfs[s]); /* Get an LFN char */ + if (!wchr) break; /* End of LFN? */ + if (i >= _MAX_LFN) return FALSE; /* Buffer overflow */ + lfnbuf[i++] = wchr; /* Store it */ + } while (++s < 13); /* Repeat until last char is copied */ + if (dir[LDIR_Ord] & 0x40) lfnbuf[i] = 0; /* Put terminator if last LFN entry */ + return TRUE; } - - -/*-----------------------------------------------------------------------*/ -/* Get file status from directory entry */ -/*-----------------------------------------------------------------------*/ - -#if _FS_MINIMIZE <= 1 +#if !_FS_READONLY static -void get_fileinfo ( /* No return code */ - FILINFO *finfo, /* Ptr to store the file information */ - const BYTE *dir /* Ptr to the directory entry */ +void fit_lfn ( + const WCHAR *lfnbuf, /* Pointer to the LFN buffer */ + BYTE *dir, /* Pointer to the directory entry */ + BYTE ord, /* LFN order (1-20) */ + BYTE sum /* SFN sum */ ) { - BYTE n, c, a; + int i, s; + WCHAR wchr; + + + dir[LDIR_Chksum] = sum; /* Set check sum */ + dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */ + dir[LDIR_Type] = 0; + ST_WORD(dir+LDIR_FstClusLO, 0); + + i = (ord - 1) * 13; /* Offset in the LFN buffer */ + s = wchr = 0; + do { + if (wchr != 0xFFFF) wchr = lfnbuf[i++]; /* Get an effective char */ + ST_WORD(dir+LfnOfs[s], wchr); /* Put it */ + if (!wchr) wchr = 0xFFFF; /* Padding chars following last char */ + } while (++s < 13); + if (wchr == 0xFFFF || !lfnbuf[i]) ord |= 0x40;/* Bottom LFN part is the start of LFN sequence */ + dir[LDIR_Ord] = ord; /* Set the LFN order */ +} + +#endif +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Create numbered name */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +void gen_numname ( + BYTE *dst, /* Pointer to genartated SFN */ + const BYTE *src, /* Pointer to source SFN to be modified */ + const WCHAR *lfn, /* Pointer to LFN */ + WORD num /* Sequense number */ +) +{ + char ns[8]; + int i, j; + + + MemCpy(dst, src, 11); + + if (num > 5) { /* On many collisions, generate a hash number instead of sequencial number */ + do num = (num >> 1) + (num << 15) + (WORD)*lfn++; while (*lfn); + } + + /* itoa */ + i = 7; + do { + ns[i--] = (num % 10) + '0'; + num /= 10; + } while (num); + ns[i] = '~'; + + /* Append the number */ + for (j = 0; j < i && dst[j] != ' '; j++) { + if (IsDBCS1(dst[j])) { + if (j == i - 1) break; + j++; + } + } + do { + dst[j++] = (i < 8) ? ns[i++] : ' '; + } while (j < 8); +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Calculate sum of an SFN */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +static +BYTE sum_sfn ( + const BYTE *dir /* Ptr to directory entry */ +) +{ + BYTE sum = 0; + int n = 11; + + do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n); + return sum; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Find an object in the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_find ( + DIR *dj /* Pointer to the directory object linked to the file name */ +) +{ + FRESULT res; + BYTE a, c, stat, ord, sum, *dir; + + ord = sum = 0xFF; stat = *(dj->fn+11); + do { + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + dir = dj->dir; /* Ptr to the directory entry of current index */ + c = dir[DIR_Name]; + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ + a = dir[DIR_Attr] & AM_MASK; +#if _USE_LFN /* LFN configuration */ + if (c == 0xE5 || c == '.' || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ + ord = 0xFF; + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (dj->lfn) { + if (c & 0x40) { /* Is it start of LFN sequence? */ + sum = dir[LDIR_Chksum]; + c &= 0xBF; ord = c; /* LFN start order */ + dj->lfn_idx = dj->index; + } + /* Check LFN validity. Compare LFN if it is out of 8.3 format */ + ord = (c == ord && sum == dir[LDIR_Chksum] && (!(stat & 1) || test_lfn(dj->lfn, dir))) ? ord - 1 : 0xFF; + } + } else { /* An SFN entry is found */ + if (ord || sum != sum_sfn(dir)) { /* Did not LFN match? */ + dj->lfn_idx = 0xFFFF; + ord = 0xFF; + } + if (stat & 1) { /* Match LFN if it is out of 8.3 format */ + if (ord == 0) break; + } else { /* Match SFN if LFN is in 8.3 format */ + if (!MemCmp(dir, dj->fn, 11)) break; + } + } + } +#else /* Non LFN configuration */ + if (c != 0xE5 && c != '.' && !(a & AM_VOL) && !MemCmp(dir, dj->fn, 11)) /* Is it a valid entry? */ + break; +#endif + res = dir_next(dj, FALSE); /* Next entry */ + } while (res == FR_OK); + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read an object from the directory */ +/*-----------------------------------------------------------------------*/ +#if _FS_MINIMIZE <= 2 +static +FRESULT dir_read ( + DIR *dj /* Pointer to the directory object to store read object name */ +) +{ + FRESULT res; + BYTE a, c, ord, sum, *dir; + + + ord = sum = 0xFF; + res = FR_NO_FILE; + while (dj->sect) { + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + dir = dj->dir; /* Ptr to the directory entry of current index */ + c = dir[DIR_Name]; + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ + a = dir[DIR_Attr] & AM_MASK; +#if _USE_LFN /* LFN configuration */ + if (c == 0xE5 || c == '.' || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ + ord = 0xFF; + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (c & 0x40) { /* Is it start of LFN sequence? */ + sum = dir[LDIR_Chksum]; + c &= 0xBF; ord = c; + dj->lfn_idx = dj->index; + } + /* Check LFN validity and capture it */ + ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF; + } else { /* An SFN entry is found */ + if (ord || sum != sum_sfn(dir)) /* Is there a valid LFN entry? */ + dj->lfn_idx = 0xFFFF; /* No LFN. */ + break; + } + } +#else /* Non LFN configuration */ + if (c != 0xE5 && c != '.' && !(a & AM_VOL)) /* Is it a valid entry? */ + break; +#endif + res = dir_next(dj, FALSE); /* Next entry */ + if (res != FR_OK) break; + } + + if (res != FR_OK) dj->sect = 0; + + return res; +} +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Register an object to the directory */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT dir_register ( /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */ + DIR *dj /* Target directory with object name to be created */ +) +{ + FRESULT res; + BYTE c, *dir; + +#if _USE_LFN /* LFN configuration */ + WORD n, ne, is; + BYTE sn[12], *fn, sum; + WCHAR *lfn; + + fn = dj->fn; lfn = dj->lfn; + MemCpy(sn, fn, 12); + if (sn[11] & 1) { /* When LFN is out of 8.3 format, generate a numbered name */ + fn[11] = 0; dj->lfn = NULL; /* Find only SFN */ + for (n = 1; n < 100; n++) { + gen_numname(fn, sn, lfn, n); /* Generate a numbered name */ + res = dir_seek(dj, 0); + if (res != FR_OK) break; + res = dir_find(dj); /* Check if the name collides with existing SFN */ + if (res != FR_OK) break; + } + if (n == 100) return FR_DENIED; /* Abort if too many collisions */ + if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */ + fn[11] = sn[11]; dj->lfn = lfn; + } + if (sn[11] & 2) { /* When eliminate LFN, reserve only an SFN entry. */ + ne = 1; + } else { /* Otherwise reserve an SFN + LFN entries. */ + for (ne = 0; lfn[ne]; ne++) ; + ne = (ne + 25) / 13; + } + + /* Reserve contiguous entries */ + res = dir_seek(dj, 0); + if (res != FR_OK) return res; + n = is = 0; + do { + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + c = *dj->dir; /* Check the entry status */ + if (c == 0xE5 || c == 0) { /* Is it a blank entry? */ + if (n == 0) is = dj->index; /* First index of the contigulus entry */ + if (++n == ne) break; /* A contiguous entry that requiered count is found */ + } else { + n = 0; /* Not a blank entry. Restart to search */ + } + res = dir_next(dj, TRUE); /* Next entry with table streach */ + } while (res == FR_OK); + + if (res == FR_OK && ne > 1) { /* Initialize LFN entry if needed */ + res = dir_seek(dj, is); + if (res == FR_OK) { + sum = sum_sfn(dj->fn); /* Sum of the SFN tied to the LFN */ + ne--; + do { /* Store LFN entries in bottom first */ + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + fit_lfn(dj->lfn, dj->dir, (BYTE)ne, sum); + dj->fs->wflag = 1; + res = dir_next(dj, FALSE); /* Next entry */ + } while (res == FR_OK && --ne); + } + } + +#else /* Non LFN configuration */ + res = dir_seek(dj, 0); + if (res == FR_OK) { + do { /* Find a blank entry for the SFN */ + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + c = *dj->dir; + if (c == 0xE5 || c == 0) break; /* Is it a blank entry? */ + res = dir_next(dj, TRUE); /* Next entry with table streach */ + } while (res == FR_OK); + } +#endif + + if (res == FR_OK) { /* Initialize the SFN entry */ + res = move_window(dj->fs, dj->sect); + if (res == FR_OK) { + dir = dj->dir; + MemSet(dir, 0, 32); /* Clean the entry */ + MemCpy(dir, dj->fn, 11); /* Put SFN */ + dir[DIR_NTres] = *(dj->fn+11) & 0x18; /* Put NT flag */ + dj->fs->wflag = 1; + } + } + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Remove an object from the directory */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY && !_FS_MINIMIZE +static +FRESULT dir_remove ( /* FR_OK: Successful, FR_DISK_ERR: A disk error */ + DIR *dj /* Directory object pointing the entry to be removed */ +) +{ + FRESULT res; + +#if _USE_LFN /* LFN configuration */ + WORD i; + + i = dj->index; /* SFN index */ + res = dir_seek(dj, (WORD)((dj->lfn_idx == 0xFFFF) ? i : dj->lfn_idx)); /* Goto the SFN or top of the LFN entries */ + if (res == FR_OK) { + do { + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + *dj->dir = 0xE5; /* Mark the entry "deleted" */ + dj->fs->wflag = 1; + if (dj->index >= i) break; /* When SFN is deleted, all entries of the object is deleted. */ + res = dir_next(dj, FALSE); /* Next entry */ + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR; + } + +#else /* Non LFN configuration */ + res = dir_seek(dj, dj->index); + if (res == FR_OK) { + res = move_window(dj->fs, dj->sect); + if (res == FR_OK) { + *dj->dir = 0xE5; /* Mark the entry "deleted" */ + dj->fs->wflag = 1; + } + } +#endif + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Pick a segment and create the object name in directory form */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT create_name ( + DIR *dj, /* Pointer to the directory object */ + const char **path /* Pointer to pointer to the segment in the path string */ +) +{ +#if _USE_LFN + BYTE c, b, cf, *sfn; + WCHAR w, *lfn; + int i, ni, si, di; + const char *p; + + /* Create LFN in Unicode */ + si = di = 0; + p = *path; + lfn = dj->lfn; + for (;;) { + w = (BYTE)p[si++]; /* Get a character */ + if (w < ' ' || w == '/' || w == '\\') break; /* Break on end of segment */ + if (IsDBCS1(w)) { /* If it is DBC 1st byte */ + c = p[si++]; /* Get 2nd byte */ + if (!IsDBCS2(c)) /* Reject invalid DBC */ + return FR_INVALID_NAME; + w = (w << 8) + c; + } else { + if (StrChr("\"*:<>?|\x7F", w)) /* Reject unallowable chars for LFN */ + return FR_INVALID_NAME; + } + w = ff_convert(w, 1); /* Convert OEM to Unicode, store it */ + if (!w || di >= _MAX_LFN) /* Reject invalid code or too long name */ + return FR_INVALID_NAME; + lfn[di++] = w; + } + *path = &p[si]; /* Rerurn pointer to the next segment */ + cf = (w < ' ') ? 4 : 0; /* Set last segment flag if end of path */ + + while (di) { /* Strip trailing spaces and dots */ + w = lfn[di - 1]; + if (w != ' ' && w != '.') break; + di--; + } + if (!di) return FR_INVALID_NAME; /* Reject null string */ + + lfn[di] = 0; /* LFN is created */ + + /* Create SFN in directory form */ + sfn = dj->fn; + MemSet(sfn, ' ', 11); + for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ; /* Strip leading spaces and dots */ + if (si) cf |= 1; + while (di && lfn[di - 1] != '.') di--; /* Find extension (di<=si: no extension) */ + + b = i = 0; ni = 8; + for (;;) { + w = lfn[si++]; /* Get an LFN char */ + if (w == 0) break; /* Break when enf of the LFN */ + if (w == ' ' || (w == '.' && si != di)) { /* Remove spaces and dots */ + cf |= 1; continue; + } + if (i >= ni || si == di) { /* Here is extension or end of SFN */ + if (ni == 11) { /* Extension is longer than 3 bytes */ + cf |= 1; break; + } + if (si != di) cf |= 1; /* File name is longer than 8 bytes */ + if (si > di) break; /* No extension */ + si = di; i = 8; ni = 11; /* Enter extension section */ + b <<= 2; continue; + } + w = ff_convert(w, 0); /* Unicode -> OEM code */ + if (w >= 0x80) cf |= 0x20; /* If there is any extended char, force create an LFN */ + if (w >= 0x100) { /* Double byte char */ + if (i >= ni - 1) { + cf |= 1; i = ni; continue; + } + sfn[i++] = (BYTE)(w >> 8); + } else { /* Single byte char */ + if (StrChr("+,;[=]", w)) { /* Replace unallowable chars for SFN */ + w = '_'; cf |= 1; + } else { + if (IsUpper(w)) { /* Large capital */ + b |= 2; + } else { + if (IsLower(w)) { /* Small capital */ + b |= 1; w -= 0x20; + } + } + } + } + sfn[i++] = (BYTE)w; + } + if (sfn[0] == 0xE5) sfn[0] = 0x05; /* When first char collides with 0xE5, replace it with 0x05 */ + + if (ni == 8) b <<= 2; + if ((cf & 0x21) == 0) { /* When LFN is in 8.3 format without extended char, NT flags are created */ + if ((b & 0x03) == 0x01) cf |= 0x10; /* NT flag (Extension has only small capital) */ + if ((b & 0x0C) == 0x04) cf |= 0x08; /* NT flag (Filename has only small capital) */ + if ((b & 0x0C) != 0x0C && (b & 0x03) != 0x03) cf |= 2; /* Eliminate LFN when non composite capitals */ + } + + sfn[11] = cf; /* SFN is created */ + +#else + BYTE c, d, b, *sfn; + int ni, si, i; + const char *p; + + /* Create file name in directory form */ + sfn = dj->fn; + MemSet(sfn, ' ', 11); + si = i = b = 0; ni = 8; + p = *path; + for (;;) { + c = p[si++]; + if (c < ' ' || c == '/' || c == '\\') break; /* Break on end of segment */ + if (c == '.' || i >= ni) { + if (ni != 8 || c != '.') return FR_INVALID_NAME; + i = 8; ni = 11; + b <<= 2; continue; + } + if (c >= 0x80) b |= 3; /* If there is any extended char, eliminate NT flag */ + if (IsDBCS1(c)) { /* If it is DBC 1st byte */ + d = p[si++]; /* Get 2nd byte */ + if (!IsDBCS2(d) || i >= ni - 1) /* Reject invalid DBC */ + return FR_INVALID_NAME; + sfn[i++] = c; + sfn[i++] = d; + } else { + if (StrChr(" +,;[=]\"*:<>?|\x7F", c)) /* Reject unallowable chrs for SFN */ + return FR_INVALID_NAME; + if (IsUpper(c)) { + b |= 2; + } else { + if (IsLower(c)) { + b |= 1; c -= 0x20; + } + } + sfn[i++] = c; + } + } + *path = &p[si]; /* Rerurn pointer to the next segment */ + c = (c < ' ') ? 4 : 0; /* Set last segment flag if end of path */ + + if (!i) return FR_INVALID_NAME; /* Reject null string */ + if (sfn[0] == 0xE5) sfn[0] = 0x05; /* When first char collides with 0xE5, replace it with 0x05 */ + + if (ni == 8) b <<= 2; + if ((b & 0x03) == 0x01) c |= 0x10; /* NT flag (Extension has only small capital) */ + if ((b & 0x0C) == 0x04) c |= 0x08; /* NT flag (Filename has only small capital) */ + + sfn[11] = c; /* Store NT flag, File name is created */ +#endif + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Get file information from directory entry */ +/*-----------------------------------------------------------------------*/ +#if _FS_MINIMIZE <= 1 +static +void get_fileinfo ( /* No return code */ + DIR *dj, /* Pointer to the directory object */ + FILINFO *fno /* Pointer to store the file information */ +) +{ + int i; + BYTE c, nt, *dir; 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]; + p = fno->fname; + if (dj->sect) { + dir = dj->dir; + nt = dir[DIR_NTres]; /* NT flag */ + for (i = 0; i < 8; i++) { /* Copy file name body */ + c = dir[i]; if (c == ' ') break; - if (a & 0x10 && c >= 'A' && c <= 'Z') c += 0x20; + if (c == 0x05) c = 0xE5; + if ((nt & 0x08) && IsUpper(c)) c += 0x20; *p++ = c; } + if (dir[8] != ' ') { /* Copy file name extension */ + *p++ = '.'; + for (i = 8; i < 11; i++) { + c = dir[i]; + if (c == ' ') break; + if ((nt & 0x10) && IsUpper(c)) c += 0x20; + *p++ = c; + } + } + fno->fattrib = dir[DIR_Attr]; /* Attribute */ + fno->fsize = LD_DWORD(dir+DIR_FileSize); /* Size */ + fno->fdate = LD_WORD(dir+DIR_WrtDate); /* Date */ + fno->ftime = LD_WORD(dir+DIR_WrtTime); /* Time */ } - *p = '\0'; + *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 */ +#if _USE_LFN + p = fno->lfname; + if (p) { + WCHAR wchr, *lfn; + + i = 0; + if (dj->sect && dj->lfn_idx != 0xFFFF) {/* Get LFN if available */ + lfn = dj->lfn; + while ((wchr = *lfn++) != 0) { /* Get an LFN char */ + wchr = ff_convert(wchr, 0); /* Unicode -> OEM code */ + if (!wchr) { i = 0; break; } /* Conversion error, no LFN */ + if (_DF1S && wchr >= 0x100) /* Put 1st byte if it is a DBC */ + p[i++] = (char)(wchr >> 8); + p[i++] = (char)wchr; + if (i >= fno->lfsize) { i = 0; break; } /* Buffer overrun, no LFN */ + } + } + p[i] = 0; /* Terminator */ + } +#endif } #endif /* _FS_MINIMIZE <= 1 */ @@ -430,204 +1265,74 @@ void get_fileinfo ( /* No return code */ /*-----------------------------------------------------------------------*/ -/* Pick a paragraph and create the name in format of directory entry */ +/* Follow a file path */ /*-----------------------------------------------------------------------*/ static -char 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)} */ +FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ + DIR *dj, /* Directory object to return last directory and found object */ + const char *path /* Full-path string to find a file or directory */ ) { - BYTE n, t, c, a, b; + FRESULT res; + BYTE *dir, stat; - 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 ^= 0x01; 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 &= 0xF7) : (b &= 0xEF); - if (c >= 'a' && c <= 'z') { /* Convert to upper case */ - c -= 0x20; - if (_USE_NTFLAG) (t == 8) ? (a |= 0x08) : (a |= 0x10); - } - } - md_l1: - a &= 0xFE; - md_l2: - if (n >= t) break; - dirname[n++] = c; - } - return 1; -} + if (*path == '/' || *path == '\\' ) path++; /* Strip heading separator */ + dj->sclust = /* Set start directory (root dir) */ + (dj->fs->fs_type == FS_FAT32) ? dj->fs->dirbase : 0; + if ((BYTE)*path < ' ') { /* Null path means the root directory */ + res = dir_seek(dj, 0); + dj->dir = NULL; - -/*-----------------------------------------------------------------------*/ -/* Trace a file path */ -/*-----------------------------------------------------------------------*/ - -static -FRESULT trace_path ( /* FR_OK(0): successful, !=0: error code */ - DIR *dj, /* Pointer to directory object to return last directory */ - char *fn, /* Pointer to last segment name to return {file(8),ext(3),attr(1)} */ - const char *path, /* Full-path string to trace a file or directory */ - BYTE **dir /* Pointer to pointer to found entry to retutn */ -) -{ - DWORD clust; - char ds; - BYTE *dptr = NULL; - FATFS *fs = dj->fs; - - - /* Initialize directory object */ - clust = fs->dirbase; - if (fs->fs_type == FS_FAT32) { - dj->clust = dj->sclust = clust; - dj->sect = clust2sect(fs, clust); - } else { - dj->clust = dj->sclust = 0; - dj->sect = clust; - } - dj->index = 0; - - 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; + } else { /* Follow path */ for (;;) { - if (!move_window(fs, dj->sect)) return FR_RW_ERROR; - dptr = &fs->win[(dj->index & ((SS(fs) - 1) / 32)) * 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(dj)) /* Next directory pointer */ - return !ds ? FR_NO_FILE : FR_NO_PATH; + res = dir_seek(dj, 0); /* Rewind directory object */ + if (res != FR_OK) break; + res = create_name(dj, &path); /* Get a segment */ + if (res != FR_OK) break; + res = dir_find(dj); /* Find it */ + stat = *(dj->fn+11); + if (res != FR_OK) { /* Could not find the object */ + if (res == FR_NO_FILE && !(stat & 4)) + res = FR_NO_PATH; + break; + } + if (stat & 4) break; /* Last segment match. Function completed. */ + dir = dj->dir; /* There is next segment. Follow the sub directory */ + if (!(dir[DIR_Attr] & AM_DIR)) { /* Cannot follow because it is a file */ + res = FR_NO_PATH; break; + } + dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); } - 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 = ((DWORD)LD_WORD(&dptr[DIR_FstClusHI]) << 16) | LD_WORD(&dptr[DIR_FstClusLO]); /* Get cluster# of the directory */ - dj->clust = dj->sclust = clust; /* Restart scanning at the new directory */ - dj->sect = clust2sect(fs, clust); - dj->index = 2; } + + return res; } -/*-----------------------------------------------------------------------*/ -/* Reserve a directory entry */ -/*-----------------------------------------------------------------------*/ - -#if !_FS_READONLY -static -FRESULT reserve_direntry ( /* FR_OK: successful, FR_DENIED: no free entry, FR_RW_ERROR: a disk error occured */ - DIR *dj, /* Target directory to create new entry */ - BYTE **dir /* Pointer to pointer to created entry to retutn */ -) -{ - DWORD clust, sector; - BYTE c, n, *dptr; - FATFS *fs = dj->fs; - - - /* Re-initialize directory object */ - clust = dj->sclust; - if (clust != 0) { /* Dyanmic directory table */ - dj->clust = clust; - dj->sect = clust2sect(fs, clust); - } else { /* Static directory table */ - dj->sect = fs->dirbase; - } - dj->index = 0; - - do { - if (!move_window(fs, dj->sect)) return FR_RW_ERROR; - dptr = &fs->win[(dj->index & ((SS(dj->fs) - 1) / 32)) * 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(dj)); /* Next directory pointer */ - /* Reached to end of the directory table */ - - /* Abort when it is a static table or could not stretch dynamic table */ - if (clust == 0 || !(clust = create_chain(fs, dj->clust))) return FR_DENIED; - if (clust == 1 || !move_window(fs, 0)) return FR_RW_ERROR; - - /* Cleanup the expanded table */ - fs->winsect = sector = clust2sect(fs, clust); - memset(fs->win, 0, SS(fs)); - for (n = fs->csize; n; n--) { - if (disk_write(fs->drive, 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 an FAT boot record */ /*-----------------------------------------------------------------------*/ static -BYTE check_fs ( /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record or error */ +BYTE check_fs ( /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record, 3:Error */ FATFS *fs, /* File system object */ DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */ ) { if (disk_read(fs->drive, fs->win, sect, 1) != RES_OK) /* Load boot record */ - return 2; + return 3; if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55) /* Check record signature (always placed at offset 510 even if the sector size is >512) */ return 2; - if (!memcmp(&fs->win[BS_FilSysType], "FAT", 3)) /* Check FAT signature */ + if (!MemCmp(&fs->win[BS_FilSysType], "FAT", 3)) /* Check FAT signature */ return 0; - if (!memcmp(&fs->win[BS_FilSysType32], "FAT32", 5) && !(fs->win[BPB_ExtFlags] & 0x80)) + if (!MemCmp(&fs->win[BS_FilSysType32], "FAT32", 5) && !(fs->win[BPB_ExtFlags] & 0x80)) return 0; return 1; @@ -647,28 +1352,30 @@ FRESULT auto_mount ( /* FR_OK(0): successful, !=0: any error occured */ BYTE chk_wp /* !=0: Check media write protection for write access */ ) { + FRESULT res; BYTE drv, fmt, *tbl; DSTATUS stat; - DWORD bootsect, fatsize, totalsect, maxclust; + DWORD bsect, fsize, tsect, mclst; const char *p = *path; FATFS *fs; /* Get drive number from the path name */ - while (*p == ' ') p++; /* Strip leading spaces */ drv = p[0] - '0'; /* Is there a drive number? */ - if (drv <= 9 && p[1] == ':') - p += 2; /* Found a drive number, get and strip it */ - else - drv = 0; /* No drive number is given, use drive number 0 as default */ - if (*p == '/') p++; /* Strip heading slash */ - *path = p; /* Return pointer to the path name */ + if (drv <= 9 && p[1] == ':') { + p += 2; /* Found a drive number, get and strip it */ + *path = p; /* Return pointer to the path name */ + } else { + drv = 0; /* No drive number is given, use drive number 0 as default */ + } /* Check if the drive number is valid or not */ if (drv >= _DRIVES) return FR_INVALID_DRIVE; /* Is the drive number valid? */ *rfs = fs = FatFs[drv]; /* Returen pointer to the corresponding file system object */ if (!fs) return FR_NOT_ENABLED; /* Is the file system object registered? */ + ENTER_FF(fs); /* Lock file system */ + if (fs->fs_type) { /* If the logical drive has been mounted */ stat = disk_status(fs->drive); if (!(stat & STA_NOINIT)) { /* and physical drive is kept initialized (has not been changed), */ @@ -682,7 +1389,7 @@ FRESULT auto_mount ( /* FR_OK(0): successful, !=0: any error occured */ /* The logical drive must be re-mounted. Following code attempts to mount the logical drive */ - memset(fs, 0, sizeof(FATFS)); /* Clean-up the file system object */ + fs->fs_type = 0; /* Clear the file system object */ fs->drive = LD2PD(drv); /* Bind the logical drive and a physical drive */ stat = disk_initialize(fs->drive); /* Initialize low level disk I/O layer */ if (stat & STA_NOINIT) /* Check if the drive is ready */ @@ -696,64 +1403,67 @@ FRESULT auto_mount ( /* FR_OK(0): successful, !=0: any error occured */ return FR_WRITE_PROTECTED; #endif /* Search FAT partition on the drive */ - fmt = check_fs(fs, bootsect = 0); /* Check sector 0 as an SFD format */ + fmt = check_fs(fs, bsect = 0); /* Check sector 0 as an SFD format */ if (fmt == 1) { /* Not an FAT boot record, it may be patitioned */ /* Check a partition listed in top of the partition table */ tbl = &fs->win[MBR_Table + LD2PT(drv) * 16]; /* Partition table */ if (tbl[4]) { /* Is the partition existing? */ - bootsect = LD_DWORD(&tbl[8]); /* Partition offset in LBA */ - fmt = check_fs(fs, bootsect); /* Check the partition */ + bsect = LD_DWORD(&tbl[8]); /* Partition offset in LBA */ + fmt = check_fs(fs, bsect); /* Check the partition */ } } - if (fmt || LD_WORD(&fs->win[BPB_BytsPerSec]) != SS(fs)) /* No valid FAT patition is found */ + if (fmt == 3) return FR_DISK_ERR; + if (fmt || LD_WORD(fs->win+BPB_BytsPerSec) != SS(fs)) /* 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 = fatsize; + fsize = LD_WORD(fs->win+BPB_FATSz16); /* Number of sectors per FAT */ + if (!fsize) fsize = LD_DWORD(fs->win+BPB_FATSz32); + fs->sects_fat = fsize; 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) */ + fsize *= fs->n_fats; /* (Number of sectors in FAT area) */ + fs->fatbase = bsect + LD_WORD(fs->win+BPB_RsvdSecCnt); /* FAT start sector (lba) */ fs->csize = 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 /* max_clust = Last cluster# + 1 */ - - LD_WORD(&fs->win[BPB_RsvdSecCnt]) - fatsize - fs->n_rootdir / (SS(fs)/32) + fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt); /* Nmuber of root directory entries */ + tsect = LD_WORD(fs->win+BPB_TotSec16); /* Number of sectors on the file system */ + if (!tsect) tsect = LD_DWORD(fs->win+BPB_TotSec32); + fs->max_clust = mclst = (tsect /* Last cluster# + 1 */ + - LD_WORD(fs->win+BPB_RsvdSecCnt) - fsize - fs->n_rootdir / (SS(fs)/32) ) / fs->csize + 2; fmt = FS_FAT12; /* Determine the FAT sub type */ - if (maxclust >= 0xFF7) fmt = FS_FAT16; - if (maxclust >= 0xFFF7) fmt = FS_FAT32; + if (mclst >= 0xFF7) fmt = FS_FAT16; /* Number of clusters >= 0xFF5 */ + if (mclst >= 0xFFF7) fmt = FS_FAT32; /* Number of clusters >= 0xFFF5 */ if (fmt == FS_FAT32) - fs->dirbase = LD_DWORD(&fs->win[BPB_RootClus]); /* Root directory start cluster */ + fs->dirbase = LD_DWORD(fs->win+BPB_RootClus); /* Root directory start cluster */ else - fs->dirbase = fs->fatbase + fatsize; /* Root directory start sector (lba) */ - fs->database = fs->fatbase + fatsize + fs->n_rootdir / (SS(fs)/32); /* Data start sector (lba) */ + fs->dirbase = fs->fatbase + fsize; /* Root directory start sector (lba) */ + fs->database = fs->fatbase + fsize + fs->n_rootdir / (SS(fs)/32); /* Data start sector (lba) */ #if !_FS_READONLY /* Initialize allocation information */ fs->free_clust = 0xFFFFFFFF; -#if _USE_FSINFO + fs->wflag = 0; /* Get fsinfo if needed */ if (fmt == FS_FAT32) { - fs->fsi_sector = bootsect + LD_WORD(&fs->win[BPB_FSInfo]); + fs->fsi_sector = bsect + LD_WORD(fs->win+BPB_FSInfo); + fs->fsi_flag = 0; if (disk_read(fs->drive, 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]); + 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 -#endif - + fs->winsect = 0; fs->fs_type = fmt; /* FAT syb-type */ - fs->id = ++fsid; /* File system mount ID */ - return FR_OK; + fs->id = ++Fsid; /* File system mount ID */ + res = FR_OK; + + return res; } @@ -764,13 +1474,16 @@ FRESULT auto_mount ( /* FR_OK(0): successful, !=0: any error occured */ /*-----------------------------------------------------------------------*/ static -FRESULT validate ( /* FR_OK(0): The object is valid, !=0: Invalid */ - const FATFS *fs, /* Pointer to the file system object */ - WORD id /* Member id of the target object to be checked */ +FRESULT validate ( /* FR_OK(0): The object is valid, !=0: Invalid */ + FATFS *fs, /* Pointer to the file system object */ + WORD id /* Member id of the target object to be checked */ ) { if (!fs || !fs->fs_type || fs->id != id) return FR_INVALID_OBJECT; + + ENTER_FF(fs); /* Lock file system */ + if (disk_status(fs->drive) & STA_NOINIT) return FR_NOT_READY; @@ -797,12 +1510,28 @@ FRESULT f_mount ( FATFS *fs /* Pointer to new file system object (NULL for unmount)*/ ) { - if (drv >= _DRIVES) return FR_INVALID_DRIVE; + FATFS *rfs; - if (FatFs[drv]) FatFs[drv]->fs_type = 0; /* Clear old object */ - FatFs[drv] = fs; /* Register and clear new object */ - if (fs) fs->fs_type = 0; + if (drv >= _DRIVES) + return FR_INVALID_DRIVE; + + rfs = FatFs[drv]; + + if (rfs) { +#if _FS_REENTRANT /* Discard mutex of the current fs. (Platform dependent) */ + CloseHandle(rfs->h_mutex); /* Discard mutex */ +#endif + rfs->fs_type = 0; /* Clear old fs object */ + } + + if (fs) { + fs->fs_type = 0; /* Clear new fs object */ +#if _FS_REENTRANT /* Create mutex for the new fs. (Platform dependent) */ + fs->h_mutex = CreateMutex(NULL, FALSE, NULL); +#endif + } + FatFs[drv] = fs; /* Register new fs object */ return FR_OK; } @@ -822,81 +1551,87 @@ FRESULT f_open ( { FRESULT res; DIR dj; + NAMEBUF(sfn, lfn); BYTE *dir; - char fn[8+3+1]; fp->fs = NULL; /* Clear file object */ #if !_FS_READONLY - mode &= (FA_READ|FA_WRITE|FA_CREATE_ALWAYS|FA_OPEN_ALWAYS|FA_CREATE_NEW); - res = auto_mount(&path, &dj.fs, (BYTE)(mode & (FA_WRITE|FA_CREATE_ALWAYS|FA_OPEN_ALWAYS|FA_CREATE_NEW))); + mode &= (FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW); + res = auto_mount(&path, &dj.fs, (BYTE)(mode & (FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW))); #else mode &= FA_READ; res = auto_mount(&path, &dj.fs, 0); #endif - if (res != FR_OK) return res; - res = trace_path(&dj, fn, path, &dir); /* Trace the file path */ + if (res != FR_OK) LEAVE_FF(dj.fs, res); + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ #if !_FS_READONLY /* Create or Open a file */ - if (mode & (FA_CREATE_ALWAYS|FA_OPEN_ALWAYS|FA_CREATE_NEW)) { - DWORD ps, rs; + if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) { + DWORD ps, cl; + if (res != FR_OK) { /* No file, create new */ - if (res != FR_NO_FILE) return res; - res = reserve_direntry(&dj, &dir); - if (res != FR_OK) return res; - memset(dir, 0, 32); /* Initialize the new entry with open name */ - memcpy(&dir[DIR_Name], fn, 8+3); - dir[DIR_NTres] = fn[11]; + if (res == FR_NO_FILE) + res = dir_register(&dj); + if (res != FR_OK) LEAVE_FF(dj.fs, res); mode |= FA_CREATE_ALWAYS; + dir = dj.dir; } else { /* Any object is already existing */ if (mode & FA_CREATE_NEW) /* Cannot create new */ - return FR_EXIST; - if (!dir || (dir[DIR_Attr] & (AM_RDO|AM_DIR))) /* Cannot overwrite it (R/O or DIR) */ - return FR_DENIED; + LEAVE_FF(dj.fs, FR_EXIST); + dir = dj.dir; + if (!dir || (dir[DIR_Attr] & (AM_RDO | AM_DIR))) /* Cannot overwrite it (R/O or DIR) */ + LEAVE_FF(dj.fs, FR_DENIED); if (mode & FA_CREATE_ALWAYS) { /* Resize it to zero if needed */ - rs = ((DWORD)LD_WORD(&dir[DIR_FstClusHI]) << 16) | LD_WORD(&dir[DIR_FstClusLO]); /* Get start cluster */ - ST_WORD(&dir[DIR_FstClusHI], 0); /* cluster = 0 */ - ST_WORD(&dir[DIR_FstClusLO], 0); - ST_DWORD(&dir[DIR_FileSize], 0); /* size = 0 */ - dj.fs->winflag = 1; + cl = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); /* Get start cluster */ + ST_WORD(dir+DIR_FstClusHI, 0); /* cluster = 0 */ + ST_WORD(dir+DIR_FstClusLO, 0); + ST_DWORD(dir+DIR_FileSize, 0); /* size = 0 */ + dj.fs->wflag = 1; ps = dj.fs->winsect; /* Remove the cluster chain */ - if (!remove_chain(dj.fs, rs) || !move_window(dj.fs, ps)) - return FR_RW_ERROR; - dj.fs->last_clust = rs - 1; /* Reuse the cluster hole */ + if (cl) { + res = remove_chain(dj.fs, cl); + if (res) LEAVE_FF(dj.fs, res); + dj.fs->last_clust = cl - 1; /* Reuse the cluster hole */ + } + res = move_window(dj.fs, ps); + if (res != FR_OK) LEAVE_FF(dj.fs, res); } } if (mode & FA_CREATE_ALWAYS) { dir[DIR_Attr] = 0; /* Reset attribute */ ps = get_fattime(); - ST_DWORD(&dir[DIR_CrtTime], ps); /* Created time */ - dj.fs->winflag = 1; + ST_DWORD(dir+DIR_CrtTime, ps); /* Created time */ + dj.fs->wflag = 1; mode |= FA__WRITTEN; /* Set file changed flag */ } } /* Open an existing file */ else { #endif /* !_FS_READONLY */ - if (res != FR_OK) return res; /* Trace failed */ + if (res != FR_OK) LEAVE_FF(dj.fs, res); /* Follow failed */ + dir = dj.dir; if (!dir || (dir[DIR_Attr] & AM_DIR)) /* It is a directory */ - return FR_NO_FILE; + LEAVE_FF(dj.fs, FR_NO_FILE); #if !_FS_READONLY if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */ - return FR_DENIED; + LEAVE_FF(dj.fs, FR_DENIED); } fp->dir_sect = dj.fs->winsect; /* Pointer to the directory entry */ - fp->dir_ptr = dir; + fp->dir_ptr = dj.dir; #endif fp->flag = mode; /* File access mode */ fp->org_clust = /* File start cluster */ - ((DWORD)LD_WORD(&dir[DIR_FstClusHI]) << 16) | LD_WORD(&dir[DIR_FstClusLO]); - fp->fsize = LD_DWORD(&dir[DIR_FileSize]); /* File size */ + ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); + fp->fsize = LD_DWORD(dir+DIR_FileSize); /* File size */ fp->fptr = 0; fp->csect = 255; /* File pointer */ - fp->curr_sect = 0; + fp->dsect = 0; fp->fs = dj.fs; fp->id = dj.fs->id; /* Owner file system object of the file */ - return FR_OK; + LEAVE_FF(dj.fs, FR_OK); } @@ -914,16 +1649,19 @@ FRESULT f_read ( ) { FRESULT res; - DWORD clust, sect, remain; + DWORD clst, sect, remain; UINT rcnt, cc; BYTE *rbuff = buff; *br = 0; + res = validate(fp->fs, fp->id); /* Check validity of the object */ - if (res != FR_OK) 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 */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check abort flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (!(fp->flag & FA_READ)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); remain = fp->fsize - fp->fptr; if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ @@ -931,47 +1669,55 @@ FRESULT f_read ( rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) { if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ if (fp->csect >= fp->fs->csize) { /* On the cluster boundary? */ - clust = (fp->fptr == 0) ? /* On the top of the file? */ + clst = (fp->fptr == 0) ? /* On the top of the file? */ fp->org_clust : get_cluster(fp->fs, fp->curr_clust); - if (clust < 2 || clust >= fp->fs->max_clust) goto fr_error; - fp->curr_clust = clust; /* Update current cluster */ - fp->csect = 0; /* Reset sector address in the cluster */ + if (clst <= 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->curr_clust = clst; /* Update current cluster */ + fp->csect = 0; /* Reset sector offset in the cluster */ } - sect = clust2sect(fp->fs, fp->curr_clust) + fp->csect; /* Get current sector */ + sect = clust2sect(fp->fs, fp->curr_clust); /* Get current sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += fp->csect; cc = btr / SS(fp->fs); /* When remaining bytes >= sector size, */ if (cc) { /* Read maximum contiguous sectors directly */ if (fp->csect + cc > fp->fs->csize) /* Clip at cluster boundary */ cc = fp->fs->csize - fp->csect; if (disk_read(fp->fs->drive, rbuff, sect, (BYTE)cc) != RES_OK) - goto fr_error; + ABORT(fp->fs, FR_DISK_ERR); fp->csect += (BYTE)cc; /* Next sector address in the cluster */ rcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ continue; } - if (sect != fp->curr_sect) { /* Is window offset changed? */ +#if !_FS_TINY #if !_FS_READONLY - if (fp->flag & FA__DIRTY) { /* Write back file I/O buffer if needed */ - if (disk_write(fp->fs->drive, fp->buffer, fp->curr_sect, 1) != RES_OK) - goto fr_error; - fp->flag &= (BYTE)~FA__DIRTY; - } -#endif - if (disk_read(fp->fs->drive, fp->buffer, sect, 1) != RES_OK) /* Fill file I/O buffer with file data */ - goto fr_error; - fp->curr_sect = sect; + if (fp->flag & FA__DIRTY) { /* Write sector I/O buffer if needed */ + if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA__DIRTY; } +#endif + if (fp->dsect != sect) { /* Fill sector buffer with file data */ + if (disk_read(fp->fs->drive, fp->buf, sect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + } +#endif + fp->dsect = sect; fp->csect++; /* Next sector address in the cluster */ } - rcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs)); /* Get partial sector from file I/O buffer */ + rcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs)); /* Get partial sector data from sector buffer */ if (rcnt > btr) rcnt = btr; - memcpy(rbuff, &fp->buffer[fp->fptr % SS(fp->fs)], rcnt); +#if _FS_TINY + if (move_window(fp->fs, fp->dsect)) /* Move sector window */ + ABORT(fp->fs, FR_DISK_ERR); + MemCpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ +#else + MemCpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ +#endif } - return FR_OK; -fr_error: /* Abort this file due to an unrecoverable error */ - fp->flag |= FA__ERROR; - return FR_RW_ERROR; + LEAVE_FF(fp->fs, FR_OK); } @@ -990,78 +1736,100 @@ FRESULT f_write ( ) { FRESULT res; - DWORD clust, sect; + DWORD clst, sect; UINT wcnt, cc; const BYTE *wbuff = buff; *bw = 0; + res = validate(fp->fs, fp->id); /* Check validity of the object */ - if (res != FR_OK) 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 */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check abort flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (!(fp->flag & FA_WRITE)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + if (fp->fsize + btw < fp->fsize) btw = 0; /* File size cannot reach 4GB */ for ( ; btw; /* Repeat until all data transferred */ wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) { if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ if (fp->csect >= fp->fs->csize) { /* On the cluster boundary? */ if (fp->fptr == 0) { /* On the top of the file? */ - clust = fp->org_clust; /* Follow from the origin */ - if (clust == 0) /* When there is no cluster chain, */ - fp->org_clust = clust = create_chain(fp->fs, 0); /* Create a new cluster chain */ + clst = fp->org_clust; /* Follow from the origin */ + if (clst == 0) /* When there is no cluster chain, */ + fp->org_clust = clst = create_chain(fp->fs, 0); /* Create a new cluster chain */ } else { /* Middle or end of the file */ - clust = create_chain(fp->fs, fp->curr_clust); /* Trace or streach cluster chain */ + clst = create_chain(fp->fs, fp->curr_clust); /* Follow or streach cluster chain */ } - if (clust == 0) break; /* Could not allocate a new cluster (disk full) */ - if (clust == 1 || clust >= fp->fs->max_clust) goto fw_error; - fp->curr_clust = clust; /* Update current cluster */ + if (clst == 0) break; /* Could not allocate a new cluster (disk full) */ + if (clst == 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->curr_clust = clst; /* Update current cluster */ fp->csect = 0; /* Reset sector address in the cluster */ } - sect = clust2sect(fp->fs, fp->curr_clust) + fp->csect; /* Get current sector */ +#if _FS_TINY + if (fp->fs->winsect == fp->dsect && move_window(fp->fs, 0)) /* Write back data buffer prior to following direct transfer */ + ABORT(fp->fs, FR_DISK_ERR); +#else + if (fp->flag & FA__DIRTY) { /* Write back data buffer prior to following direct transfer */ + if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA__DIRTY; + } +#endif + sect = clust2sect(fp->fs, fp->curr_clust); /* Get current sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += fp->csect; cc = btw / SS(fp->fs); /* When remaining bytes >= sector size, */ if (cc) { /* Write maximum contiguous sectors directly */ if (fp->csect + cc > fp->fs->csize) /* Clip at cluster boundary */ cc = fp->fs->csize - fp->csect; if (disk_write(fp->fs->drive, wbuff, sect, (BYTE)cc) != RES_OK) - goto fw_error; + ABORT(fp->fs, FR_DISK_ERR); fp->csect += (BYTE)cc; /* Next sector address in the cluster */ wcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ continue; } - if (sect != fp->curr_sect) { /* Is window offset changed? */ - if (fp->flag & FA__DIRTY) { /* Write back file I/O buffer if needed */ - if (disk_write(fp->fs->drive, fp->buffer, fp->curr_sect, 1) != RES_OK) - goto fw_error; - fp->flag &= (BYTE)~FA__DIRTY; - } - if (fp->fptr < fp->fsize && /* Fill file I/O buffer with file data */ - disk_read(fp->fs->drive, fp->buffer, sect, 1) != RES_OK) - goto fw_error; - fp->curr_sect = sect; +#if _FS_TINY + if (fp->fptr >= fp->fsize) { /* Avoid silly buffer filling at growing edge */ + if (move_window(fp->fs, 0)) ABORT(fp->fs, FR_DISK_ERR); + fp->fs->winsect = sect; } +#else + if (fp->dsect != sect) { /* Fill sector buffer with file data */ + if (fp->fptr < fp->fsize && + disk_read(fp->fs->drive, fp->buf, sect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + } +#endif + fp->dsect = sect; fp->csect++; /* Next sector address in the cluster */ } wcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs)); /* Put partial sector into file I/O buffer */ if (wcnt > btw) wcnt = btw; - memcpy(&fp->buffer[fp->fptr % SS(fp->fs)], wbuff, wcnt); +#if _FS_TINY + if (move_window(fp->fs, fp->dsect)) /* Move sector window */ + ABORT(fp->fs, FR_DISK_ERR); + MemCpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ + fp->fs->wflag = 1; +#else + MemCpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ fp->flag |= FA__DIRTY; +#endif } 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 file due to an unrecoverable error */ - fp->flag |= FA__ERROR; - return FR_RW_ERROR; + LEAVE_FF(fp->fs, FR_OK); } /*-----------------------------------------------------------------------*/ -/* Synchronize the file object */ +/* Synchronize the File Object */ /*-----------------------------------------------------------------------*/ FRESULT f_sync ( @@ -1076,27 +1844,31 @@ FRESULT f_sync ( res = validate(fp->fs, fp->id); /* Check validity of the object */ if (res == FR_OK) { if (fp->flag & FA__WRITTEN) { /* Has the file been written? */ - /* Write back data buffer if needed */ +#if !_FS_TINY /* Write-back dirty buffer */ if (fp->flag & FA__DIRTY) { - if (disk_write(fp->fs->drive, fp->buffer, fp->curr_sect, 1) != RES_OK) - return FR_RW_ERROR; + if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK) + LEAVE_FF(fp->fs, FR_DISK_ERR); fp->flag &= (BYTE)~FA__DIRTY; } +#endif /* Update the directory entry */ - if (!move_window(fp->fs, 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 */ - ST_WORD(&dir[DIR_FstClusHI], fp->org_clust >> 16); - tim = get_fattime(); /* Updated time */ - ST_DWORD(&dir[DIR_WrtTime], tim); - fp->flag &= (BYTE)~FA__WRITTEN; - res = sync(fp->fs); + res = move_window(fp->fs, fp->dir_sect); + if (res == FR_OK) { + 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 */ + ST_WORD(dir+DIR_FstClusHI, fp->org_clust >> 16); + tim = get_fattime(); /* Updated time */ + ST_DWORD(dir+DIR_WrtTime, tim); + fp->flag &= (BYTE)~FA__WRITTEN; + fp->fs->wflag = 1; + res = sync(fp->fs); + } } } - return res; + + LEAVE_FF(fp->fs, res); } #endif /* !_FS_READONLY */ @@ -1115,13 +1887,15 @@ FRESULT f_close ( FRESULT res; -#if !_FS_READONLY - res = f_sync(fp); -#else +#if _FS_READONLY res = validate(fp->fs, fp->id); -#endif + if (res == FR_OK) fp->fs = NULL; + LEAVE_FF(fp->fs, res); +#else + res = f_sync(fp); if (res == FR_OK) fp->fs = NULL; return res; +#endif } @@ -1138,12 +1912,13 @@ FRESULT f_lseek ( ) { FRESULT res; - DWORD clust, csize, nsect, ifptr; + DWORD clst, bcs, nsect, ifptr; res = validate(fp->fs, fp->id); /* Check validity of the object */ - if (res != FR_OK) return res; - if (fp->flag & FA__ERROR) return FR_RW_ERROR; + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check abort flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); if (ofs > fp->fsize /* In read-only mode, clip offset with the file size */ #if !_FS_READONLY && !(fp->flag & FA_WRITE) @@ -1154,72 +1929,73 @@ FRESULT f_lseek ( fp->fptr = 0; fp->csect = 255; nsect = 0; if (ofs > 0) { - csize = (DWORD)fp->fs->csize * SS(fp->fs); /* Cluster size (byte) */ + bcs = (DWORD)fp->fs->csize * SS(fp->fs); /* Cluster size (byte) */ if (ifptr > 0 && - (ofs - 1) / csize >= (ifptr - 1) / csize) {/* When seek to same or following cluster, */ - fp->fptr = (ifptr - 1) & ~(csize - 1); /* start from the current cluster */ + (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */ + fp->fptr = (ifptr - 1) & ~(bcs - 1); /* start from the current cluster */ ofs -= fp->fptr; - clust = fp->curr_clust; + clst = fp->curr_clust; } else { /* When seek to back cluster, */ - clust = fp->org_clust; /* start from the first cluster */ + clst = fp->org_clust; /* start from the first cluster */ #if !_FS_READONLY - if (clust == 0) { /* If no cluster chain, create a new chain */ - clust = create_chain(fp->fs, 0); - if (clust == 1) goto fk_error; - fp->org_clust = clust; + if (clst == 0) { /* If no cluster chain, create a new chain */ + clst = create_chain(fp->fs, 0); + if (clst == 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->org_clust = clst; } #endif - fp->curr_clust = clust; + fp->curr_clust = clst; } - if (clust != 0) { - while (ofs > csize) { /* Cluster following loop */ + if (clst != 0) { + while (ofs > bcs) { /* Cluster following loop */ #if !_FS_READONLY if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ - clust = create_chain(fp->fs, clust); /* Force streached if in write mode */ - if (clust == 0) { /* When disk gets full, clip file size */ - ofs = csize; break; + clst = create_chain(fp->fs, clst); /* Force streached if in write mode */ + if (clst == 0) { /* When disk gets full, clip file size */ + ofs = bcs; break; } } else #endif - clust = get_cluster(fp->fs, clust); /* Follow cluster chain if not in write mode */ - if (clust < 2 || clust >= fp->fs->max_clust) goto fk_error; - fp->curr_clust = clust; - fp->fptr += csize; - ofs -= csize; + clst = get_cluster(fp->fs, clst); /* Follow cluster chain if not in write mode */ + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + if (clst <= 1 || clst >= fp->fs->max_clust) ABORT(fp->fs, FR_INT_ERR); + fp->curr_clust = clst; + fp->fptr += bcs; + ofs -= bcs; } fp->fptr += ofs; fp->csect = (BYTE)(ofs / SS(fp->fs)); /* Sector offset in the cluster */ - if (ofs & (SS(fp->fs) - 1)) { - nsect = clust2sect(fp->fs, clust) + fp->csect; /* Current sector */ + if (ofs % SS(fp->fs)) { + nsect = clust2sect(fp->fs, clst); /* Current sector */ + if (!nsect) ABORT(fp->fs, FR_INT_ERR); + nsect += fp->csect; fp->csect++; } } } - if (nsect && nsect != fp->curr_sect) { + if (nsect && nsect != fp->dsect && fp->fptr % SS(fp->fs)) { +#if !_FS_TINY #if !_FS_READONLY if (fp->flag & FA__DIRTY) { /* Write-back dirty buffer if needed */ - if (disk_write(fp->fs->drive, fp->buffer, fp->curr_sect, 1) != RES_OK) - goto fk_error; + if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); fp->flag &= (BYTE)~FA__DIRTY; } #endif - if (disk_read(fp->fs->drive, fp->buffer, nsect, 1) != RES_OK) - goto fk_error; - fp->curr_sect = nsect; + if (disk_read(fp->fs->drive, fp->buf, nsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); +#endif + fp->dsect = nsect; } - #if !_FS_READONLY - if (fp->fptr > fp->fsize) { /* Set changed flag if the file was extended */ + if (fp->fptr > fp->fsize) { /* Set changed flag if the file size is extended */ fp->fsize = fp->fptr; fp->flag |= FA__WRITTEN; } #endif - return FR_OK; - -fk_error: /* Abort this file due to an unrecoverable error */ - fp->flag |= FA__ERROR; - return FR_RW_ERROR; + LEAVE_FF(fp->fs, res); } @@ -1227,7 +2003,7 @@ fk_error: /* Abort this file due to an unrecoverable error */ #if _FS_MINIMIZE <= 1 /*-----------------------------------------------------------------------*/ -/* Create a directroy object */ +/* Create a Directroy Object */ /*-----------------------------------------------------------------------*/ FRESULT f_opendir ( @@ -1236,28 +2012,33 @@ FRESULT f_opendir ( ) { FRESULT res; + NAMEBUF(sfn, lfn); BYTE *dir; - char fn[8+3+1]; res = auto_mount(&path, &dj->fs, 0); if (res == FR_OK) { - res = trace_path(dj, fn, path, &dir); /* Trace the directory path */ - if (res == FR_OK) { /* Trace completed */ + INITBUF((*dj), sfn, lfn); + res = follow_path(dj, path); /* Follow the path to the directory */ + if (res == FR_OK) { /* Follow completed */ + dir = dj->dir; if (dir) { /* It is not the root dir */ - if (dir[DIR_Attr] & AM_DIR) { /* The entry is a directory */ - dj->clust = ((DWORD)LD_WORD(&dir[DIR_FstClusHI]) << 16) | LD_WORD(&dir[DIR_FstClusLO]); - dj->sect = clust2sect(dj->fs, dj->clust); - dj->index = 2; - } else { /* The entry is not a directory */ - res = FR_NO_FILE; + if (dir[DIR_Attr] & AM_DIR) { /* The object is a directory */ + dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); + } else { /* The object is not a directory */ + res = FR_NO_PATH; } + } else { /* It is the root dir */ + dj->sclust = (dj->fs->fs_type == FS_FAT32) ? dj->fs->dirbase : 0; } + if (res == FR_OK) res = dir_seek(dj, 0); dj->id = dj->fs->id; + } else { + if (res == FR_NO_FILE) res = FR_NO_PATH; } } - return res; + LEAVE_FF(dj->fs, res); } @@ -1268,35 +2049,41 @@ FRESULT f_opendir ( /*-----------------------------------------------------------------------*/ FRESULT f_readdir ( - DIR *dj, /* Pointer to the directory object */ - FILINFO *finfo /* Pointer to file information to return */ + DIR *dj, /* Pointer to the open directory object */ + FILINFO *fno /* Pointer to file information to return */ ) { - BYTE *dir, c, res; + FRESULT res; + NAMEBUF(sfn, lfn); res = validate(dj->fs, dj->id); /* Check validity of the object */ - if (res != FR_OK) return res; - - finfo->fname[0] = 0; - while (dj->sect) { - if (!move_window(dj->fs, dj->sect)) - return FR_RW_ERROR; - dir = &dj->fs->win[(dj->index & ((SS(dj->fs) - 1) >> 5)) * 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(dj)) dj->sect = 0; /* Next entry */ - if (finfo->fname[0]) break; /* Found valid entry */ + if (res == FR_OK) { + INITBUF((*dj), sfn, lfn); + if (!fno) { + res = dir_seek(dj, 0); + } else { + res = dir_read(dj); + if (res == FR_NO_FILE) { + dj->sect = 0; + res = FR_OK; + } + if (res == FR_OK) { /* A valid entry is found */ + get_fileinfo(dj, fno); /* Get the object information */ + res = dir_next(dj, FALSE); /* Increment index for next */ + if (res == FR_NO_FILE) { + dj->sect = 0; + res = FR_OK; + } + } + } } - return FR_OK; + LEAVE_FF(dj->fs, res); } - #if _FS_MINIMIZE == 0 /*-----------------------------------------------------------------------*/ /* Get File Status */ @@ -1304,27 +2091,27 @@ FRESULT f_readdir ( FRESULT f_stat ( const char *path, /* Pointer to the file path */ - FILINFO *finfo /* Pointer to file information to return */ + FILINFO *fno /* Pointer to file information to return */ ) { FRESULT res; DIR dj; - BYTE *dir; - char fn[8+3+1]; + NAMEBUF(sfn, lfn); res = auto_mount(&path, &dj.fs, 0); if (res == FR_OK) { - res = trace_path(&dj, fn, path, &dir); /* Trace the file path */ - if (res == FR_OK) { /* Trace completed */ - if (dir) /* Found an object */ - get_fileinfo(finfo, dir); + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) { /* Follwo completed */ + if (dj.dir) /* Found an object */ + get_fileinfo(&dj, fno); else /* It is root dir */ res = FR_INVALID_NAME; } } - return res; + LEAVE_FF(dj.fs, res); } @@ -1343,31 +2130,32 @@ FRESULT f_truncate ( res = validate(fp->fs, fp->id); /* Check validity of the object */ - if (res != FR_OK) 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 (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check abort flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (!(fp->flag & FA_WRITE)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); if (fp->fsize > fp->fptr) { fp->fsize = fp->fptr; /* Set file size to current R/W point */ fp->flag |= FA__WRITTEN; if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */ - if (!remove_chain(fp->fs, fp->org_clust)) goto ft_error; + res = remove_chain(fp->fs, fp->org_clust); fp->org_clust = 0; } else { /* When truncate a part of the file, remove remaining clusters */ ncl = get_cluster(fp->fs, fp->curr_clust); - if (ncl < 2) goto ft_error; - if (ncl < fp->fs->max_clust) { - if (!put_cluster(fp->fs, fp->curr_clust, 0x0FFFFFFF)) goto ft_error; - if (!remove_chain(fp->fs, ncl)) goto ft_error; + res = FR_OK; + if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (ncl == 1) res = FR_INT_ERR; + if (res == FR_OK && ncl < fp->fs->max_clust) { + res = put_cluster(fp->fs, fp->curr_clust, 0x0FFFFFFF); + if (res == FR_OK) res = remove_chain(fp->fs, ncl); } } } + if (res != FR_OK) fp->flag |= FA__ERROR; - return FR_OK; - -ft_error: /* Abort this file due to an unrecoverable error */ - fp->flag |= FA__ERROR; - return FR_RW_ERROR; + LEAVE_FF(fp->fs, res); } @@ -1378,41 +2166,43 @@ ft_error: /* Abort this file due to an unrecoverable error */ /*-----------------------------------------------------------------------*/ FRESULT f_getfree ( - const char *drv, /* Pointer to the logical drive number (root dir) */ - DWORD *nclust, /* Pointer to the variable to return number of free clusters */ + const char *path, /* Pointer to the logical drive number (root dir) */ + DWORD *nclst, /* Pointer to the variable to return number of free clusters */ FATFS **fatfs /* Pointer to pointer to corresponding file system object to return */ ) { FRESULT res; - DWORD n, clust, sect; + DWORD n, clst, sect; BYTE fat, f, *p; /* Get drive number */ - res = auto_mount(&drv, fatfs, 0); - if (res != FR_OK) return res; + res = auto_mount(&path, fatfs, 0); + if (res != FR_OK) LEAVE_FF(*fatfs, res); /* If number of free cluster is valid, return it without cluster scan. */ if ((*fatfs)->free_clust <= (*fatfs)->max_clust - 2) { - *nclust = (*fatfs)->free_clust; - return FR_OK; + *nclst = (*fatfs)->free_clust; + LEAVE_FF(*fatfs, FR_OK); } /* Get number of free clusters */ fat = (*fatfs)->fs_type; n = 0; if (fat == FS_FAT12) { - clust = 2; + clst = 2; do { - if ((WORD)get_cluster(*fatfs, clust) == 0) n++; - } while (++clust < (*fatfs)->max_clust); + if ((WORD)get_cluster(*fatfs, clst) == 0) n++; + } while (++clst < (*fatfs)->max_clust); } else { - clust = (*fatfs)->max_clust; + clst = (*fatfs)->max_clust; sect = (*fatfs)->fatbase; f = 0; p = 0; do { if (!f) { - if (!move_window(*fatfs, sect++)) return FR_RW_ERROR; + res = move_window(*fatfs, sect++); + if (res != FR_OK) + LEAVE_FF(*fatfs, res); p = (*fatfs)->win; } if (fat == FS_FAT16) { @@ -1422,15 +2212,13 @@ FRESULT f_getfree ( if (LD_DWORD(p) == 0) n++; p += 4; f += 2; } - } while (--clust); + } while (--clst); } (*fatfs)->free_clust = n; -#if _USE_FSINFO if (fat == FS_FAT32) (*fatfs)->fsi_flag = 1; -#endif + *nclst = n; - *nclust = n; - return FR_OK; + LEAVE_FF(*fatfs, FR_OK); } @@ -1445,40 +2233,45 @@ FRESULT f_unlink ( ) { FRESULT res; - DIR dj; - BYTE *dir, *sdir; - DWORD dclust, dsect; - char fn[8+3+1]; + DIR dj, sdj; + NAMEBUF(sfn, lfn); + BYTE *dir; + DWORD dclst; res = auto_mount(&path, &dj.fs, 1); - if (res != FR_OK) return res; - res = trace_path(&dj, fn, path, &dir); /* Trace the file path */ - if (res != FR_OK) return res; /* Trace failed */ - if (!dir) 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 = dj.fs->winsect; - dclust = ((DWORD)LD_WORD(&dir[DIR_FstClusHI]) << 16) | LD_WORD(&dir[DIR_FstClusLO]); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (res != FR_OK) LEAVE_FF(dj.fs, res); /* Follow failed */ + + dir = dj.dir; + if (!dir) /* Is it the root directory? */ + LEAVE_FF(dj.fs, FR_INVALID_NAME); + if (dir[DIR_Attr] & AM_RDO) /* Is it a R/O object? */ + LEAVE_FF(dj.fs, FR_DENIED); + dclst = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); if (dir[DIR_Attr] & AM_DIR) { /* It is a sub-directory */ - dj.clust = dclust; /* Check if the sub-dir is empty or not */ - dj.sect = clust2sect(dj.fs, dclust); - dj.index = 2; - do { - if (!move_window(dj.fs, dj.sect)) return FR_RW_ERROR; - sdir = &dj.fs->win[(dj.index & ((SS(dj.fs) - 1) >> 5)) * 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(&dj)); + if (dclst < 2) LEAVE_FF(dj.fs, FR_INT_ERR); + MemCpy(&sdj, &dj, sizeof(DIR)); /* Check if the sub-dir is empty or not */ + sdj.sclust = dclst; + res = dir_seek(&sdj, 0); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + res = dir_read(&sdj); + if (res == FR_OK) res = FR_DENIED; /* Not empty sub-dir */ + if (res != FR_NO_FILE) LEAVE_FF(dj.fs, res); } - if (!move_window(dj.fs, dsect)) return FR_RW_ERROR; /* Mark the directory entry 'deleted' */ - dir[DIR_Name] = 0xE5; - dj.fs->winflag = 1; - if (!remove_chain(dj.fs, dclust)) return FR_RW_ERROR; /* Remove the cluster chain */ + res = dir_remove(&dj); /* Remove directory entry */ + if (res == FR_OK) { + if (dclst) + res = remove_chain(dj.fs, dclst); /* Remove the cluster chain */ + if (res == FR_OK) res = sync(dj.fs); + } - return sync(dj.fs); + LEAVE_FF(dj.fs, FR_OK); } @@ -1494,56 +2287,68 @@ FRESULT f_mkdir ( { FRESULT res; DIR dj; - BYTE *dir, *fw, n; - char fn[8+3+1]; - DWORD sect, dsect, dclust, pclust, tim; + NAMEBUF(sfn, lfn); + BYTE *dir, n; + DWORD dsect, dclst, pclst, tim; res = auto_mount(&path, &dj.fs, 1); - if (res != FR_OK) return res; - res = trace_path(&dj, 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; + if (res != FR_OK) LEAVE_FF(dj.fs, res); - res = reserve_direntry(&dj, &dir); /* Reserve a directory entry */ - if (res != FR_OK) return res; - sect = dj.fs->winsect; - dclust = create_chain(dj.fs, 0); /* Allocate a cluster for new directory table */ - if (dclust == 1) return FR_RW_ERROR; - dsect = clust2sect(dj.fs, dclust); - if (!dsect) return FR_DENIED; - if (!move_window(dj.fs, dsect)) return FR_RW_ERROR; + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) res = FR_EXIST; /* Any file or directory is already existing */ + if (res != FR_NO_FILE) /* Any error occured */ + LEAVE_FF(dj.fs, res); - fw = dj.fs->win; - memset(fw, 0, SS(dj.fs)); /* Clear the new directory table */ - for (n = 1; n < dj.fs->csize; n++) { - if (disk_write(dj.fs->drive, 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; + dclst = create_chain(dj.fs, 0); /* Allocate a new cluster for new directory table */ + res = FR_OK; + if (dclst == 0) res = FR_DENIED; + if (dclst == 1) res = FR_INT_ERR; + if (dclst == 0xFFFFFFFF) res = FR_DISK_ERR; + if (res == FR_OK) + res = move_window(dj.fs, 0); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + dsect = clust2sect(dj.fs, dclst); + + dir = dj.fs->win; /* Initialize the new directory table */ + MemSet(dir, 0, SS(dj.fs)); + MemSet(dir+DIR_Name, ' ', 8+3); /* Create "." entry */ + dir[DIR_Name] = '.'; + dir[DIR_Attr] = AM_DIR; tim = get_fattime(); - ST_DWORD(&fw[DIR_WrtTime], tim); - memcpy(&fw[32], &fw[0], 32); fw[33] = '.'; /* Create ".." entry */ - ST_WORD(&fw[ DIR_FstClusLO], dclust); - ST_WORD(&fw[ DIR_FstClusHI], dclust >> 16); - pclust = dj.sclust; - if (dj.fs->fs_type == FS_FAT32 && pclust == dj.fs->dirbase) pclust = 0; - ST_WORD(&fw[32+DIR_FstClusLO], pclust); - ST_WORD(&fw[32+DIR_FstClusHI], pclust >> 16); - dj.fs->winflag = 1; + ST_DWORD(dir+DIR_WrtTime, tim); + ST_WORD(dir+DIR_FstClusLO, dclst); + ST_WORD(dir+DIR_FstClusHI, dclst >> 16); + MemCpy(dir+32, dir, 32); /* Create ".." entry */ + dir[33] = '.'; + pclst = dj.sclust; + if (dj.fs->fs_type == FS_FAT32 && pclst == dj.fs->dirbase) + pclst = 0; + ST_WORD(dir+32+DIR_FstClusLO, pclst); + ST_WORD(dir+32+DIR_FstClusHI, pclst >> 16); + for (n = 0; n < dj.fs->csize; n++) { /* Write dot entries and clear left sectors */ + dj.fs->winsect = dsect++; + dj.fs->wflag = 1; + res = move_window(dj.fs, 0); + if (res) LEAVE_FF(dj.fs, res); + MemSet(dir, 0, SS(dj.fs)); + } - if (!move_window(dj.fs, sect)) return FR_RW_ERROR; - memset(&dir[0], 0, 32); /* Initialize 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 */ - ST_WORD(&dir[DIR_FstClusHI], dclust >> 16); + res = dir_register(&dj); + if (res != FR_OK) { + remove_chain(dj.fs, dclst); + } else { + dir = dj.dir; + dir[DIR_Attr] = AM_DIR; /* Attribute */ + ST_DWORD(dir+DIR_WrtTime, tim); /* Crated time */ + ST_WORD(dir+DIR_FstClusLO, dclst); /* Table start cluster */ + ST_WORD(dir+DIR_FstClusHI, dclst >> 16); + dj.fs->wflag = 1; + res = sync(dj.fs); + } - return sync(dj.fs); + LEAVE_FF(dj.fs, res); } @@ -1561,24 +2366,28 @@ FRESULT f_chmod ( { FRESULT res; DIR dj; + NAMEBUF(sfn, lfn); BYTE *dir; - char fn[8+3+1]; res = auto_mount(&path, &dj.fs, 1); if (res == FR_OK) { - res = trace_path(&dj, fn, path, &dir); /* Trace the file path */ - if (res == FR_OK) { /* Trace completed */ - if (!dir) { - res = FR_INVALID_NAME; /* Root directory */ - } else { + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) { + dir = dj.dir; + if (!dir) { /* Is it a root directory? */ + res = FR_INVALID_NAME; + } else { /* File or sub directory */ 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 */ + dj.fs->wflag = 1; res = sync(dj.fs); } } } - return res; + + LEAVE_FF(dj.fs, res); } @@ -1589,30 +2398,34 @@ FRESULT f_chmod ( /*-----------------------------------------------------------------------*/ FRESULT f_utime ( - const char *path, /* Pointer to the file/directory name */ - const FILINFO *finfo /* Pointer to the timestamp to be set */ + const char *path, /* Pointer to the file/directory name */ + const FILINFO *fno /* Pointer to the timestamp to be set */ ) { FRESULT res; DIR dj; + NAMEBUF(sfn, lfn); BYTE *dir; - char fn[8+3+1]; res = auto_mount(&path, &dj.fs, 1); if (res == FR_OK) { - res = trace_path(&dj, fn, path, &dir); /* Trace the file path */ - if (res == FR_OK) { /* Trace completed */ - if (!dir) { - res = FR_INVALID_NAME; /* Root directory */ - } else { - ST_WORD(&dir[DIR_WrtTime], finfo->ftime); - ST_WORD(&dir[DIR_WrtDate], finfo->fdate); + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) { + dir = dj.dir; + if (!dir) { /* Root directory */ + res = FR_INVALID_NAME; + } else { /* File or sub-directory */ + ST_WORD(dir+DIR_WrtTime, fno->ftime); + ST_WORD(dir+DIR_WrtDate, fno->fdate); + dj.fs->wflag = 1; res = sync(dj.fs); } } } - return res; + + LEAVE_FF(dj.fs, res); } @@ -1628,36 +2441,57 @@ FRESULT f_rename ( ) { FRESULT res; - DIR dj; - DWORD sect_old; - BYTE *dir_old, *dir_new, direntry[32-11]; - char fn[8+3+1]; + DIR dj_old, dj_new; + NAMEBUF(sfn, lfn); + BYTE buf[21], *dir; + DWORD dw; - res = auto_mount(&path_old, &dj.fs, 1); - if (res != FR_OK) return res; + INITBUF(dj_old, sfn, lfn); + res = auto_mount(&path_old, &dj_old.fs, 1); + if (res == FR_OK) { + dj_new.fs = dj_old.fs; + res = follow_path(&dj_old, path_old); /* Check old object */ + } + if (res != FR_OK) LEAVE_FF(dj_old.fs, res); /* The old object is not found */ - res = trace_path(&dj, 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 = dj.fs->winsect; /* Save the object information */ - memcpy(direntry, &dir_old[DIR_Attr], 32-11); + if (!dj_old.dir) LEAVE_FF(dj_old.fs, FR_NO_FILE); /* Is root dir? */ + MemCpy(buf, dj_old.dir+DIR_Attr, 21); /* Save the object information */ - res = trace_path(&dj, 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(&dj, &dir_new); /* Reserve a directory entry */ - if (res != FR_OK) return res; + MemCpy(&dj_new, &dj_old, sizeof(DIR)); + res = follow_path(&dj_new, path_new); /* Check new object */ + if (res == FR_OK) res = FR_EXIST; /* The new object name is already existing */ + if (res == FR_NO_FILE) { /* Is it a valid path and no name collision? */ + res = dir_register(&dj_new); /* Register the new object */ + if (res == FR_OK) { + dir = dj_new.dir; /* Copy object information into new entry */ + MemCpy(dir+13, buf+2, 19); + dir[DIR_Attr] = buf[0]; + dj_old.fs->wflag = 1; + if (dir[DIR_Attr] & AM_DIR) { /* Update .. entry in the directory if needed */ + dw = clust2sect(dj_new.fs, (DWORD)LD_WORD(dir+DIR_FstClusHI) | LD_WORD(dir+DIR_FstClusLO)); + if (!dw) { + res = FR_INT_ERR; + } else { + res = move_window(dj_new.fs, dw); + dir = dj_new.fs->win+32; + if (res == FR_OK && dir[1] == '.') { + dw = (dj_new.fs->fs_type == FS_FAT32 && dj_new.sclust == dj_new.fs->dirbase) ? 0 : dj_new.sclust; + ST_WORD(dir+DIR_FstClusLO, dw); + ST_WORD(dir+DIR_FstClusHI, dw >> 16); + dj_new.fs->wflag = 1; + } + } + } + if (res == FR_OK) { + res = dir_remove(&dj_old); /* Remove old entry */ + if (res == FR_OK) + res = sync(dj_old.fs); + } + } + } - 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]; - dj.fs->winflag = 1; - - if (!move_window(dj.fs, sect_old)) return FR_RW_ERROR; /* Delete old entry */ - dir_old[DIR_Name] = 0xE5; - - return sync(dj.fs); + LEAVE_FF(dj_old.fs, res); } #endif /* !_FS_READONLY */ @@ -1667,27 +2501,89 @@ FRESULT f_rename ( +/*-----------------------------------------------------------------------*/ +/* Forward data to the stream directly (Available on only _FS_TINY cfg) */ +/*-----------------------------------------------------------------------*/ +#if _USE_FORWARD && _FS_TINY + +FRESULT f_forward ( + FIL *fp, /* Pointer to the file object */ + UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */ + UINT btr, /* Number of bytes to forward */ + UINT *bf /* Pointer to number of bytes forwarded */ +) +{ + FRESULT res; + DWORD remain, clst, sect; + UINT rcnt; + + + *bf = 0; + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check error flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (!(fp->flag & FA_READ)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + + remain = fp->fsize - fp->fptr; + if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ + + for ( ; btr && (*func)(NULL, 0); /* Repeat until all data transferred or stream becomes busy */ + fp->fptr += rcnt, *bf += rcnt, btr -= rcnt) { + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + if (fp->csect >= fp->fs->csize) { /* On the cluster boundary? */ + clst = (fp->fptr == 0) ? /* On the top of the file? */ + fp->org_clust : get_cluster(fp->fs, fp->curr_clust); + if (clst <= 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->curr_clust = clst; /* Update current cluster */ + fp->csect = 0; /* Reset sector address in the cluster */ + } + fp->csect++; /* Next sector address in the cluster */ + } + sect = clust2sect(fp->fs, fp->curr_clust); /* Get current data sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += fp->csect - 1; + if (move_window(fp->fs, sect)) /* Move sector window */ + ABORT(fp->fs, FR_DISK_ERR); + fp->dsect = sect; + rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs)); /* Forward data from sector window */ + if (rcnt > btr) rcnt = btr; + rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt); + if (!rcnt) ABORT(fp->fs, FR_INT_ERR); + } + + LEAVE_FF(fp->fs, FR_OK); +} +#endif /* _USE_FORWARD */ + + + #if _USE_MKFS && !_FS_READONLY /*-----------------------------------------------------------------------*/ /* Create File System on the Drive */ /*-----------------------------------------------------------------------*/ #define N_ROOTDIR 512 /* Multiple of 32 and <= 2048 */ #define N_FATS 1 /* 1 or 2 */ -#define MAX_SECTOR 64000000UL /* Maximum partition size */ +#define MAX_SECTOR 131072000UL /* Maximum partition size */ #define MIN_SECTOR 2000UL /* Minimum partition size */ - FRESULT f_mkfs ( BYTE drv, /* Logical drive number */ BYTE partition, /* Partitioning rule 0:FDISK, 1:SFD */ WORD allocsize /* Allocation unit size [bytes] */ ) { + static const DWORD sstbl[] = { 2048000, 1024000, 512000, 256000, 128000, 64000, 32000, 16000, 8000, 4000, 0 }; + static const WORD cstbl[] = { 32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512 }; BYTE fmt, m, *tbl; DWORD b_part, b_fat, b_dir, b_data; /* Area offset (LBA) */ DWORD n_part, n_rsv, n_fat, n_dir; /* Area size */ - DWORD n_clust, n; + DWORD n_clst, n; + WORD as; FATFS *fs; DSTATUS stat; @@ -1695,8 +2591,6 @@ FRESULT f_mkfs ( /* Check validity of the parameters */ if (drv >= _DRIVES) return FR_INVALID_DRIVE; if (partition >= 2) return FR_MKFS_ABORTED; - for (n = 512; n <= 32768U && n != allocsize; n <<= 1); - if (n != allocsize) return FR_MKFS_ABORTED; /* Check mounted drive and clear work area */ fs = FatFs[drv]; @@ -1713,7 +2607,15 @@ FRESULT f_mkfs ( if (n_part > MAX_SECTOR) n_part = MAX_SECTOR; b_part = (!partition) ? 63 : 0; /* Boot sector */ n_part -= b_part; -#if S_MAX_SIZ > 512 /* Check disk sector size */ +#if MAX_SS == 512 + if (!allocsize) { /* Auto selection of cluster size */ + for (n = 0; n_part < sstbl[n]; n++) ; + allocsize = cstbl[n]; + } +#endif + for (as = 512; as <= 32768U && as != allocsize; as <<= 1); + if (as != allocsize) return FR_MKFS_ABORTED; +#if MAX_SS > 512 /* Check disk sector size */ if (disk_ioctl(drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > S_MAX_SIZ || SS(fs) > allocsize) @@ -1722,25 +2624,25 @@ FRESULT f_mkfs ( allocsize /= SS(fs); /* Number of sectors per cluster */ /* Pre-compute number of clusters and FAT type */ - n_clust = n_part / allocsize; + n_clst = n_part / allocsize; fmt = FS_FAT12; - if (n_clust >= 0xFF5) fmt = FS_FAT16; - if (n_clust >= 0xFFF5) fmt = FS_FAT32; + if (n_clst >= 0xFF5) fmt = FS_FAT16; + if (n_clst >= 0xFFF5) fmt = FS_FAT32; /* Determine offset and size of FAT structure */ switch (fmt) { case FS_FAT12: - n_fat = ((n_clust * 3 + 1) / 2 + 3 + SS(fs) - 1) / SS(fs); + n_fat = ((n_clst * 3 + 1) / 2 + 3 + SS(fs) - 1) / SS(fs); n_rsv = 1 + partition; n_dir = N_ROOTDIR * 32 / SS(fs); break; case FS_FAT16: - n_fat = ((n_clust * 2) + 4 + SS(fs) - 1) / SS(fs); + n_fat = ((n_clst * 2) + 4 + SS(fs) - 1) / SS(fs); n_rsv = 1 + partition; n_dir = N_ROOTDIR * 32 / SS(fs); break; default: - n_fat = ((n_clust * 4) + 8 + SS(fs) - 1) / SS(fs); + n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs); n_rsv = 33 - partition; n_dir = 0; } @@ -1755,17 +2657,17 @@ FRESULT f_mkfs ( /* b_dir and b_data are no longer used below */ /* Determine number of cluster and final check of validity of the FAT type */ - n_clust = (n_part - n_rsv - n_fat * N_FATS - n_dir) / allocsize; - if ( (fmt == FS_FAT16 && n_clust < 0xFF5) - || (fmt == FS_FAT32 && n_clust < 0xFFF5)) + n_clst = (n_part - n_rsv - n_fat * N_FATS - n_dir) / allocsize; + if ( (fmt == FS_FAT16 && n_clst < 0xFF5) + || (fmt == FS_FAT32 && n_clst < 0xFFF5)) return FR_MKFS_ABORTED; /* Create partition table if needed */ if (!partition) { DWORD n_disk = b_part + n_part; - tbl = &fs->win[MBR_Table]; - ST_DWORD(&tbl[0], 0x00010180); /* Partition start in CHS */ + tbl = fs->win+MBR_Table; + ST_DWORD(tbl, 0x00010180); /* Partition start in CHS */ if (n_disk < 63UL * 255 * 1024) { /* Partition end in CHS */ n_disk = n_disk / 63 / 255; tbl[7] = (BYTE)n_disk; @@ -1778,71 +2680,71 @@ FRESULT f_mkfs ( tbl[4] = (n_part < 0x10000) ? 0x04 : 0x06; else tbl[4] = 0x0c; - ST_DWORD(&tbl[8], 63); /* Partition start in LBA */ - ST_DWORD(&tbl[12], n_part); /* Partition size in LBA */ - ST_WORD(&tbl[64], 0xAA55); /* Signature */ + ST_DWORD(tbl+8, 63); /* Partition start in LBA */ + ST_DWORD(tbl+12, n_part); /* Partition size in LBA */ + ST_WORD(tbl+64, 0xAA55); /* Signature */ if (disk_write(drv, fs->win, 0, 1) != RES_OK) - return FR_RW_ERROR; + return FR_DISK_ERR; } /* Create boot record */ tbl = fs->win; /* Clear buffer */ - memset(tbl, 0, SS(fs)); - ST_DWORD(&tbl[BS_jmpBoot], 0x90FEEB); /* Boot code (jmp $, nop) */ - ST_WORD(&tbl[BPB_BytsPerSec], SS(fs)); /* Sector size */ + MemSet(tbl, 0, SS(fs)); + ST_DWORD(tbl+BS_jmpBoot, 0x90FEEB); /* Boot code (jmp $, nop) */ + ST_WORD(tbl+BPB_BytsPerSec, SS(fs)); /* Sector size */ tbl[BPB_SecPerClus] = (BYTE)allocsize; /* Sectors per cluster */ - ST_WORD(&tbl[BPB_RsvdSecCnt], n_rsv); /* Reserved sectors */ + ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv); /* Reserved sectors */ tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */ - ST_WORD(&tbl[BPB_RootEntCnt], SS(fs) / 32 * n_dir); /* Number of rootdir entries */ + ST_WORD(tbl+BPB_RootEntCnt, SS(fs) / 32 * n_dir); /* Number of rootdir entries */ if (n_part < 0x10000) { /* Number of total sectors */ - ST_WORD(&tbl[BPB_TotSec16], n_part); + ST_WORD(tbl+BPB_TotSec16, n_part); } else { - ST_DWORD(&tbl[BPB_TotSec32], n_part); + ST_DWORD(tbl+BPB_TotSec32, n_part); } tbl[BPB_Media] = 0xF8; /* Media descripter */ - ST_WORD(&tbl[BPB_SecPerTrk], 63); /* Number of sectors per track */ - ST_WORD(&tbl[BPB_NumHeads], 255); /* Number of heads */ - ST_DWORD(&tbl[BPB_HiddSec], b_part); /* Hidden sectors */ + ST_WORD(tbl+BPB_SecPerTrk, 63); /* Number of sectors per track */ + ST_WORD(tbl+BPB_NumHeads, 255); /* Number of heads */ + ST_DWORD(tbl+BPB_HiddSec, b_part); /* Hidden sectors */ n = get_fattime(); /* Use current time as a VSN */ if (fmt != FS_FAT32) { - ST_DWORD(&tbl[BS_VolID], n); /* Volume serial number */ - ST_WORD(&tbl[BPB_FATSz16], n_fat); /* Number of secters per FAT */ + ST_DWORD(tbl+BS_VolID, n); /* Volume serial number */ + ST_WORD(tbl+BPB_FATSz16, n_fat); /* Number of secters per FAT */ tbl[BS_DrvNum] = 0x80; /* Drive number */ tbl[BS_BootSig] = 0x29; /* Extended boot signature */ - memcpy(&tbl[BS_VolLab], "NO NAME FAT ", 19); /* Volume lavel, FAT signature */ + MemCpy(tbl+BS_VolLab, "NO NAME FAT ", 19); /* Volume lavel, FAT signature */ } else { - ST_DWORD(&tbl[BS_VolID32], n); /* Volume serial number */ - ST_DWORD(&tbl[BPB_FATSz32], n_fat); /* Number of secters per FAT */ - ST_DWORD(&tbl[BPB_RootClus], 2); /* Root directory cluster (2) */ - ST_WORD(&tbl[BPB_FSInfo], 1); /* FSInfo record (bs+1) */ - ST_WORD(&tbl[BPB_BkBootSec], 6); /* Backup boot record (bs+6) */ + ST_DWORD(tbl+BS_VolID32, n); /* Volume serial number */ + ST_DWORD(tbl+BPB_FATSz32, n_fat); /* Number of secters per FAT */ + ST_DWORD(tbl+BPB_RootClus, 2); /* Root directory cluster (2) */ + ST_WORD(tbl+BPB_FSInfo, 1); /* FSInfo record offset (bs+1) */ + ST_WORD(tbl+BPB_BkBootSec, 6); /* Backup boot record offset (bs+6) */ tbl[BS_DrvNum32] = 0x80; /* Drive number */ tbl[BS_BootSig32] = 0x29; /* Extended boot signature */ - memcpy(&tbl[BS_VolLab32], "NO NAME FAT32 ", 19); /* Volume lavel, FAT signature */ + MemCpy(tbl+BS_VolLab32, "NO NAME FAT32 ", 19); /* Volume lavel, FAT signature */ } - ST_WORD(&tbl[BS_55AA], 0xAA55); /* Signature */ + ST_WORD(tbl+BS_55AA, 0xAA55); /* Signature */ if (disk_write(drv, tbl, b_part+0, 1) != RES_OK) - return FR_RW_ERROR; + return FR_DISK_ERR; if (fmt == FS_FAT32) disk_write(drv, tbl, b_part+6, 1); /* Initialize FAT area */ for (m = 0; m < N_FATS; m++) { - memset(tbl, 0, SS(fs)); /* 1st sector of the FAT */ + MemSet(tbl, 0, SS(fs)); /* 1st sector of the FAT */ if (fmt != FS_FAT32) { n = (fmt == FS_FAT12) ? 0x00FFFFF8 : 0xFFFFFFF8; - ST_DWORD(&tbl[0], n); /* Reserve cluster #0-1 (FAT12/16) */ + ST_DWORD(tbl, n); /* Reserve cluster #0-1 (FAT12/16) */ } else { - ST_DWORD(&tbl[0], 0xFFFFFFF8); /* Reserve cluster #0-1 (FAT32) */ - ST_DWORD(&tbl[4], 0xFFFFFFFF); - ST_DWORD(&tbl[8], 0x0FFFFFFF); /* Reserve cluster #2 for root dir */ + ST_DWORD(tbl+0, 0xFFFFFFF8); /* Reserve cluster #0-1 (FAT32) */ + ST_DWORD(tbl+4, 0xFFFFFFFF); + ST_DWORD(tbl+8, 0x0FFFFFFF); /* Reserve cluster #2 for root dir */ } if (disk_write(drv, tbl, b_fat++, 1) != RES_OK) - return FR_RW_ERROR; - memset(tbl, 0, SS(fs)); /* Following FAT entries are filled by zero */ + return FR_DISK_ERR; + MemSet(tbl, 0, SS(fs)); /* Following FAT entries are filled by zero */ for (n = 1; n < n_fat; n++) { if (disk_write(drv, tbl, b_fat++, 1) != RES_OK) - return FR_RW_ERROR; + return FR_DISK_ERR; } } @@ -1850,21 +2752,21 @@ FRESULT f_mkfs ( m = (BYTE)((fmt == FS_FAT32) ? allocsize : n_dir); do { if (disk_write(drv, tbl, b_fat++, 1) != RES_OK) - return FR_RW_ERROR; + return FR_DISK_ERR; } while (--m); /* Create FSInfo record if needed */ if (fmt == FS_FAT32) { - ST_WORD(&tbl[BS_55AA], 0xAA55); - ST_DWORD(&tbl[FSI_LeadSig], 0x41615252); - ST_DWORD(&tbl[FSI_StrucSig], 0x61417272); - ST_DWORD(&tbl[FSI_Free_Count], n_clust - 1); - ST_DWORD(&tbl[FSI_Nxt_Free], 0xFFFFFFFF); + ST_WORD(tbl+BS_55AA, 0xAA55); + ST_DWORD(tbl+FSI_LeadSig, 0x41615252); + ST_DWORD(tbl+FSI_StrucSig, 0x61417272); + ST_DWORD(tbl+FSI_Free_Count, n_clst - 1); + ST_DWORD(tbl+FSI_Nxt_Free, 0xFFFFFFFF); disk_write(drv, tbl, b_part+1, 1); disk_write(drv, tbl, b_part+7, 1); } - return (disk_ioctl(drv, CTRL_SYNC, NULL) == RES_OK) ? FR_OK : FR_RW_ERROR; + return (disk_ioctl(drv, CTRL_SYNC, (void*)NULL) == RES_OK) ? FR_OK : FR_DISK_ERR; } #endif /* _USE_MKFS && !_FS_READONLY */ @@ -1872,11 +2774,11 @@ FRESULT f_mkfs ( -#if _USE_STRFUNC >= 1 +#if _USE_STRFUNC /*-----------------------------------------------------------------------*/ /* Get a string from the file */ /*-----------------------------------------------------------------------*/ -char* fgets ( +char* f_gets ( char* buff, /* Pointer to the string buffer to read */ int len, /* Size of string buffer */ FIL* fil /* Pointer to the file object */ @@ -1897,7 +2799,7 @@ char* fgets ( if (*p++ == '\n') break; /* Break when reached end of line */ } *p = 0; - return i ? buff : 0; /* When no data read (eof or error), return with error. */ + return i ? buff : NULL; /* When no data read (eof or error), return with error. */ } @@ -1907,7 +2809,7 @@ char* fgets ( /*-----------------------------------------------------------------------*/ /* Put a character to the file */ /*-----------------------------------------------------------------------*/ -int fputc ( +int f_putc ( int chr, /* A character to be output */ FIL* fil /* Ponter to the file object */ ) @@ -1917,7 +2819,7 @@ int fputc ( #if _USE_STRFUNC >= 2 - if (chr == '\n') fputc ('\r', fil); /* LF -> CRLF conversion */ + if (chr == '\n') f_putc ('\r', fil); /* LF -> CRLF conversion */ #endif if (!fil) { /* Special value may be used to switch the destination to any other device */ /* put_console(chr); */ @@ -1925,7 +2827,7 @@ int fputc ( } c = (char)chr; f_write(fil, &c, 1, &bw); /* Write a byte to the file */ - return bw ? chr : EOF; /* Return the resulut */ + return bw ? chr : EOF; /* Return the result */ } @@ -1934,7 +2836,7 @@ int fputc ( /*-----------------------------------------------------------------------*/ /* Put a string to the file */ /*-----------------------------------------------------------------------*/ -int fputs ( +int f_puts ( const char* str, /* Pointer to the string to be output */ FIL* fil /* Pointer to the file object */ ) @@ -1943,7 +2845,7 @@ int fputs ( for (n = 0; *str; str++, n++) { - if (fputc(*str, fil) == EOF) return EOF; + if (f_putc(*str, fil) == EOF) return EOF; } return n; } @@ -1954,7 +2856,7 @@ int fputs ( /*-----------------------------------------------------------------------*/ /* Put a formatted string to the file */ /*-----------------------------------------------------------------------*/ -int fprintf ( +int f_printf ( FIL* fil, /* Pointer to the file object */ const char* str, /* Pointer to the format string */ ... /* Optional arguments... */ @@ -1973,7 +2875,7 @@ int fprintf ( c = *str++; if (c == 0) break; /* End of string */ if (c != '%') { /* Non escape cahracter */ - cc = fputc(c, fil); + cc = f_putc(c, fil); if (cc != EOF) cc = 1; continue; } @@ -1990,11 +2892,11 @@ int fprintf ( f |= 2; c = *str++; } if (c == 's') { /* Type is string */ - cc = fputs(va_arg(arp, char*), fil); + cc = f_puts(va_arg(arp, char*), fil); continue; } if (c == 'c') { /* Type is character */ - cc = fputc(va_arg(arp, char), fil); + cc = f_putc(va_arg(arp, int), fil); if (cc != EOF) cc = 1; continue; } @@ -2010,7 +2912,7 @@ int fprintf ( } /* Put numeral string */ if (c == 'd') { - if (val >= 0x80000000) { + if (val & 0x80000000) { val = 0 - val; f |= 4; } @@ -2025,7 +2927,7 @@ int fprintf ( if (i && (f & 4)) s[--i] = '-'; w = sizeof(s) - 1 - w; while (i && i > w) s[--i] = (f & 1) ? '0' : ' '; - cc = fputs(&s[i], fil); + cc = f_puts(&s[i], fil); } va_end(arp); @@ -2033,4 +2935,4 @@ int fprintf ( } #endif /* !_FS_READONLY */ -#endif /* _USE_STRFUNC >= 1*/ +#endif /* _USE_STRFUNC */ diff --git a/ff.h b/ff.h index ad650e6..cbd3b8b 100644 --- a/ff.h +++ b/ff.h @@ -1,198 +1,367 @@ -/*--------------------------------------------------------------------------/ -/ FatFs - FAT file system module include file R0.06 (C)ChaN, 2008 -/---------------------------------------------------------------------------/ -/ 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. +/*---------------------------------------------------------------------------/ +/ FatFs - FAT file system module include file R0.07 (C)ChaN, 2009 +/----------------------------------------------------------------------------/ +/ FatFs module is an open source project to implement FAT file system to small +/ embedded systems. It is opened for education, research and development under +/ license policy of following trems. / -/ Copyright (C) 2008, ChaN, all right reserved. +/ Copyright (C) 2009, 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 / commercial use without any restriction under your responsibility. / * Redistributions of source code must retain the above copyright notice. / -/---------------------------------------------------------------------------*/ +/----------------------------------------------------------------------------*/ +#include "integer.h" + +/*---------------------------------------------------------------------------/ +/ FatFs Configuration Options +/ +/ CAUTION! Do not forget to make clean the project after any changes to +/ the configuration options. +/ +/----------------------------------------------------------------------------*/ #ifndef _FATFS +#define _FATFS + +#define _WORD_ACCESS 0 +/* The _WORD_ACCESS option defines which access method is used to the word +/ data in the FAT structure. +/ +/ 0: Byte-by-byte access. Always compatible with all platforms. +/ 1: Word access. Do not choose this unless following condition is met. +/ +/ When the byte order on the memory is big-endian or address miss-aligned +/ word access results incorrect behavior, the _WORD_ACCESS must be set to 0. +/ If it is not the case, the value can also be set to 1 to improve the +/ performance and code efficiency. */ -#define _MCU_ENDIAN 2 -/* The _MCU_ENDIAN defines which access method is used to the FAT structure. -/ 1: Enable word access. -/ 2: Disable word access and use byte-by-byte access instead. -/ When the architectural byte order of the MCU is big-endian and/or address -/ miss-aligned access results incorrect behavior, the _MCU_ENDIAN must be set to 2. -/ If it is not the case, it can also be set to 1 for good code efficiency. */ #define _FS_READONLY 1 /* Setting _FS_READONLY to 1 defines read only configuration. This removes / writing functions, f_write, f_sync, f_unlink, f_mkdir, f_chmod, f_rename, / f_truncate and useless f_getfree. */ + #define _FS_MINIMIZE 2 /* The _FS_MINIMIZE option defines minimization level to remove some functions. -/ 0: Full function. -/ 1: f_stat, f_getfree, f_unlink, f_mkdir, f_chmod, f_truncate and f_rename are removed. -/ 2: f_opendir and f_readdir are removed in addition to level 1. -/ 3: f_lseek is removed in addition to level 2. */ +/ +/ 0: Full function. +/ 1: f_stat, f_getfree, f_unlink, f_mkdir, f_chmod, f_truncate and f_rename +/ are removed. +/ 2: f_opendir and f_readdir are removed in addition to level 1. +/ 3: f_lseek is removed in addition to level 2. */ + + +#define _FS_TINY 1 +/* When _FS_TINY is set to 1, FatFs uses the sector buffer in the file system +/ object instead of the sector buffer in the individual file object for file +/ data transfer. This reduces memory consumption 512 bytes each file object. */ + + +#define _DRIVES 1 +/* Number of volumes (logical drives) to be used. */ + #define _USE_STRFUNC 0 /* To enable string functions, set _USE_STRFUNC to 1 or 2. */ -#define _USE_MKFS 0 -/* When _USE_MKFS is set to 1 and _FS_READONLY is set to 0, f_mkfs function is -/ enabled. */ -#define _DRIVES 2 -/* Number of logical drives to be used. This affects the size of internal table. */ +#define _USE_MKFS 0 +/* To enable f_mkfs function, set _USE_MKFS to 1 and set _FS_READONLY to 0 */ + + +#define _USE_FORWARD 0 +/* To enable f_forward function, set _USE_FORWARD to 1 and set _FS_TINY to 1. */ + + +#define _USE_LFN 0 +#define _MAX_LFN 255 /* Maximum LFN length to handle (max:255) */ +/* The _USE_LFN option switches the LFN support. +/ +/ 0: Disable LFN. +/ 1: Enable LFN with static working buffer on the bss. Not re-entrant. +/ 2: Enable LFN with dynamic working buffer on the caller's 'stack'. +/ +/ The working buffer occupies (_MAX_LFN + 1) * 2 bytes. When enable LFN, +/ a Unicode - OEM code conversion function ff_convert() must be linked. */ + + +#define _CODE_PAGE 437 +/* The _CODE_PAGE specifies the OEM code page to be used on the target system. +/ When it is non LFN configuration, there is no difference between SBCS code +/ pages. When LFN is enabled, the code page must always be set correctly. +/ 437 - U.S. +/ 720 - Arabic +/ 737 - Greek +/ 775 - Baltic +/ 850 - Multilingual Latin 1 +/ 852 - Latin 2 +/ 855 - Cyrillic +/ 857 - Turkish +/ 858 - Multilingual Latin 1 + Euro +/ 862 - Hebrew +/ 866 - Russian +/ 874 - Thai +/ 932 - Japanese Shift-JIS (DBCS) +/ 936 - Simplified Chinese GBK (DBCS) +/ 949 - Korean (DBCS) +/ 950 - Traditional Chinese Big5 (DBCS) +/ 1258 - Vietnam +*/ + #define _MULTI_PARTITION 0 -/* When _MULTI_PARTITION is set to 0, each logical drive is bound to same -/ physical drive number and can mount only 1st primaly partition. When it is -/ set to 1, each logical drive can mount a partition listed in Drives[]. */ - -#define _USE_FSINFO 0 -/* To enable FSInfo support on FAT32 volume, set _USE_FSINFO to 1. */ - -#define _USE_SJIS 1 -/* When _USE_SJIS is set to 1, Shift-JIS code transparency is enabled, otherwise -/ only US-ASCII(7bit) code can be accepted as file/directory name. */ - -#define _USE_NTFLAG 1 -/* When _USE_NTFLAG is set to 1, upper/lower case of the file name is preserved. -/ Note that the files are always accessed in case insensitive. */ +/* When _MULTI_PARTITION is set to 0, each volume is bound to same physical +/ drive number and can mount only 1st primaly partition. When it is set to 1, +/ each volume is tied to the partition listed in Drives[]. */ -#include "integer.h" +#define _FS_REENTRANT 0 +#define _TIMEOUT 1000 +/* To make the FatFs module re-entrant, set 1 and re-write platform dependent +/ lock out code that defined arownd _FS_REENTRANT. The _TIMEOUT defines the +/ time out value in unit of milliseconds on the multi access exclusion. */ + + +#define _EXCLUDE_LIB 0 +/* When _EXCLUDE_LIB is set to 1, FatFs module does not use standard library. */ -/* Definitions corresponds to multiple sector size (not tested) */ -#define S_MAX_SIZ 512U /* Do not change */ -#if S_MAX_SIZ > 512U +/* End of configuration options. Do not change followings without care. */ +/*--------------------------------------------------------------------------*/ + + + +/* Definitions corresponds to multiple sector size (Not tested) */ + +#define MAX_SS 512U /* Do not change */ +#if MAX_SS > 512U #define SS(fs) ((fs)->s_size) #else #define SS(fs) 512U #endif + /* File system object structure */ + typedef struct _FATFS { - WORD id; /* File system mount ID */ - WORD n_rootdir; /* Number of root directory entries */ - DWORD winsect; /* Current sector appearing in the win[] */ - DWORD sects_fat; /* Sectors per fat */ - DWORD max_clust; /* Maximum cluster# + 1 */ - DWORD fatbase; /* FAT start sector */ - DWORD dirbase; /* Root directory start sector (cluster# for FAT32) */ - DWORD database; /* Data start sector */ -#if !_FS_READONLY - DWORD last_clust; /* Last allocated cluster */ - DWORD free_clust; /* Number of free clusters */ -#if _USE_FSINFO - DWORD fsi_sector; /* fsinfo sector */ - BYTE fsi_flag; /* fsinfo dirty flag (1:must be written back) */ - BYTE pad2; -#endif -#endif - BYTE fs_type; /* FAT sub type */ - BYTE csize; /* Number of sectors per cluster */ -#if S_MAX_SIZ > 512U - WORD s_size; /* Sector size */ -#endif - BYTE n_fats; /* Number of FAT copies */ - BYTE drive; /* Physical drive number */ - BYTE winflag; /* win[] dirty flag (1:must be written back) */ + BYTE fs_type; /* FAT sub type */ + BYTE drive; /* Physical drive number */ + BYTE csize; /* Number of sectors per cluster */ + BYTE n_fats; /* Number of FAT copies */ + BYTE wflag; /* win[] dirty flag (1:must be written back) */ BYTE pad1; - BYTE win[S_MAX_SIZ]; /* Disk access window for Directory/FAT */ + WORD id; /* File system mount ID */ + WORD n_rootdir; /* Number of root directory entries (0 on FAT32) */ +#if _FS_REENTRANT + HANDLE h_mutex; /* Handle to the mutex (Platform dependent) */ +#endif +#if MAX_SS > 512U + WORD s_size; /* Sector size */ +#endif +#if !_FS_READONLY + BYTE fsi_flag; /* fsinfo dirty flag (1:must be written back) */ + BYTE pad2; + DWORD last_clust; /* Last allocated cluster */ + DWORD free_clust; /* Number of free clusters */ + DWORD fsi_sector; /* fsinfo sector */ +#endif + DWORD sects_fat; /* Sectors per fat */ + DWORD max_clust; /* Maximum cluster# + 1. Number of clusters is max_clust - 2 */ + DWORD fatbase; /* FAT start sector */ + DWORD dirbase; /* Root directory start sector (Cluster# on FAT32) */ + DWORD database; /* Data start sector */ + DWORD winsect; /* Current sector appearing in the win[] */ + BYTE win[MAX_SS];/* Disk access window for Directory/FAT */ } FATFS; + /* Directory object structure */ + typedef struct _DIR { WORD id; /* Owner file system mount ID */ - WORD index; /* Current index */ + WORD index; /* Current index number */ FATFS* fs; /* Pointer to the owner file system object */ - DWORD sclust; /* Start cluster */ + DWORD sclust; /* Table start cluster (0:Static table) */ DWORD clust; /* Current cluster */ DWORD sect; /* Current sector */ + BYTE* dir; /* Pointer to the current SFN entry in the win[] */ + BYTE* fn; /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */ +#if _USE_LFN + WCHAR* lfn; /* Pointer to the LFN working buffer */ + WORD lfn_idx; /* Last matched LFN index (0xFFFF:No LFN) */ +#endif } DIR; + /* File object structure */ + typedef struct _FIL { - WORD id; /* Owner file system mount ID */ - BYTE flag; /* File status flags */ - BYTE csect; /* Sector address in the cluster */ - FATFS* fs; /* Pointer to the owner file system object */ - DWORD fptr; /* File R/W pointer */ - DWORD fsize; /* File size */ - DWORD org_clust; /* File start cluster */ - DWORD curr_clust; /* Current cluster */ - DWORD curr_sect; /* Current sector */ + FATFS* fs; /* Pointer to the owner file system object */ + WORD id; /* Owner file system mount ID */ + BYTE flag; /* File status flags */ + BYTE csect; /* Sector address in the cluster */ + DWORD fptr; /* File R/W pointer */ + DWORD fsize; /* File size */ + DWORD org_clust; /* File start cluster */ + DWORD curr_clust; /* Current cluster */ + DWORD dsect; /* Current data sector */ #if _FS_READONLY == 0 - DWORD dir_sect; /* Sector containing the directory entry */ - BYTE* dir_ptr; /* Ponter to the directory entry in the window */ + DWORD dir_sect; /* Sector containing the directory entry */ + BYTE* dir_ptr; /* Ponter to the directory entry in the window */ +#endif +#if !_FS_TINY + BYTE buf[MAX_SS];/* File R/W buffer */ #endif - BYTE buffer[S_MAX_SIZ]; /* File R/W buffer */ } FIL; + /* File status structure */ + typedef struct _FILINFO { - DWORD fsize; /* Size */ - WORD fdate; /* Date */ - WORD ftime; /* Time */ - BYTE fattrib; /* Attribute */ - char fname[8+1+3+1]; /* Name (8.3 format) */ + DWORD fsize; /* File size */ + WORD fdate; /* Last modified date */ + WORD ftime; /* Last modified time */ + BYTE fattrib; /* Attribute */ + char fname[13]; /* Short file name (8.3 format) */ +#if _USE_LFN + char *lfname; /* Pointer to the LFN buffer */ + int lfsize; /* Size of LFN buffer [bytes] */ +#endif } FILINFO; +/* DBCS code ranges */ + +#if _CODE_PAGE == 932 /* CP932 (Japanese Shift-JIS) */ +#define _DF1S 0x81 /* DBC 1st byte range 1 start */ +#define _DF1E 0x9F /* DBC 1st byte range 1 end */ +#define _DF2S 0xE0 /* DBC 1st byte range 2 start */ +#define _DF2E 0xFC /* DBC 1st byte range 2 end */ +#define _DS1S 0x40 /* DBC 2nd byte range 1 start */ +#define _DS1E 0x7E /* DBC 2nd byte range 1 end */ +#define _DS2S 0x80 /* DBC 2nd byte range 2 start */ +#define _DS2E 0xFC /* DBC 2nd byte range 2 end */ + +#elif _CODE_PAGE == 936 /* CP936 (Simplified Chinese GBK) */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0x80 +#define _DS2E 0xFE + +#elif _CODE_PAGE == 949 /* CP949 (Korean) */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x41 +#define _DS1E 0x5A +#define _DS2S 0x61 +#define _DS2E 0x7A +#define _DS3S 0x81 +#define _DS3E 0xFE + +#elif _CODE_PAGE == 950 /* CP950 (Traditional Chinese Big5) */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0xA1 +#define _DS2E 0xFE + +#else /* SBCS code pages */ +#define _DF1S 0 + +#endif + + + +/* Character code support macros */ + +#define IsUpper(c) (((c)>='A')&&((c)<='Z')) +#define IsLower(c) (((c)>='a')&&((c)<='z')) +#define IsDigit(c) (((c)>='0')&&((c)<='9')) + +#if _DF1S /* DBCS configuration */ + +#if _DF2S /* Two 1st byte areas */ +#define IsDBCS1(c) (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E)) +#else /* One 1st byte area */ +#define IsDBCS1(c) ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) +#endif + +#if _DS3S /* Three 2nd byte areas */ +#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E)) +#else /* Two 2nd byte areas */ +#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E)) +#endif + +#else /* SBCS configuration */ + +#define IsDBCS1(c) 0 +#define IsDBCS2(c) 0 + +#endif /* _DF1S */ + + + /* Definitions corresponds to multi partition */ -#if _MULTI_PARTITION != 0 /* Multiple partition cfg */ +#if _MULTI_PARTITION /* Multiple partition configuration */ typedef struct _PARTITION { - BYTE pd; /* Physical drive # (0-255) */ + BYTE pd; /* Physical drive# */ BYTE pt; /* Partition # (0-3) */ } PARTITION; + extern const PARTITION Drives[]; /* Logical drive# to physical location conversion table */ #define LD2PD(drv) (Drives[drv].pd) /* Get physical drive# */ #define LD2PT(drv) (Drives[drv].pt) /* Get partition# */ -#else /* Single partition cfg */ +#else /* Single partition configuration */ -#define LD2PD(drv) (drv) /* Physical drive# is equal to logical drive# */ -#define LD2PT(drv) 0 /* Always mounts the 1st partition */ +#define LD2PD(drv) (drv) /* Physical drive# is equal to the logical drive# */ +#define LD2PT(drv) 0 /* Always mounts the 1st partition */ #endif + /* File function return code (FRESULT) */ typedef enum { FR_OK = 0, /* 0 */ - FR_NOT_READY, /* 1 */ - FR_NO_FILE, /* 2 */ - FR_NO_PATH, /* 3 */ - FR_INVALID_NAME, /* 4 */ - FR_INVALID_DRIVE, /* 5 */ - FR_DENIED, /* 6 */ - FR_EXIST, /* 7 */ - FR_RW_ERROR, /* 8 */ - FR_WRITE_PROTECTED, /* 9 */ - FR_NOT_ENABLED, /* 10 */ - FR_NO_FILESYSTEM, /* 11 */ - FR_INVALID_OBJECT, /* 12 */ - FR_MKFS_ABORTED /* 13 */ + FR_DISK_ERR, /* 1 */ + FR_INT_ERR, /* 2 */ + FR_NOT_READY, /* 3 */ + FR_NO_FILE, /* 4 */ + FR_NO_PATH, /* 5 */ + FR_INVALID_NAME, /* 6 */ + FR_DENIED, /* 7 */ + FR_EXIST, /* 8 */ + FR_INVALID_OBJECT, /* 9 */ + FR_WRITE_PROTECTED, /* 10 */ + FR_INVALID_DRIVE, /* 11 */ + FR_NOT_ENABLED, /* 12 */ + FR_NO_FILESYSTEM, /* 13 */ + FR_MKFS_ABORTED, /* 14 */ + FR_TIMEOUT /* 15 */ } FRESULT; -/*-----------------------------------------------------*/ -/* FatFs module application interface */ +/*--------------------------------------------------------------*/ +/* FatFs module application interface */ FRESULT f_mount (BYTE, FATFS*); /* Mount/Unmount a logical drive */ FRESULT f_open (FIL*, const char*, BYTE); /* Open or create a file */ @@ -208,24 +377,43 @@ FRESULT f_truncate (FIL*); /* Truncate file */ FRESULT f_sync (FIL*); /* Flush cached data of a writing file */ FRESULT f_unlink (const char*); /* Delete an existing file or directory */ FRESULT f_mkdir (const char*); /* Create a new directory */ -FRESULT f_chmod (const char*, BYTE, BYTE); /* Change file/dir attriburte */ -FRESULT f_utime (const char*, const FILINFO*); /* Change file/dir timestamp */ +FRESULT f_chmod (const char*, BYTE, BYTE); /* Change attriburte of the file/dir */ +FRESULT f_utime (const char*, const FILINFO*); /* Change timestamp of the file/dir */ FRESULT f_rename (const char*, const char*); /* Rename/Move a file or directory */ +FRESULT f_forward (FIL*, UINT(*)(const BYTE*,UINT), UINT, UINT*); /* Forward data to the stream */ FRESULT f_mkfs (BYTE, BYTE, WORD); /* Create a file system on the drive */ + #if _USE_STRFUNC -#define feof(fp) ((fp)->fptr == (fp)->fsize) +int f_putc (int, FIL*); /* Put a character to the file */ +int f_puts (const char*, FIL*); /* Put a string to the file */ +int f_printf (FIL*, const char*, ...); /* Put a formatted string to the file */ +char* f_gets (char*, int, FIL*); /* Get a string from the file */ +#define f_eof(fp) (((fp)->fptr == (fp)->fsize) ? 1 : 0) +#define f_error(fp) (((fp)->flag & FA__ERROR) ? 1 : 0) +#ifndef EOF #define EOF -1 -int fputc (int, FIL*); /* Put a character to the file */ -int fputs (const char*, FIL*); /* Put a string to the file */ -int fprintf (FIL*, const char*, ...); /* Put a formatted string to the file */ -char* fgets (char*, int, FIL*); /* Get a string from the file */ +#endif #endif -/* User defined function to give a current time to fatfs module */ +/*--------------------------------------------------------------*/ +/* User defined functions */ + + +/* Real time clock */ DWORD get_fattime (void); /* 31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */ /* 15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */ +/* Unicode - OEM code conversion */ +#if _USE_LFN +WCHAR ff_convert (WCHAR, UINT); +#endif + + + + +/*--------------------------------------------------------------*/ +/* Flags and offset address */ /* File access control and file status flags (FIL.flag) */ @@ -259,10 +447,12 @@ DWORD get_fattime (void); /* 31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20 #define AM_LFN 0x0F /* LFN entry */ #define AM_DIR 0x10 /* Directory */ #define AM_ARC 0x20 /* Archive */ +#define AM_MASK 0x3F /* Mask of defined bits */ - -/* Offset of FAT structure members */ +/* FatFs refers the members in the FAT structures with byte offset instead +/ of structure member because there are incompatibility of the packing option +/ between various compilers. */ #define BS_jmpBoot 0 #define BS_OEMName 3 @@ -315,25 +505,28 @@ DWORD get_fattime (void); /* 31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20 #define DIR_WrtDate 24 #define DIR_FstClusLO 26 #define DIR_FileSize 28 +#define LDIR_Ord 0 +#define LDIR_Attr 11 +#define LDIR_Type 12 +#define LDIR_Chksum 13 +#define LDIR_FstClusLO 26 +/*--------------------------------*/ /* Multi-byte word access macros */ -#if _MCU_ENDIAN == 1 /* Use word access */ +#if _WORD_ACCESS == 1 /* Enable word access to the FAT structure */ #define LD_WORD(ptr) (WORD)(*(WORD*)(BYTE*)(ptr)) #define LD_DWORD(ptr) (DWORD)(*(DWORD*)(BYTE*)(ptr)) #define ST_WORD(ptr,val) *(WORD*)(BYTE*)(ptr)=(WORD)(val) #define ST_DWORD(ptr,val) *(DWORD*)(BYTE*)(ptr)=(DWORD)(val) -#elif _MCU_ENDIAN == 2 /* Use byte-by-byte access */ +#else /* Use byte-by-byte access to the FAT structure */ #define LD_WORD(ptr) (WORD)(((WORD)*(volatile BYTE*)((ptr)+1)<<8)|(WORD)*(volatile BYTE*)(ptr)) #define LD_DWORD(ptr) (DWORD)(((DWORD)*(volatile BYTE*)((ptr)+3)<<24)|((DWORD)*(volatile BYTE*)((ptr)+2)<<16)|((WORD)*(volatile BYTE*)((ptr)+1)<<8)|*(volatile BYTE*)(ptr)) #define ST_WORD(ptr,val) *(volatile BYTE*)(ptr)=(BYTE)(val); *(volatile BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8) #define ST_DWORD(ptr,val) *(volatile BYTE*)(ptr)=(BYTE)(val); *(volatile BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8); *(volatile BYTE*)((ptr)+2)=(BYTE)((DWORD)(val)>>16); *(volatile BYTE*)((ptr)+3)=(BYTE)((DWORD)(val)>>24) -#else -#error Do not forget to set _MCU_ENDIAN properly! #endif -#define _FATFS #endif /* _FATFS */ diff --git a/integer.h b/integer.h index 8c70183..1d6bac3 100644 --- a/integer.h +++ b/integer.h @@ -4,6 +4,10 @@ #ifndef _INTEGER +#if 0 +#include +#else + /* These types must be 16-bit, 32-bit or larger integer */ typedef int INT; typedef unsigned int UINT; @@ -17,6 +21,7 @@ typedef unsigned char BYTE; typedef short SHORT; typedef unsigned short USHORT; typedef unsigned short WORD; +typedef unsigned short WCHAR; /* These types must be 32-bit integer */ typedef long LONG; @@ -26,5 +31,7 @@ typedef unsigned long DWORD; /* Boolean type */ typedef enum { FALSE = 0, TRUE } BOOL; +#endif + #define _INTEGER #endif diff --git a/lcd.c b/lcd.c deleted file mode 100644 index 796fdce..0000000 --- a/lcd.c +++ /dev/null @@ -1,208 +0,0 @@ -#include "types.h" -#include "lcd.h" -#include "start.h" -#include "vsprintf.h" -#include "string.h" -#include "utils.h" -#include "hollywood.h" - -void lcdinit(void); - -char ddram[0x80]; -u8 ddaddr = 0; -u32 chardelay = 0; - -#define WIDTH 16 -#define HEIGHT 2 - -u8 linestarts[HEIGHT] = { 0x00, 0x40 }; - -u8 cx, cy; - -// for 4x20 -//u8 linestarts[HEIGHT] = { 0x00, 0x40, 0x14, 0x54 }; - -#define LCD_HOME_CLEAR 0x01 -#define LCD_HOME 0x02 -#define LCD_ENTRY_MODE 0x04 -# define EM_INC 0x02 -# define EM_DEC 0x00 -# define EM_SHIFT 0x01 -#define LCD_DISPLAY_ONOFF 0x08 -# define D_DISPLAY 0x04 -# define D_CURSOR 0x02 -# define D_BLINK 0x01 -#define LCD_SHIFT 0x10 -# define SH_SHIFT 0x08 -# define SH_CURSOR 0x00 -# define SH_RIGHT 0x04 -# define SH_LEFT 0x00 -#define LCD_FUNCTION_SET 0x20 -# define FS_8BIT 0x10 -# define FS_4BIT 0x00 -# define FS_2LINE 0x08 -# define FS_1LINE 0x00 -# define FS_5X10 0x04 -# define FS_5X8 0x00 -#define LCD_CGRAM 0x40 -#define LCD_DDRAM 0x80 - -#define LCD_PORT HW_GPIO1OUT -#define LCD_MASK 0x00FC0000 -#define LCD_E 0x00040000 -#define LCD_RS 0x00080000 -#define LCD_DSHIFT 20 - -static void _lcdnybble(char rs, char n) -{ - if(rs) - mask32(LCD_PORT, LCD_MASK, ((n&0xF) << LCD_DSHIFT) | LCD_RS); - else - mask32(LCD_PORT, LCD_MASK, ((n&0xF) << LCD_DSHIFT)); - - udelay(4); - set32(LCD_PORT, LCD_E); - udelay(4); - clear32(LCD_PORT, LCD_E); - udelay(4); -} - -static void _lcdcmd(char c) -{ - _lcdnybble(0, c>>4); - _lcdnybble(0, c&0x0F); - udelay(50); -} - -static void _lcdwrite(char c) -{ - _lcdnybble(1, c>>4); - _lcdnybble(1, c&0x0F); - ddram[ddaddr] = c; - ddaddr = (ddaddr+1)&0x7F; - udelay(50); -} - -static void _lcdgoto(u8 addr) -{ - _lcdcmd(LCD_DDRAM | addr); - ddaddr = addr; -} - -static void _lcdgotoxy(u8 x, u8 y) -{ - u8 addr = (x + linestarts[y]); - _lcdcmd(LCD_DDRAM | addr); - ddaddr = addr; -} - -static void _lcdclear(void) -{ - int i; - _lcdcmd(LCD_HOME_CLEAR); - udelay(2000); - for(i=0;i<0x80;i++) { - ddram[i] = ' '; - } -} - -static void _lcdscroll(u8 count) -{ - int x,y; - _lcdcmd(LCD_HOME_CLEAR); - udelay(2000); - for(y=0;y<(HEIGHT-count);y++) { - _lcdgotoxy(0,y); - for(x=0;x>4); - udelay(10000); - _lcdnybble(0, (LCD_FUNCTION_SET | FS_8BIT)>>4); - udelay(1000); - _lcdnybble(0, (LCD_FUNCTION_SET | FS_8BIT)>>4); - udelay(1000); - _lcdnybble(0, (LCD_FUNCTION_SET | FS_4BIT)>>4); - udelay(1000); - _lcdcmd(LCD_FUNCTION_SET | FS_4BIT | FS_2LINE | FS_5X8); - _lcdcmd(LCD_DISPLAY_ONOFF); - _lcdcmd(LCD_HOME_CLEAR); - udelay(2000); - _lcdcmd(LCD_ENTRY_MODE | EM_INC); - _lcdcmd(LCD_DISPLAY_ONOFF | D_DISPLAY | D_CURSOR); -} - -int lcd_putchar(int ic) -{ - char c = ic; - // defer newlines to keep last line of LCD full - if(c == '\n') { - cx = 0; - cy++; - if(cy < HEIGHT) - _lcdgotoxy(cx,cy); - } else { - if(cx >= 16) { - cx = 0; - cy++; - if(cy < HEIGHT) - _lcdgotoxy(cx,cy); - } - if(cy >= (2*HEIGHT-1)) { - _lcdclear(); - _lcdgoto(linestarts[HEIGHT-1]); - cy = HEIGHT - 1; - cx = 0; - } else if(cy >= HEIGHT) { - _lcdscroll(cy - HEIGHT + 1); - cy = HEIGHT - 1; - cx = 0; - } - _lcdwrite(c); - cx++; - } - return (u8)c; -} - -void lcd_setdelay(u32 delay) -{ - chardelay = delay; -} - -int lcd_puts(const char *s) -{ - while(*s) { - lcd_putchar(*s++); - udelay(chardelay); - } - return 0; -} - -int lcd_printf( const char *fmt, ...) -{ - va_list args; - char buffer[1024]; - int i; - - va_start(args, fmt); - i = vsprintf(buffer, fmt, args); - va_end(args); - lcd_puts(buffer); - return i; -} diff --git a/lcd.h b/lcd.h deleted file mode 100644 index 4303b76..0000000 --- a/lcd.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef __LCD_H__ -#define __LCD_H__ - -#include "types.h" - -void lcd_init(void); -int lcd_putchar(int c); -int lcd_puts(const char *s); -void lcd_setdelay(u32 delay); -int lcd_printf( const char *fmt, ...); -#endif