diff --git a/pcb/cart/sd2snes17.brd b/pcb/cart/sd2snes17.brd index 9a396b2..9ea66e4 100644 Binary files a/pcb/cart/sd2snes17.brd and b/pcb/cart/sd2snes17.brd differ diff --git a/pcb/cart/sd2snes17.sch b/pcb/cart/sd2snes17.sch index 3bd4abd..1ce202a 100644 Binary files a/pcb/cart/sd2snes17.sch and b/pcb/cart/sd2snes17.sch differ diff --git a/src/Makefile b/src/Makefile index 4979872..694511f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -100,7 +100,7 @@ FORMAT = ihex TARGET = $(OBJDIR)/sd2snes # List C source files here. (C dependencies are automatically generated.) -SRC = main.c ff.c utils.c timer.c led.c diskio.c sdcard.c spi.c crc7.c snes.c fpga.c memory.c crc16.c fileops.c fpga_spi.c filetypes.c +SRC = main.c ff.c utils.c timer.c led.c diskio.c sdcard.c spi.c crc7.c snes.c fpga.c memory.c crc16.c fileops.c fpga_spi.c smc.c filetypes.c ifeq ($(CONFIG_UART_DEBUG),y) SRC += uart.c @@ -119,8 +119,8 @@ ASRC = # 0 = turn off optimization. s = optimize for size. # (Note: 3 is not always the best optimization level. See avr-libc FAQ.) # Use s -mcall-prologues when you really need size... -#OPT = 2 -OPT = 3 -finline-functions +OPT = s +#OPT = 3 -finline-functions # Debugging format. # Native formats for AVR-GCC's -g are dwarf-2 [default] or stabs. diff --git a/src/config b/src/config index a098d1f..6bae32e 100644 --- a/src/config +++ b/src/config @@ -35,7 +35,7 @@ CONFIG_UART_BAUDRATE=38400 CONFIG_UART_BUF_SHIFT=7 CONFIG_HARDWARE_NAME=sd2snes CONFIG_SD_AUTO_RETRIES=10 -#CONFIG_SD_DATACRC=y +CONFIG_SD_DATACRC=y CONFIG_EEPROM_SIZE=512 CONFIG_EEPROM_OFFSET=512 -CONFIG_MAX_PARTITIONS=2 +CONFIG_MAX_PARTITIONS=1 diff --git a/src/crc16.c b/src/crc16.c index 21db530..056d75b 100644 --- a/src/crc16.c +++ b/src/crc16.c @@ -2,59 +2,20 @@ * \file stdout * Functions and types for CRC checks. * - * Generated on Tue Sep 15 09:32:35 2009, + * Generated on Tue Jun 30 23:02:59 2009, * by pycrc v0.7.1, http://www.tty1.net/pycrc/ * using the configuration: * Width = 16 * Poly = 0x8005 - * XorIn = 0xffff + * XorIn = 0x0000 * ReflectIn = True - * XorOut = 0xffff + * XorOut = 0x0000 * ReflectOut = True - * Algorithm = table-driven + * Algorithm = bit-by-bit-fast * Direct = True *****************************************************************************/ -#include "crc16.h" #include - -/** - * Static table used for the table_driven implementation. - *****************************************************************************/ -static const crc_t crc_table[256] = { - 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, - 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, - 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, - 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, - 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, - 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, - 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, - 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, - 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, - 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, - 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, - 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, - 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, - 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, - 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, - 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, - 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, - 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, - 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, - 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, - 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, - 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, - 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, - 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, - 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, - 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, - 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, - 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, - 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, - 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, - 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, - 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040 -}; - +#include "crc16.h" /** * Update the crc value with new data. * @@ -65,14 +26,26 @@ static const crc_t crc_table[256] = { *****************************************************************************/ crc_t crc16_update(crc_t crc, const unsigned char *data, size_t data_len) { - unsigned int tbl_idx; + unsigned int i; + uint8_t bit; + unsigned char c; while (data_len--) { - tbl_idx = (crc ^ *data) & 0xff; - crc = (crc_table[tbl_idx] ^ (crc >> 8)) & 0xffff; - - data++; + c = *data++; + for (i = 0x01; i & 0xff; i <<= 1) { + bit = (crc & 0x8000 ? 1 : 0); + if (c & i) { + bit ^= 1; + } + crc <<= 1; + if (bit) { + crc ^= 0x8005; + } + } + crc &= 0xffff; } return crc & 0xffff; } + + diff --git a/src/ff.c b/src/ff.c index 5b38038..da8ec95 100644 --- a/src/ff.c +++ b/src/ff.c @@ -1,2936 +1,3036 @@ -/*----------------------------------------------------------------------------/ -/ FatFs - FAT file system module R0.07a (C)ChaN, 2009 -/-----------------------------------------------------------------------------/ -/ 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 commercial developments under license policy of following trems. -/ -/ Copyright (C) 2009, ChaN, all right reserved. -/ -/ * 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. -//-----------------------------------------------------------------------------/ -/ Feb 26,'06 R0.00 Prototype. -/ -/ Apr 29,'06 R0.01 First stable version. -/ -/ Jun 01,'06 R0.02 Added FAT12 support. -/ Removed unbuffered mode. -/ Fixed a problem on small (<32M) patition. -/ Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM). -/ -/ Sep 22,'06 R0.03 Added f_rename(). -/ Changed option _FS_MINIMUM to _FS_MINIMIZE. -/ Dec 11,'06 R0.03a Improved cluster scan algolithm to write files fast. -/ Fixed f_mkdir() creates incorrect directory on FAT32. -/ -/ Feb 04,'07 R0.04 Supported multiple drive system. -/ Changed some interfaces for multiple drive system. -/ Changed f_mountdrv() to f_mount(). -/ Added f_mkfs(). -/ Apr 01,'07 R0.04a Supported multiple partitions on a plysical drive. -/ Added a capability of extending file size to f_lseek(). -/ Added minimization level 3. -/ Fixed an endian sensitive code in f_mkfs(). -/ May 05,'07 R0.04b Added a configuration option _USE_NTFLAG. -/ Added FSInfo support. -/ Fixed DBCS name can result FR_INVALID_NAME. -/ Fixed short seek (<= csize) collapses the file object. -/ -/ Aug 25,'07 R0.05 Changed arguments of f_read(), f_write() and f_mkfs(). -/ Fixed f_mkfs() on FAT32 creates incorrect FSInfo. -/ Fixed f_mkdir() on FAT32 creates incorrect directory. -/ Feb 03,'08 R0.05a Added f_truncate() and f_utime(). -/ Fixed off by one error at FAT sub-type determination. -/ Fixed btr in f_read() can be mistruncated. -/ Fixed cached sector is not flushed when create and close -/ without write. -/ -/ 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. -/ Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg. -/ Added multiple sector size support. -/---------------------------------------------------------------------------*/ - -#include "ff.h" /* FatFs configurations and declarations */ -#include "diskio.h" /* Declarations of low level disk I/O functions */ - - -/*-------------------------------------------------------------------------- - - Module Private Definitions - ----------------------------------------------------------------------------*/ - -#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); } - -#ifndef NULL -#define NULL 0 -#endif - - -/*-------------------------------------------------------------------------- - - 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 - - - - -/*-------------------------------------------------------------------------- - - Private Functions - ----------------------------------------------------------------------------*/ - - -/*-----------------------------------------------------------------------*/ -/* String functions */ -/*-----------------------------------------------------------------------*/ - -/* Copy memory to memory */ -static -void mem_cpy (void* dst, const void* src, int cnt) { - char *d = (char*)dst; - const char *s = (const char *)src; - while (cnt--) *d++ = *s++; -} - -/* Fill memory */ -static -void mem_set (void* dst, int val, int cnt) { - char *d = (char*)dst; - while (cnt--) *d++ = (char)val; -} - -/* Compare memory to memory */ -static -int mem_cmp (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++) == 0) ; - return r; -} - -/* Check if chr is contained in the string */ -static -int chk_chr (const char* str, int chr) { - while (*str && *str != chr) str++; - return *str; -} - - - -/*-----------------------------------------------------------------------*/ -/* Request/Release grant to access the volume */ -/*-----------------------------------------------------------------------*/ -#if _FS_REENTRANT - -static -BOOL lock_fs ( - FATFS *fs /* File system object */ -) -{ - return ff_req_grant(fs->sobj); -} - - -static -void unlock_fs ( - FATFS *fs, /* File system object */ - FRESULT res /* Result code to be returned */ -) -{ - if (res != FR_NOT_ENABLED && - res != FR_INVALID_DRIVE && - res != FR_INVALID_OBJECT && - res != FR_TIMEOUT) { - ff_rel_grant(fs->sobj); - } -} -#endif - - - -/*-----------------------------------------------------------------------*/ -/* Change window offset */ -/*-----------------------------------------------------------------------*/ - -static -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 */ -{ - DWORD wsect; - - - wsect = fs->winsect; - if (wsect != sector) { /* Changed current window */ -#if !_FS_READONLY - if (fs->wflag) { /* Write back dirty window if needed */ - if (disk_write(fs->drive, fs->win, wsect, 1) != RES_OK) - return FR_DISK_ERR; - fs->wflag = 0; - if (wsect < (fs->fatbase + fs->sects_fat)) { /* In FAT area */ - 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); - } - } - } -#endif - if (sector) { - if (disk_read(fs->drive, fs->win, sector, 1) != RES_OK) - return FR_DISK_ERR; - fs->winsect = sector; - } - } - - return FR_OK; -} - - - - -/*-----------------------------------------------------------------------*/ -/* Clean-up cached data */ -/*-----------------------------------------------------------------------*/ -#if !_FS_READONLY -static -FRESULT sync ( /* FR_OK: successful, FR_DISK_ERR: failed */ - FATFS *fs /* File system object */ -) -{ - 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; - mem_set(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; - } - - return res; -} -#endif - - - - -/*-----------------------------------------------------------------------*/ -/* Get a cluster status */ -/*-----------------------------------------------------------------------*/ - -static -DWORD get_cluster ( /* 0xFFFFFFFF:Disk error, 1:Interal error, Else:Cluster status */ - FATFS *fs, /* File system object */ - DWORD clst /* Cluster# to get the link information */ -) -{ - WORD wc, bc; - DWORD fsect; - - - if (clst < 2 || clst >= fs->max_clust) /* Check cluster address range */ - return 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_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 0xFFFFFFFF; /* An error occured at the disk I/O layer */ -} - - - - -/*-----------------------------------------------------------------------*/ -/* Change a cluster status */ -/*-----------------------------------------------------------------------*/ -#if !_FS_READONLY -static -FRESULT put_cluster ( - FATFS *fs, /* File system object */ - 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 fsect; - FRESULT res; - - - if (clst < 2 || clst >= fs->max_clust) { /* Check cluster address range */ - res = FR_INT_ERR; - - } 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_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; - - 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; - } - - return res; -} -#endif /* !_FS_READONLY */ - - - - -/*-----------------------------------------------------------------------*/ -/* Remove a cluster chain */ -/*-----------------------------------------------------------------------*/ -#if !_FS_READONLY -static -FRESULT remove_chain ( - FATFS *fs, /* File system object */ - DWORD clst /* Cluster# to remove chain from */ -) -{ - FRESULT res; - DWORD nxt; - - - 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 */ - } - } - - return res; -} -#endif - - - - -/*-----------------------------------------------------------------------*/ -/* Stretch or create a cluster chain */ -/*-----------------------------------------------------------------------*/ -#if !_FS_READONLY -static -DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ - FATFS *fs, /* File system object */ - DWORD clst /* Cluster# to stretch. 0 means create a new chain. */ -) -{ - DWORD cs, ncl, scl, mcl; - - - 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 */ - 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 */ - for (;;) { - ncl++; /* Next cluster */ - if (ncl >= mcl) { /* Wrap around */ - ncl = 2; - if (ncl > scl) return 0; /* No free custer */ - } - 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)) /* 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 */ - if (fs->free_clust != 0xFFFFFFFF) { - fs->free_clust--; - fs->fsi_flag = 1; - } - - return ncl; /* Return new cluster number */ -} -#endif /* !_FS_READONLY */ - - - - -/*-----------------------------------------------------------------------*/ -/* Get sector# from cluster# */ -/*-----------------------------------------------------------------------*/ - -static -DWORD clust2sect ( /* !=0: sector number, 0: failed - invalid cluster# */ - FATFS *fs, /* File system object */ - DWORD clst /* Cluster# to be converted */ -) -{ - clst -= 2; - if (clst >= (fs->max_clust - 2)) return 0; /* Invalid cluster# */ - return clst * fs->csize + fs->database; -} - - - - -/*-----------------------------------------------------------------------*/ -/* Seek directory index */ -/*-----------------------------------------------------------------------*/ - -static -FRESULT dir_seek ( - DIR *dj, /* Pointer to directory object */ - WORD idx /* Directory index number */ -) -{ - DWORD clst; - WORD ic; - - - 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 */ - mem_set(dj->fs->win, 0, SS(dj->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 = 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; -} - - -#if !_FS_READONLY -static -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 */ -) -{ - 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; - - - mem_cpy(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, lfen, ord, sum, *dir; - - - res = dir_seek(dj, 0); /* Rewind directory object */ - if (res != FR_OK) return res; - - ord = sum = 0xFF; lfen = *(dj->fn+11) & 1; - 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] && (!lfen || 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 (lfen) { /* 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 (!mem_cmp(dir, dj->fn, 11)) break; - } - } - } -#else /* Non LFN configuration */ - if (c != 0xE5 && c != '.' && !(a & AM_VOL) && !mem_cmp(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; - mem_cpy(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_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; - mem_set(dir, 0, 32); /* Clean the entry */ - mem_cpy(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 (chk_chr("\"*:<>\?|\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; - mem_set(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 (chk_chr("+,;[=]", 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; - mem_set(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 (chk_chr(" +,;[=]\"*:<>\?|\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 = 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 (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; - -#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 */ - - - - -/*-----------------------------------------------------------------------*/ -/* Follow a file path */ -/*-----------------------------------------------------------------------*/ - -static -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 */ -) -{ - FRESULT res; - BYTE *dir, last; - - - 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; - - } else { /* Follow path */ - for (;;) { - res = create_name(dj, &path); /* Get a segment */ - if (res != FR_OK) break; - res = dir_find(dj); /* Find it */ - last = *(dj->fn+11) & 4; - if (res != FR_OK) { /* Could not find the object */ - if (res == FR_NO_FILE && !last) - res = FR_NO_PATH; - break; - } - if (last) 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); - } - } - - return res; -} - - - - -/*-----------------------------------------------------------------------*/ -/* 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, 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 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 (!mem_cmp(&fs->win[BS_FilSysType], "FAT", 3)) /* Check FAT signature */ - return 0; - if (!mem_cmp(&fs->win[BS_FilSysType32], "FAT32", 5) && !(fs->win[BPB_ExtFlags] & 0x80)) - return 0; - - return 1; -} - - - - -/*-----------------------------------------------------------------------*/ -/* Make sure that the file system is valid */ -/*-----------------------------------------------------------------------*/ - -static -FRESULT auto_mount ( /* FR_OK(0): successful, !=0: any error occured */ - const char **path, /* Pointer to pointer to the path name (drive number) */ - FATFS **rfs, /* Pointer to pointer to the found file system object */ - BYTE chk_wp /* !=0: Check media write protection for write access */ -) -{ - FRESULT res; - BYTE vol, fmt, *tbl; - DSTATUS stat; - DWORD bsect, fsize, tsect, mclst; - const char *p = *path; - FATFS *fs; - - - /* Get logical drive number from the path name */ - vol = p[0] - '0'; /* Is there a drive number? */ - if (vol <= 9 && p[1] == ':') { - p += 2; /* Found a drive number, get and strip it */ - *path = p; /* Return pointer to the path name */ - } else { - vol = 0; /* No drive number is given, use drive number 0 as default */ - } - - /* Check if the logical drive number is valid or not */ - if (vol >= _DRIVES) return FR_INVALID_DRIVE; /* Is the drive number valid? */ - *rfs = fs = FatFs[vol]; /* 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), */ -#if !_FS_READONLY - if (chk_wp && (stat & STA_PROTECT)) /* Check write protection if needed */ - return FR_WRITE_PROTECTED; -#endif - return FR_OK; /* The file system object is valid */ - } - } - - /* The logical drive must be re-mounted. Following code attempts to mount the volume */ - - fs->fs_type = 0; /* Clear the file system object */ - fs->drive = LD2PD(vol); /* 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 */ - return FR_NOT_READY; -#if _MAX_SS != 512 /* Get disk sector size if needed */ - if (disk_ioctl(fs->drive, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS) - return FR_NO_FILESYSTEM; -#endif -#if !_FS_READONLY - if (chk_wp && (stat & STA_PROTECT)) /* Check write protection if needed */ - return FR_WRITE_PROTECTED; -#endif - /* Search FAT partition on the drive */ - 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(vol) * 16]; /* Partition table */ - if (tbl[4]) { /* Is the partition existing? */ - bsect = LD_DWORD(&tbl[8]); /* Partition offset in LBA */ - fmt = check_fs(fs, bsect); /* Check the partition */ - } - } - 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 */ - 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 */ - 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 */ - 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 (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 */ - else - 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; - fs->wflag = 0; - /* Get fsinfo if needed */ - if (fmt == FS_FAT32) { - 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); - } - } -#endif - fs->winsect = 0; - fs->fs_type = fmt; /* FAT syb-type */ - fs->id = ++Fsid; /* File system mount ID */ - res = FR_OK; - - return res; -} - - - - -/*-----------------------------------------------------------------------*/ -/* Check if the file/dir object is valid or not */ -/*-----------------------------------------------------------------------*/ - -static -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; - - return FR_OK; -} - - - - -/*-------------------------------------------------------------------------- - - Public Functions - ---------------------------------------------------------------------------*/ - - - -/*-----------------------------------------------------------------------*/ -/* Mount/Unmount a Locical Drive */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_mount ( - BYTE vol, /* Logical drive number to be mounted/unmounted */ - FATFS *fs /* Pointer to new file system object (NULL for unmount)*/ -) -{ - FATFS *rfs; - - - if (vol >= _DRIVES) /* Check if the drive number is valid */ - return FR_INVALID_DRIVE; - rfs = FatFs[vol]; /* Get current state */ - - if (rfs) { -#if _FS_REENTRANT /* Discard sync object of the current volume */ - if (!ff_del_syncobj(fs->sobj)) return FR_INT_ERR; -#endif - rfs->fs_type = 0; /* Clear old fs object */ - } - - if (fs) { - fs->fs_type = 0; /* Clear new fs object */ -#if _FS_REENTRANT /* Create sync object for the new volume */ - if (!ff_cre_syncobj(vol, &fs->sobj)) return FR_INT_ERR; -#endif - } - FatFs[vol] = fs; /* Register new fs object */ - - return FR_OK; -} - - - - -/*-----------------------------------------------------------------------*/ -/* Open or Create a File */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_open ( - FIL *fp, /* Pointer to the blank file object */ - const char *path, /* Pointer to the file name */ - BYTE mode /* Access mode and file open mode flags */ -) -{ - FRESULT res; - DIR dj; - NAMEBUF(sfn, lfn); - BYTE *dir; - - - 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))); -#else - mode &= FA_READ; - res = auto_mount(&path, &dj.fs, 0); -#endif - 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, cl; - - if (res != FR_OK) { /* No file, create new */ - 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 */ - 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 */ - 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 (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->wflag = 1; - mode |= FA__WRITTEN; /* Set file changed flag */ - } - } - /* Open an existing file */ - else { -#endif /* !_FS_READONLY */ - 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 */ - LEAVE_FF(dj.fs, FR_NO_FILE); -#if !_FS_READONLY - if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */ - LEAVE_FF(dj.fs, FR_DENIED); - } - fp->dir_sect = dj.fs->winsect; /* Pointer to the directory entry */ - 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 */ - fp->fptr = 0; fp->csect = 255; /* File pointer */ - fp->dsect = 0; - fp->fs = dj.fs; fp->id = dj.fs->id; /* Owner file system object of the file */ - - LEAVE_FF(dj.fs, FR_OK); -} - - - - -/*-----------------------------------------------------------------------*/ -/* Read File */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_read ( - FIL *fp, /* Pointer to the file object */ - void *buff, /* Pointer to data buffer */ - UINT btr, /* Number of bytes to read */ - UINT *br /* Pointer to number of bytes read */ -) -{ - FRESULT res; - 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) 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 */ - - for ( ; btr; /* Repeat until all data transferred */ - 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? */ - 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 offset in the cluster */ - } - 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) - 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 !_FS_TINY -#if !_FS_READONLY - 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 data from sector buffer */ - if (rcnt > btr) rcnt = btr; -#if _FS_TINY - if (move_window(fp->fs, fp->dsect)) /* Move sector window */ - ABORT(fp->fs, FR_DISK_ERR); - mem_cpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ -#else - mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ -#endif - } - - - LEAVE_FF(fp->fs, FR_OK); -} - - - - -#if !_FS_READONLY -/*-----------------------------------------------------------------------*/ -/* Write File */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_write ( - FIL *fp, /* Pointer to the file object */ - const void *buff, /* Pointer to the data to be written */ - UINT btw, /* Number of bytes to write */ - UINT *bw /* Pointer to number of bytes written */ -) -{ - FRESULT res; - 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) 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? */ - 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 */ - clst = create_chain(fp->fs, fp->curr_clust); /* Follow or streach cluster chain */ - } - 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 */ - } -#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) - 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 _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; -#if _FS_TINY - if (move_window(fp->fs, fp->dsect)) /* Move sector window */ - ABORT(fp->fs, FR_DISK_ERR); - mem_cpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ - fp->fs->wflag = 1; -#else - mem_cpy(&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 */ - - LEAVE_FF(fp->fs, FR_OK); -} - - - - -/*-----------------------------------------------------------------------*/ -/* Synchronize the File Object */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_sync ( - FIL *fp /* Pointer to the file object */ -) -{ - FRESULT res; - DWORD tim; - BYTE *dir; - - - 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? */ -#if !_FS_TINY /* Write-back dirty buffer */ - if (fp->flag & FA__DIRTY) { - 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 */ - 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); - } - } - } - - LEAVE_FF(fp->fs, res); -} - -#endif /* !_FS_READONLY */ - - - - -/*-----------------------------------------------------------------------*/ -/* Close File */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_close ( - FIL *fp /* Pointer to the file object to be closed */ -) -{ - FRESULT res; - - -#if _FS_READONLY - res = validate(fp->fs, fp->id); - 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 -} - - - - -#if _FS_MINIMIZE <= 2 -/*-----------------------------------------------------------------------*/ -/* Seek File R/W Pointer */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_lseek ( - FIL *fp, /* Pointer to the file object */ - DWORD ofs /* File pointer from top of file */ -) -{ - FRESULT res; - DWORD clst, bcs, nsect, ifptr; - - - 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 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) -#endif - ) ofs = fp->fsize; - - ifptr = fp->fptr; - fp->fptr = 0; fp->csect = 255; - nsect = 0; - if (ofs > 0) { - bcs = (DWORD)fp->fs->csize * SS(fp->fs); /* Cluster size (byte) */ - if (ifptr > 0 && - (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; - clst = fp->curr_clust; - } else { /* When seek to back cluster, */ - clst = fp->org_clust; /* start from the first cluster */ -#if !_FS_READONLY - 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 = clst; - } - if (clst != 0) { - while (ofs > bcs) { /* Cluster following loop */ -#if !_FS_READONLY - if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ - 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 - 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)) { - nsect = clust2sect(fp->fs, clst); /* Current sector */ - if (!nsect) ABORT(fp->fs, FR_INT_ERR); - nsect += fp->csect; - fp->csect++; - } - } - } - 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->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->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 size is extended */ - fp->fsize = fp->fptr; - fp->flag |= FA__WRITTEN; - } -#endif - - LEAVE_FF(fp->fs, res); -} - - - - -#if _FS_MINIMIZE <= 1 -/*-----------------------------------------------------------------------*/ -/* Create a Directroy Object */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_opendir ( - DIR *dj, /* Pointer to directory object to create */ - const char *path /* Pointer to the directory path */ -) -{ - FRESULT res; - NAMEBUF(sfn, lfn); - BYTE *dir; - - - res = auto_mount(&path, &dj->fs, 0); - if (res == FR_OK) { - 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 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; - } - } - - LEAVE_FF(dj->fs, res); -} - - - - -/*-----------------------------------------------------------------------*/ -/* Read Directory Entry in Sequense */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_readdir ( - DIR *dj, /* Pointer to the open directory object */ - FILINFO *fno /* Pointer to file information to return */ -) -{ - FRESULT res; - NAMEBUF(sfn, lfn); - - - res = validate(dj->fs, dj->id); /* Check validity of the object */ - 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; - } - } - } - } - - LEAVE_FF(dj->fs, res); -} - - - -#if _FS_MINIMIZE == 0 -/*-----------------------------------------------------------------------*/ -/* Get File Status */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_stat ( - const char *path, /* Pointer to the file path */ - FILINFO *fno /* Pointer to file information to return */ -) -{ - FRESULT res; - DIR dj; - NAMEBUF(sfn, lfn); - - - res = auto_mount(&path, &dj.fs, 0); - if (res == FR_OK) { - 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; - } - } - - LEAVE_FF(dj.fs, res); -} - - - -#if !_FS_READONLY -/*-----------------------------------------------------------------------*/ -/* Truncate File */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_truncate ( - FIL *fp /* Pointer to the file object */ -) -{ - FRESULT res; - DWORD ncl; - - - 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 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 */ - 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); - 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; - - LEAVE_FF(fp->fs, res); -} - - - - -/*-----------------------------------------------------------------------*/ -/* Get Number of Free Clusters */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_getfree ( - 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, clst, sect; - BYTE fat, f, *p; - - - /* Get drive number */ - 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) { - *nclst = (*fatfs)->free_clust; - LEAVE_FF(*fatfs, FR_OK); - } - - /* Get number of free clusters */ - fat = (*fatfs)->fs_type; - n = 0; - if (fat == FS_FAT12) { - clst = 2; - do { - if ((WORD)get_cluster(*fatfs, clst) == 0) n++; - } while (++clst < (*fatfs)->max_clust); - } else { - clst = (*fatfs)->max_clust; - sect = (*fatfs)->fatbase; - f = 0; p = 0; - do { - if (!f) { - res = move_window(*fatfs, sect++); - if (res != FR_OK) - LEAVE_FF(*fatfs, res); - p = (*fatfs)->win; - } - if (fat == FS_FAT16) { - if (LD_WORD(p) == 0) n++; - p += 2; f += 1; - } else { - if (LD_DWORD(p) == 0) n++; - p += 4; f += 2; - } - } while (--clst); - } - (*fatfs)->free_clust = n; - if (fat == FS_FAT32) (*fatfs)->fsi_flag = 1; - *nclst = n; - - LEAVE_FF(*fatfs, FR_OK); -} - - - - -/*-----------------------------------------------------------------------*/ -/* Delete a File or Directory */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_unlink ( - const char *path /* Pointer to the file or directory path */ -) -{ - FRESULT res; - DIR dj, sdj; - NAMEBUF(sfn, lfn); - BYTE *dir; - DWORD dclst; - - - res = auto_mount(&path, &dj.fs, 1); - 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 */ - if (dclst < 2) LEAVE_FF(dj.fs, FR_INT_ERR); - mem_cpy(&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); - } - - 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); - } - - LEAVE_FF(dj.fs, FR_OK); -} - - - - -/*-----------------------------------------------------------------------*/ -/* Create a Directory */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_mkdir ( - const char *path /* Pointer to the directory path */ -) -{ - FRESULT res; - DIR dj; - NAMEBUF(sfn, lfn); - BYTE *dir, n; - DWORD dsect, dclst, pclst, tim; - - - res = auto_mount(&path, &dj.fs, 1); - 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) res = FR_EXIST; /* Any file or directory is already existing */ - if (res != FR_NO_FILE) /* Any error occured */ - LEAVE_FF(dj.fs, res); - - 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 */ - mem_set(dir, 0, SS(dj.fs)); - mem_set(dir+DIR_Name, ' ', 8+3); /* Create "." entry */ - dir[DIR_Name] = '.'; - dir[DIR_Attr] = AM_DIR; - tim = get_fattime(); - ST_DWORD(dir+DIR_WrtTime, tim); - ST_WORD(dir+DIR_FstClusLO, dclst); - ST_WORD(dir+DIR_FstClusHI, dclst >> 16); - mem_cpy(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); - mem_set(dir, 0, SS(dj.fs)); - } - - 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); - } - - LEAVE_FF(dj.fs, res); -} - - - - -/*-----------------------------------------------------------------------*/ -/* Change File Attribute */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_chmod ( - const char *path, /* Pointer to the file path */ - BYTE value, /* Attribute bits */ - BYTE mask /* Attribute mask to change */ -) -{ - FRESULT res; - DIR dj; - NAMEBUF(sfn, lfn); - BYTE *dir; - - - res = auto_mount(&path, &dj.fs, 1); - if (res == FR_OK) { - 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); - } - } - } - - LEAVE_FF(dj.fs, res); -} - - - - -/*-----------------------------------------------------------------------*/ -/* Change Timestamp */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_utime ( - 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; - - - res = auto_mount(&path, &dj.fs, 1); - if (res == FR_OK) { - 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); - } - } - } - - LEAVE_FF(dj.fs, res); -} - - - - -/*-----------------------------------------------------------------------*/ -/* Rename File/Directory */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_rename ( - const char *path_old, /* Pointer to the old name */ - const char *path_new /* Pointer to the new name */ -) -{ - FRESULT res; - DIR dj_old, dj_new; - NAMEBUF(sfn, lfn); - BYTE buf[21], *dir; - DWORD dw; - - - 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 */ - - if (!dj_old.dir) LEAVE_FF(dj_old.fs, FR_NO_FILE); /* Is root dir? */ - mem_cpy(buf, dj_old.dir+DIR_Attr, 21); /* Save the object information */ - - mem_cpy(&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 */ - mem_cpy(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); - } - } - } - - LEAVE_FF(dj_old.fs, res); -} - -#endif /* !_FS_READONLY */ -#endif /* _FS_MINIMIZE == 0 */ -#endif /* _FS_MINIMIZE <= 1 */ -#endif /* _FS_MINIMIZE <= 2 */ - - - -/*-----------------------------------------------------------------------*/ -/* 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 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_clst, n; - WORD as; - FATFS *fs; - DSTATUS stat; - - - /* Check validity of the parameters */ - if (drv >= _DRIVES) return FR_INVALID_DRIVE; - if (partition >= 2) return FR_MKFS_ABORTED; - - /* Check mounted drive and clear work area */ - fs = FatFs[drv]; - if (!fs) return FR_NOT_ENABLED; - fs->fs_type = 0; - drv = LD2PD(drv); - - /* Get disk statics */ - stat = disk_initialize(drv); - if (stat & STA_NOINIT) return FR_NOT_READY; - if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; - if (disk_ioctl(drv, GET_SECTOR_COUNT, &n_part) != RES_OK || n_part < MIN_SECTOR) - return FR_MKFS_ABORTED; - if (n_part > MAX_SECTOR) n_part = MAX_SECTOR; - b_part = (!partition) ? 63 : 0; /* Boot sector */ - n_part -= b_part; -#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) > _MAX_SS - || SS(fs) > allocsize) - return FR_MKFS_ABORTED; -#endif - allocsize /= SS(fs); /* Number of sectors per cluster */ - - /* Pre-compute number of clusters and FAT type */ - n_clst = n_part / allocsize; - fmt = FS_FAT12; - 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_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_clst * 2) + 4 + SS(fs) - 1) / SS(fs); - n_rsv = 1 + partition; - n_dir = N_ROOTDIR * 32 / SS(fs); - break; - default: - n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs); - n_rsv = 33 - partition; - n_dir = 0; - } - b_fat = b_part + n_rsv; /* FATs start sector */ - b_dir = b_fat + n_fat * N_FATS; /* Directory start sector */ - b_data = b_dir + n_dir; /* Data start sector */ - - /* Align data start sector to erase block boundary (for flash memory media) */ - if (disk_ioctl(drv, GET_BLOCK_SIZE, &n) != RES_OK) return FR_MKFS_ABORTED; - n = (b_data + n - 1) & ~(n - 1); - n_fat += (n - b_data) / N_FATS; - /* b_dir and b_data are no longer used below */ - - /* Determine number of cluster and final check of validity of the FAT type */ - 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, 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; - tbl[6] = (BYTE)((n_disk >> 2) | 63); - } else { - ST_WORD(&tbl[6], 0xFFFF); - } - tbl[5] = 254; - if (fmt != FS_FAT32) /* System ID */ - 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 */ - if (disk_write(drv, fs->win, 0, 1) != RES_OK) - return FR_DISK_ERR; - } - - /* Create boot record */ - tbl = fs->win; /* Clear buffer */ - mem_set(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 */ - tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */ - 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); - } else { - 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 */ - 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 */ - tbl[BS_DrvNum] = 0x80; /* Drive number */ - tbl[BS_BootSig] = 0x29; /* Extended boot signature */ - mem_cpy(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 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 */ - mem_cpy(tbl+BS_VolLab32, "NO NAME FAT32 ", 19); /* Volume lavel, FAT signature */ - } - ST_WORD(tbl+BS_55AA, 0xAA55); /* Signature */ - if (disk_write(drv, tbl, b_part+0, 1) != RES_OK) - 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++) { - mem_set(tbl, 0, SS(fs)); /* 1st sector of the FAT */ - if (fmt != FS_FAT32) { - n = (fmt == FS_FAT12) ? 0x00FFFFF8 : 0xFFFFFFF8; - 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 */ - } - if (disk_write(drv, tbl, b_fat++, 1) != RES_OK) - return FR_DISK_ERR; - mem_set(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_DISK_ERR; - } - } - - /* Initialize Root directory */ - m = (BYTE)((fmt == FS_FAT32) ? allocsize : n_dir); - do { - if (disk_write(drv, tbl, b_fat++, 1) != RES_OK) - 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_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, (void*)NULL) == RES_OK) ? FR_OK : FR_DISK_ERR; -} - -#endif /* _USE_MKFS && !_FS_READONLY */ - - - - -#if _USE_STRFUNC -/*-----------------------------------------------------------------------*/ -/* Get a string from the file */ -/*-----------------------------------------------------------------------*/ -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 */ -) -{ - int i = 0; - char *p = buff; - UINT rc; - - - while (i < len - 1) { /* Read bytes until buffer gets filled */ - f_read(fil, p, 1, &rc); - if (rc != 1) break; /* Break when no data to read */ -#if _USE_STRFUNC >= 2 - if (*p == '\r') continue; /* Strip '\r' */ -#endif - i++; - if (*p++ == '\n') break; /* Break when reached end of line */ - } - *p = 0; - return i ? buff : NULL; /* When no data read (eof or error), return with error. */ -} - - - -#if !_FS_READONLY -#include -/*-----------------------------------------------------------------------*/ -/* Put a character to the file */ -/*-----------------------------------------------------------------------*/ -int f_putc ( - int chr, /* A character to be output */ - FIL* fil /* Ponter to the file object */ -) -{ - UINT bw; - char c; - - -#if _USE_STRFUNC >= 2 - 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); */ - return chr; - } - c = (char)chr; - f_write(fil, &c, 1, &bw); /* Write a byte to the file */ - return bw ? chr : EOF; /* Return the result */ -} - - - - -/*-----------------------------------------------------------------------*/ -/* Put a string to the file */ -/*-----------------------------------------------------------------------*/ -int f_puts ( - const char* str, /* Pointer to the string to be output */ - FIL* fil /* Pointer to the file object */ -) -{ - int n; - - - for (n = 0; *str; str++, n++) { - if (f_putc(*str, fil) == EOF) return EOF; - } - return n; -} - - - - -/*-----------------------------------------------------------------------*/ -/* Put a formatted string to the file */ -/*-----------------------------------------------------------------------*/ -int f_printf ( - FIL* fil, /* Pointer to the file object */ - const char* str, /* Pointer to the format string */ - ... /* Optional arguments... */ -) -{ - va_list arp; - UCHAR c, f, r; - ULONG val; - char s[16]; - int i, w, res, cc; - - - va_start(arp, str); - - for (cc = res = 0; cc != EOF; res += cc) { - c = *str++; - if (c == 0) break; /* End of string */ - if (c != '%') { /* Non escape cahracter */ - cc = f_putc(c, fil); - if (cc != EOF) cc = 1; - continue; - } - w = f = 0; - c = *str++; - if (c == '0') { /* Flag: '0' padding */ - f = 1; c = *str++; - } - while (c >= '0' && c <= '9') { /* Precision */ - w = w * 10 + (c - '0'); - c = *str++; - } - if (c == 'l') { /* Prefix: Size is long int */ - f |= 2; c = *str++; - } - if (c == 's') { /* Type is string */ - cc = f_puts(va_arg(arp, char*), fil); - continue; - } - if (c == 'c') { /* Type is character */ - cc = f_putc(va_arg(arp, int), fil); - if (cc != EOF) cc = 1; - continue; - } - r = 0; - if (c == 'd') r = 10; /* Type is signed decimal */ - if (c == 'u') r = 10; /* Type is unsigned decimal */ - if (c == 'X') r = 16; /* Type is unsigned hexdecimal */ - if (r == 0) break; /* Unknown type */ - if (f & 2) { /* Get the value */ - val = (ULONG)va_arg(arp, long); - } else { - val = (c == 'd') ? (ULONG)(long)va_arg(arp, int) : (ULONG)va_arg(arp, unsigned int); - } - /* Put numeral string */ - if (c == 'd') { - if (val & 0x80000000) { - val = 0 - val; - f |= 4; - } - } - i = sizeof(s) - 1; s[i] = 0; - do { - c = (UCHAR)(val % r + '0'); - if (c > '9') c += 7; - s[--i] = c; - val /= r; - } while (i && val); - if (i && (f & 4)) s[--i] = '-'; - w = sizeof(s) - 1 - w; - while (i && i > w) s[--i] = (f & 1) ? '0' : ' '; - cc = f_puts(&s[i], fil); - } - - va_end(arp); - return (cc == EOF) ? cc : res; -} - -#endif /* !_FS_READONLY */ -#endif /* _USE_STRFUNC */ +/*----------------------------------------------------------------------------/ +/ FatFs - FAT file system module R0.06 (C)ChaN, 2008 +/-----------------------------------------------------------------------------/ +/ The FatFs module is an experimenal project to implement FAT file system to +/ cheap microcontrollers. This is a free software and is opened for education, +/ research and development under license policy of following trems. +/ +/ Copyright (C) 2008, 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. +/ * Redistributions of source code must retain the above copyright notice. +/ +/---------------------------------------------------------------------------/ +/ Feb 26, 2006 R0.00 Prototype. +/ +/ Apr 29, 2006 R0.01 First stable version. +/ +/ Jun 01, 2006 R0.02 Added FAT12 support. +/ Removed unbuffered mode. +/ Fixed a problem on small (<32M) patition. +/ Jun 10, 2006 R0.02a Added a configuration option (_FS_MINIMUM). +/ +/ Sep 22, 2006 R0.03 Added f_rename(). +/ Changed option _FS_MINIMUM to _FS_MINIMIZE. +/ Dec 11, 2006 R0.03a Improved cluster scan algolithm to write files fast. +/ Fixed f_mkdir() creates incorrect directory on FAT32. +/ +/ Feb 04, 2007 R0.04 Supported multiple drive system. +/ Changed some interfaces for multiple drive system. +/ Changed f_mountdrv() to f_mount(). +/ Added f_mkfs(). +/ Apr 01, 2007 R0.04a Supported multiple partitions on a plysical drive. +/ Added a capability of extending file size to f_lseek(). +/ Added minimization level 3. +/ Fixed an endian sensitive code in f_mkfs(). +/ May 05, 2007 R0.04b Added a configuration option _USE_NTFLAG. +/ Added FSInfo support. +/ Fixed DBCS name can result FR_INVALID_NAME. +/ Fixed short seek (<= csize) collapses the file object. +/ +/ Aug 25, 2007 R0.05 Changed arguments of f_read(), f_write() and f_mkfs(). +/ Fixed f_mkfs() on FAT32 creates incorrect FSInfo. +/ Fixed f_mkdir() on FAT32 creates incorrect directory. +/ Feb 03, 2008 R0.05a Added f_truncate() and f_utime(). +/ Fixed off by one error at FAT sub-type determination. +/ Fixed btr in f_read() can be mistruncated. +/ Fixed cached sector is not flushed when create and close +/ without write. +/ +/ Apr 01, 2008 R0.06 Added fputc(), fputs(), fprintf() and fgets(). +/ Improved performance of f_lseek() on moving to the same +/ or following cluster. +/---------------------------------------------------------------------------*/ + +#include +#include +#include "config.h" +#include "ff.h" /* FatFs declarations */ +#include "diskio.h" /* Include file for user provided disk functions */ + + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ +#if _USE_DRIVE_PREFIX != 0 +static +FATFS *FatFs[_LOGICAL_DRIVES]; /* Pointer to the file system objects (logical drives) */ +#endif +//static +//WORD fsid; /* File system mount ID */ + +#if _USE_LFN!=0 +static const PROGMEM +BYTE LFN_pos[13]={1,3,5,7,9,14,16,18,20,22,24,28,30}; +#endif + +#if _USE_1_BUF != 0 +# define FSBUF static_buf +static +BUF static_buf; +#else +# define FSBUF (fs->buf) +#endif + +#if _USE_FS_BUF != 0 +# define FPBUF FSBUF +#else +# define FPBUF (fp->buf) +#endif + +/*-----------------------------------------------------------------------*/ +/* Change window offset */ +/*-----------------------------------------------------------------------*/ + +static +BOOL move_window ( /* TRUE: successful, FALSE: failed */ + FATFS *fs, /* File system object */ + BUF *buf, + DWORD sector /* Sector number to make apperance in the fs->buf.data[] */ +) /* Move to zero only writes back dirty window */ +{ + DWORD wsect; +#if _USE_1_BUF != 0 + FATFS* ofs = buf->fs; +#else +#define ofs fs +#endif + + + wsect = buf->sect; +#if _USE_1_BUF != 0 + if (wsect != sector || fs != ofs) { /* Changed current window */ +#else + if (wsect != sector) { /* Changed current window */ +#endif +#if !_FS_READONLY + BYTE n; + if (buf->dirty) { /* Write back dirty window if needed */ + if (disk_write(ofs->drive, buf->data, wsect, 1) != RES_OK) + return FALSE; + buf->dirty = FALSE; + if (wsect < (ofs->fatbase + ofs->sects_fat)) { /* In FAT area */ + for (n = ofs->n_fats; n >= 2; n--) { /* Reflect the change to FAT copy */ + wsect += ofs->sects_fat; + disk_write(ofs->drive, buf->data, wsect, 1); + } + } + } +#endif + if (sector) { + if (disk_read(fs->drive, buf->data, sector, 1) != RES_OK) + return FALSE; + buf->sect = sector; +#if _USE_1_BUF != 0 + buf->fs=fs; +#endif + } + } + return TRUE; +} + + + + +static +BOOL move_fs_window( + FATFS* fs, + DWORD sector +) +{ + return move_window(fs,&FSBUF,sector); +} + + + + +static +BOOL move_fp_window( + FIL* fp, + DWORD sector +) +{ + return move_window(fp->fs,&FPBUF,sector); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Clean-up cached data */ +/*-----------------------------------------------------------------------*/ + +#if !_FS_READONLY +static +FRESULT sync ( /* FR_OK: successful, FR_RW_ERROR: failed */ + FATFS *fs /* File system object */ +) +{ + FSBUF.dirty = TRUE; + if (!move_fs_window(fs, 0)) return FR_RW_ERROR; +#if _USE_FSINFO + /* Update FSInfo sector if needed */ + if (fs->fs_type == FS_FAT32 && fs->fsi_flag) { + FSBUF.sect = 0; + memset(FSBUF.data, 0, 512); + ST_WORD(&FSBUF.data[BS_55AA], 0xAA55); + ST_DWORD(&FSBUF.data[FSI_LeadSig], 0x41615252); + ST_DWORD(&FSBUF.data[FSI_StrucSig], 0x61417272); + ST_DWORD(&FSBUF.data[FSI_Free_Count], fs->free_clust); + ST_DWORD(&FSBUF.data[FSI_Nxt_Free], fs->last_clust); + disk_write(fs->drive, FSBUF.data, fs->fsi_sector, 1); + fs->fsi_flag = 0; + } +#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; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Get a cluster status */ +/*-----------------------------------------------------------------------*/ + +static +DWORD get_cluster ( /* 0,>=2: successful, 1: failed */ + FATFS *fs, /* File system object */ + DWORD clust /* Cluster# to get the link information */ +) +{ + WORD wc, bc; + DWORD fatsect; + + + 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_fs_window(fs, fatsect + (bc / SS(fs)))) break; + wc = FSBUF.data[bc & (SS(fs) - 1)]; bc++; + if (!move_fs_window(fs, fatsect + (bc / SS(fs)))) break; + wc |= (WORD)FSBUF.data[bc & (SS(fs) - 1)] << 8; + return (clust & 1) ? (wc >> 4) : (wc & 0xFFF); + + case FS_FAT16 : + if (!move_fs_window(fs, fatsect + (clust / (SS(fs) / 2)))) break; + return LD_WORD(&FSBUF.data[((WORD)clust * 2) & (SS(fs) - 1)]); + + case FS_FAT32 : + if (!move_fs_window(fs, fatsect + (clust / (SS(fs) / 4)))) break; + return LD_DWORD(&FSBUF.data[((WORD)clust * 4) & (SS(fs) - 1)]) & 0x0FFFFFFF; + } + } + + return 1; /* Out of cluster range, or an error occured */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change a cluster status */ +/*-----------------------------------------------------------------------*/ + +#if !_FS_READONLY +static +BOOL put_cluster ( /* TRUE: successful, FALSE: failed */ + FATFS *fs, /* File system object */ + DWORD clust, /* Cluster# to change (must be 2 to fs->max_clust-1) */ + DWORD val /* New value to mark the cluster */ +) +{ + WORD bc; + BYTE *p; + DWORD fatsect; + + + fatsect = fs->fatbase; + switch (fs->fs_type) { + case FS_FAT12 : + bc = (WORD)clust * 3 / 2; + if (!move_fs_window(fs, fatsect + (bc / SS(fs)))) return FALSE; + p = &FSBUF.data[bc & (SS(fs) - 1)]; + *p = (clust & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; + bc++; + FSBUF.dirty = TRUE; + if (!move_fs_window(fs, fatsect + (bc / SS(fs)))) return FALSE; + p = &FSBUF.data[bc & (SS(fs) - 1)]; + *p = (clust & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); + break; + + case FS_FAT16 : + if (!move_fs_window(fs, fatsect + (clust / (SS(fs) / 2)))) return FALSE; + ST_WORD(&FSBUF.data[((WORD)clust * 2) & (SS(fs) - 1)], (WORD)val); + break; + + case FS_FAT32 : + if (!move_fs_window(fs, fatsect + (clust / (SS(fs) / 4)))) return FALSE; + ST_DWORD(&FSBUF.data[((WORD)clust * 4) & (SS(fs) - 1)], val); + break; + + default : + return FALSE; + } + FSBUF.dirty = TRUE; + return TRUE; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* 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 */ +) +{ + 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 + } + clust = nxt; + } + return TRUE; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Stretch or create a cluster chain */ +/*-----------------------------------------------------------------------*/ + +#if !_FS_READONLY +static +DWORD create_chain ( /* 0: No free cluster, 1: Error, >=2: New cluster number */ + FATFS *fs, /* File system object */ + DWORD clust /* Cluster# to stretch, 0 means create new */ +) +{ + DWORD cstat, ncl, scl, mcl = fs->max_clust; + + + if (clust == 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; + } + + ncl = scl; /* Start cluster */ + for (;;) { + ncl++; /* Next cluster */ + if (ncl >= mcl) { /* Wrap around */ + ncl = 2; + if (ncl > scl) return 0; /* No free custer */ + } + cstat = get_cluster(fs, ncl); /* Get the cluster status */ + if (cstat == 0) break; /* Found a free cluster */ + if (cstat == 1) return 1; /* Any error occured */ + if (ncl == scl) return 0; /* No free custer */ + } + + if (!put_cluster(fs, ncl, 0x0FFFFFFF)) return 1; /* Mark the new cluster "in use" */ + if (clust && !put_cluster(fs, clust, ncl)) return 1; /* Link it to previous one if needed */ + + 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 */ +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Get sector# from cluster# */ +/*-----------------------------------------------------------------------*/ + +static +DWORD clust2sect ( /* !=0: sector number, 0: failed - invalid cluster# */ + FATFS *fs, /* File system object */ + DWORD clust /* Cluster# to be converted */ +) +{ + clust -= 2; + if (clust >= (fs->max_clust - 2)) return 0; /* Invalid cluster# */ + return clust * fs->csize + fs->database; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Move directory pointer to next */ +/*-----------------------------------------------------------------------*/ + +static +BOOL next_dir_entry ( /* TRUE: successful, FALSE: could not move next */ + DIR *dj /* Pointer to directory object */ +) +{ + DWORD clust; + WORD idx; + + + 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; /* Lower several bits of dj->index indicates offset in dj->sect */ + return TRUE; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Get file status from directory entry */ +/*-----------------------------------------------------------------------*/ + +#if _FS_MINIMIZE <= 1 +static +void get_fileinfo ( /* No return code */ + FILINFO *finfo, /* Ptr to store the file information */ + const BYTE *dir /* Ptr to the directory entry */ +) +{ + BYTE n, c, a; + UCHAR *p; + + + p = &finfo->fname[0]; + a = _USE_NTFLAG ? dir[DIR_NTres] : 0; /* NT flag */ + for (n = 0; n < 8; n++) { /* Convert file name (body) */ + c = dir[n]; + if (c == ' ') break; + if (c == 0x05) c = 0xE5; + if (a & 0x08 && c >= 'A' && c <= 'Z') c += 0x20; + *p++ = c; + } + if (dir[8] != ' ') { /* Convert file name (extension) */ + *p++ = '.'; + for (n = 8; n < 11; n++) { + c = dir[n]; + if (c == ' ') break; + if (a & 0x10 && c >= 'A' && c <= 'Z') c += 0x20; + *p++ = c; + } + } + *p = '\0'; + + finfo->fattrib = dir[DIR_Attr]; /* Attribute */ + finfo->fsize = LD_DWORD(&dir[DIR_FileSize]); /* Size */ + finfo->fdate = LD_WORD(&dir[DIR_WrtDate]); /* Date */ + finfo->ftime = LD_WORD(&dir[DIR_WrtTime]); /* Time */ + finfo->clust = ((DWORD)LD_WORD(&dir[DIR_FstClusHI]) << 16) + | LD_WORD(&dir[DIR_FstClusLO]); /* Get cluster# */ +} +#endif /* _FS_MINIMIZE <= 1 */ + + + + +/*-----------------------------------------------------------------------*/ +/* Pick a paragraph and create the name in format of directory entry */ +/*-----------------------------------------------------------------------*/ + +static +UCHAR make_dirfile ( /* 1: error - detected an invalid format, '\0'or'/': next character */ + const UCHAR **path, /* Pointer to the file path pointer */ + UCHAR *dirname, /* Pointer to directory name buffer {Name(8), Ext(3), NT flag(1)} */ + BOOL* lfn /* is this an LFN name? */ +) +{ + BYTE n, t, c, a, b; + + + *lfn=FALSE; + 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 == ' ') goto md_l3; + if (c == '.') { +#if _USE_CHDIR != 0 || _USE_CURR_DIR != 0 + if (n == 0 || (n == 1 && *dirname == '.')) + goto md_l2; +#endif + if (!(a & 1) && n >= 1 && n <= 8) { /* Enter extension part */ + n = 8; t = 11; continue; + } + goto md_l3; + } + 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 <= ',') goto md_l3; /* Reject * + , */ + if (c <= '9') goto md_l1; /* Accept - 0-9 */ + if (c <= '?') goto md_l3; /* Reject : ; < = > ? */ + if (!(a & 1)) { /* These checks are not applied to S-JIS 2nd byte */ + if (c == '|') goto md_l3; /* Reject | */ + if (c >= '[' && c <= ']') goto md_l3;/* Reject [ \ ] */ + if (_USE_NTFLAG && c >= 'A' && c <= 'Z') + (t == 8) ? (b &= (BYTE)~0x08) : (b &= (BYTE)~0x10); + if (c >= 'a' && c <= 'z') { /* Convert to upper case */ + c -= 0x20; + if (_USE_NTFLAG) (t == 8) ? (a |= 0x08) : (a |= 0x10); + } + } + md_l1: + a &= (BYTE)~0x01; + md_l2: + if (n >= t) goto md_l3; + dirname[n++] = c; + } + return 1; + md_l3: +#if _USE_LFN != 0 + do { + if(c== '\\' || c==':' || c=='*' || c=='?' || c == '"' || c == '<' || c == '>' || c=='|') + return 1; + c = *(*path)++; + } while (c != '\0' && c != '/'); /* Reached to end of str or directory separator */ + *lfn=TRUE; + return c; +#else + return 1; +#endif +} + + + + +/*-----------------------------------------------------------------------*/ +/* 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 */ + UCHAR *fn, /* Pointer to last segment name to return {file(8),ext(3),attr(1)} */ + const UCHAR *path, /* Full-path string to trace a file or directory */ + BYTE **dir /* Pointer to pointer to found entry to return */ +#if _USE_LFN != 0 + ,DIR *fileobj, /* Pointer DIR holding the beginning of the LFN, identical to dj if none */ + const UCHAR** spath, /* path to start of last item's name */ + UINT *len /* length of LFN entry, 0=non-LFN */ +#endif +) +{ + DWORD clust; + UCHAR ds; + BYTE *dptr = NULL; + FATFS *fs = dj->fs; + BOOL lfn; +#if _USE_LFN != 0 + BYTE a,b,i,j; + BOOL match; + UINT l; + BOOL store=TRUE; +#endif + + /* Initialize directory object */ +#if _USE_CHDIR != 0 || _USE_CURR_DIR != 0 + if(fs->curr_dir==0 || (*path!=0 && path[0]=='/')) { +#endif + 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; + } +#if _USE_CHDIR != 0 || _USE_CURR_DIR !=0 + } else { + clust=fs->curr_dir; + dj->clust = dj->sclust = clust; + dj->sect = clust2sect(fs, clust); + } +#endif + dj->index = 0; + +#if _USE_LFN != 0 + //fileobj->id=dj->id; + fileobj->fs=dj->fs; +#endif + +#if _USE_CHDIR != 0 + while (path[0] == '/') path++; +#endif + + if (*path == '\0') { /* Null path means the root directory */ + *dir = NULL; return FR_OK; + } + + for (;;) { +#if _USE_LFN != 0 + *spath=path; // save this off, as we may need it for the LFN + match=TRUE; + l=0; +#endif + ds = make_dirfile(&path, fn, &lfn); /* Get a paragraph into fn[] */ +#if _USE_LFN != 0 + if(lfn) + *len=path-*spath-1; /* this might not be ANSI-compatible, not sure */ + else + *len=0; +#endif + if (ds == 1) return FR_INVALID_NAME; + for (;;) { + if (!move_fs_window(fs, dj->sect)) return FR_RW_ERROR; +#if _USE_LFN != 0 + if (store) { + fileobj->clust = dj->clust; + fileobj->sect = dj->sect; + fileobj->index = dj->index; + store = FALSE; + if (!lfn) + /* We don't know the length of the LFN, estimate it */ + *len = 0; + } +#endif + dptr = &FSBUF.data[(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 _USE_LFN != 0 + if (dptr[DIR_Name] != 0xE5) { /* Matched? */ + if((dptr[DIR_Attr] & AM_LFN) == AM_LFN) { + if (lfn) { + i=((dptr[0]&0x1f)-1)*13; + j=0; + while(j<13 && match) { + a=dptr[pgm_read_byte(LFN_pos+j)]; + b=dptr[pgm_read_byte(LFN_pos+j++)+1]; + if(!a && !b) { + j--; + break; + } + b=(*spath)[i++]; + if (b >= 'a' && b <= 'z') { /* Convert to upper case */ + b -= 0x20; + } + if (a >= 'a' && a <= 'z') { /* Convert to upper case */ + a -= 0x20; + } + if(a!= b) { + match=FALSE; + } + } + l+=j; + } else { + /* Track the length of the LFN entry in case it belongs to our file */ + *len = *len + 13; + } + } else if ((dptr[DIR_Attr] & AM_LFN) != AM_LFN) { /* we're a normal entry */ + if (lfn) { + if(lfn && (match && l == *len)) { /* match */ + memcpy(fn,&dptr[DIR_Name], 8+3); + fn[11] = dptr[DIR_NTres]; + break; + } + l=0; + match=TRUE; + } else { + /* No LFN to match against */ + if (dptr[DIR_Name] != 0xE5 /* Matched? */ + && !(dptr[DIR_Attr] & AM_VOL) + && !memcmp(&dptr[DIR_Name], fn, 8+3) ) { + break; + } + } + store = TRUE; + } + } else + /* This is a deleted entry, move fileobj forward */ + store = TRUE; +#else + if (dptr[DIR_Name] != 0xE5 /* Matched? */ + && !(dptr[DIR_Attr] & AM_VOL) + && !memcmp(&dptr[DIR_Name], fn, 8+3) ) { + break; + } +#endif + if (!next_dir_entry(dj)) { /* Next directory pointer */ +#if _USE_LFN != 0 + if (!lfn) + *len = 0; +#endif + return !ds ? FR_NO_FILE : FR_NO_PATH; + } + } + if (!ds) { *dir = dptr; return FR_OK; } /* Matched with end of path */ + if (!(dptr[DIR_Attr] & AM_DIR)) return FR_NO_PATH; /* Cannot trace because it is a file */ + clust = ((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; + } +} + + + + +/*-----------------------------------------------------------------------*/ +/* 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 */ +#if _USE_LFN != 0 + ,UINT len +#endif +) +{ + DWORD clust, sector; + BYTE c, n, *dptr; + FATFS *fs = dj->fs; + +#if _USE_LFN != 0 + BYTE entries=0; + WORD isave=0; + DWORD csave=0,ssave=0; + + len=(len+25)/13; +#endif + /* Re-initialize directory object */ + clust = dj->sclust; + if (clust != 0) { /* Dynamic directory table */ + dj->clust = clust; + dj->sect = clust2sect(fs, clust); + } else { /* Static directory table */ + dj->sect = fs->dirbase; + } + dj->index = 0; + + do { + if (!move_fs_window(fs, dj->sect)) return FR_RW_ERROR; + dptr = &FSBUF.data[(dj->index & ((SS(fs) - 1) / 32)) * 32]; /* Pointer to the directory entry */ + c = dptr[DIR_Name]; + if (c == 0 || c == 0xE5) { /* Found an empty entry */ +#if _USE_LFN != 0 + /* capture initial entry. */ + if((entries++) == 0) { + *dir=dptr; + isave=dj->index; + ssave=dj->sect; + csave=dj->clust; + } + if(entries==len) { + dj->index=isave; + dj->sect=ssave; + dj->clust=csave; + return FR_OK; + } + } else if(entries!=len){ + entries=0; +#else + *dir = dptr; return FR_OK; +#endif + } + } 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_fs_window(fs, 0)) return FR_RW_ERROR; + /* Cleanup the expanded table */ + FSBUF.sect = sector = clust2sect(fs, clust); + memset(FSBUF.data, 0, SS(fs)); + for (n = fs->csize; n; n--) { + if (disk_write(fs->drive, FSBUF.data, sector, 1) != RES_OK) + return FR_RW_ERROR; + sector++; + } + FSBUF.dirty = TRUE; +#if _USE_LFN != 0 + if(entries) { // we had to expand the table, but now we need to go back. + if (!move_fs_window(fs, ssave)) return FR_RW_ERROR; + dj->index=isave; + dj->sect=ssave; + dj->clust=csave; + } else { + /* We allocated a new cluster for all entries, point dj there */ + dj->index = 0; + dj->sect = FSBUF.sect; + dj->clust = clust; + *dir = FSBUF.data; + } +#else + *dir = FSBUF.data; +#endif + + return FR_OK; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Load boot record and check if it is a FAT boot record */ +/*-----------------------------------------------------------------------*/ + +static const PROGMEM UCHAR fat32string[] = "FAT32"; + +static +BYTE check_fs ( /* 0:The FAT boot record, 1:Valid boot record but not a FAT, 2:Not a boot record or error */ + FATFS *fs, /* File system object */ + DWORD sect /* Sector# (lba) to check if it is a FAT boot record or not */ +) +{ + if (!move_fs_window(fs, sect)) /* Load boot record, save off old data in process */ + return 2; + if (!sect) { + if (disk_read(fs->drive, FSBUF.data, sect, 1) != RES_OK) /* Load boot record, if sector 0 */ + return 2; + FSBUF.sect = 0; + } + if (LD_WORD(&FSBUF.data[BS_55AA]) != 0xAA55) /* Check record signature (always placed at offset 510 even if the sector size is >512) */ + return 2; + + if (!memcmp_P(&FSBUF.data[BS_FilSysType], fat32string, 3)) /* Check FAT signature */ + return 0; + if (!memcmp_P(&FSBUF.data[BS_FilSysType32], fat32string, 5) && !(FSBUF.data[BPB_ExtFlags] & 0x80)) + return 0; + + return 1; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Mount a drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT mount_drv( + BYTE drv, + FATFS* fs, + BYTE chk_wp /* !=0: Check media write protection for write access */ +) +{ + DSTATUS stat; + BYTE fmt, *tbl; + DWORD bootsect, fatsize, totalsect, maxclust; + + memset(fs, 0, sizeof(FATFS)); /* Clean-up 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 */ + return FR_NOT_READY; +#if S_MAX_SIZ > 512 /* Get disk sector size if needed */ + if (disk_ioctl(fs->drive, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > S_MAX_SIZ) + return FR_NO_FILESYSTEM; +#endif +#if !_FS_READONLY + if (chk_wp && (stat & STA_PROTECT)) /* Check write protection if needed */ + return FR_WRITE_PROTECTED; +#endif +#if _MULTI_PARTITION == 0 + /* Search FAT partition on the drive */ + fmt = check_fs(fs, bootsect = 0); /* Check sector 0 as an SFD format */ + if (fmt == 1) { /* Not a FAT boot record, it may be patitioned */ + /* Check a partition listed in top of the partition table */ + tbl = &FSBUF.data[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 */ + } + } +#else + /* Check only the partition that was requested */ + if (LD2PT(drv) == 0) { + /* Unpartitioned media */ + fmt = check_fs(fs, bootsect = 0); + } else { + /* Read MBR */ + fmt = 1; + if (disk_read(fs->drive, FSBUF.data, 0, 1) != RES_OK) + goto failed; + + if (LD2PT(drv) < 5) { + /* Primary partition */ + tbl = &FSBUF.data[MBR_Table + (LD2PT(drv)-1) * 16]; + fmt = 1; + if (tbl[4]) { + bootsect = LD_DWORD(&tbl[8]); + fmt = check_fs(fs, bootsect); + } + } else { + /* Logical drive */ + BYTE i,curr; + fmt = 1; + bootsect = 0; + fatsize = 0; // Used to store the offset of the first extended part + curr = LD2PT(drv)-4; + /* Walk the chain of extended partitions */ + do { + /* Check for an extended partition */ + for (i=0;i<4;i++) { + tbl = &FSBUF.data[MBR_Table + i*16]; + if (tbl[4] == 5 || tbl[4] == 0x0f) + break; + } + if (i == 4) { + fmt = 255; + goto failed; + } + bootsect = fatsize + LD_DWORD(&tbl[8]); + + if (fatsize == 0) + fatsize = bootsect; + + /* Read the next sector in the partition chain */ + if (disk_read(fs->drive, FSBUF.data, bootsect, 1) != RES_OK) + goto failed; + } while (--curr); + /* Look for the non-extended, non-empty partition entry */ + for (i=0;i<4;i++) { + tbl = &FSBUF.data[MBR_Table + i*16]; + if (tbl[4] && tbl[4] != 5 && tbl[4] != 0x0f) + break; + } + if (i == 4) { + /* End of extended partition chain */ + fmt = 255; + goto failed; + } + bootsect = bootsect + LD_DWORD(&tbl[8]); + fmt = check_fs(fs, bootsect); + } + } + failed: + +#endif + + if (fmt || LD_WORD(&FSBUF.data[BPB_BytsPerSec]) != SS(fs)) { /* No valid FAT patition is found */ + if (fmt == 255) { + /* At end of extended partition chain */ + return FR_INVALID_OBJECT; + } else { + /* No file system found */ + return FR_NO_FILESYSTEM; + } + } + + /* Initialize the file system object */ + fatsize = LD_WORD(&FSBUF.data[BPB_FATSz16]); /* Number of sectors per FAT */ + if (!fatsize) fatsize = LD_DWORD(&FSBUF.data[BPB_FATSz32]); + fs->sects_fat = fatsize; + fs->n_fats = FSBUF.data[BPB_NumFATs]; /* Number of FAT copies */ + fatsize *= fs->n_fats; /* (Number of sectors in FAT area) */ + fs->fatbase = bootsect + LD_WORD(&FSBUF.data[BPB_RsvdSecCnt]); /* FAT start sector (lba) */ + fs->csize = FSBUF.data[BPB_SecPerClus]; /* Number of sectors per cluster */ + fs->n_rootdir = LD_WORD(&FSBUF.data[BPB_RootEntCnt]); /* Nmuber of root directory entries */ + totalsect = LD_WORD(&FSBUF.data[BPB_TotSec16]); /* Number of sectors on the file system */ + if (!totalsect) totalsect = LD_DWORD(&FSBUF.data[BPB_TotSec32]); + fs->max_clust = maxclust = (totalsect /* max_clust = Last cluster# + 1 */ + - LD_WORD(&FSBUF.data[BPB_RsvdSecCnt]) - fatsize - 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; + fs->fs_type = fmt; + + if (fmt == FS_FAT32) + fs->dirbase = LD_DWORD(&FSBUF.data[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) */ + +#if !_FS_READONLY + fs->free_clust = 0xFFFFFFFF; +# if _USE_FSINFO + /* Get fsinfo if needed */ + if (fmt == FS_FAT32) { + fs->fsi_sector = bootsect + LD_WORD(&FSBUF.data[BPB_FSInfo]); + //if (disk_read(fs->drive, FSBUF.data, fs->fsi_sector, 1) == RES_OK && + if (move_fs_window(fs,fs->fsi_sector) && + LD_WORD(&FSBUF.data[BS_55AA]) == 0xAA55 && + LD_DWORD(&FSBUF.data[FSI_LeadSig]) == 0x41615252 && + LD_DWORD(&FSBUF.data[FSI_StrucSig]) == 0x61417272) { + fs->last_clust = LD_DWORD(&FSBUF.data[FSI_Nxt_Free]); + fs->free_clust = LD_DWORD(&FSBUF.data[FSI_Free_Count]); + } + } +# endif +#endif + fs->fs_type = fmt; /* FAT syb-type */ + //fs->id = ++fsid; /* File system mount ID */ + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Make sure that the file system is valid */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT auto_mount ( /* FR_OK(0): successful, !=0: any error occured */ + const UCHAR **path, /* Pointer to pointer to the path name (drive number) */ + FATFS **rfs, /* Pointer to pointer to the found file system object */ + BYTE chk_wp /* !=0: Check media write protection for write access */ +) +{ + DSTATUS stat; + const UCHAR *p = *path; +#if _USE_DRIVE_PREFIX != 0 + BYTE drv; + + /* Get drive number from the path name */ + while (*p == ' ') p++; /* Strip leading spaces */ +# if _MULTI_PARTITION != 0 + *path=p; + drv = 0; + while(*p >= '0' && *p<='9') { + if(drv >= 25 && *p > '5') + return FR_INVALID_DRIVE; + drv = drv*10+((*p++)-'0'); + } + if(*p != ':') { + p = *path; + drv = 0; + } +# else + 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 */ +# endif +#endif +#if _USE_CHDIR == 0 + if (*p == '/') p++; /* Strip heading slash */ +#endif + *path = p; /* Return pointer to the path name */ + + /* Check if the drive number is valid or not */ +#if _USE_DRIVE_PREFIX != 0 + if (drv >= _LOGICAL_DRIVES) return FR_INVALID_DRIVE; /* Is the drive number valid? */ + *rfs = FatFs[drv]; /* Returen pointer to the corresponding file system object */ +#endif + if (!*rfs) return FR_NOT_ENABLED; /* Is the file system object registered? */ + + if ((*rfs)->fs_type) { /* If the logical drive has been mounted */ + stat = disk_status((*rfs)->drive); + if (!(stat & STA_NOINIT)) { /* and physical drive is kept initialized (has not been changed), */ +#if !_FS_READONLY + if (chk_wp && (stat & STA_PROTECT)) /* Check write protection if needed */ + return FR_WRITE_PROTECTED; +#endif + return FR_OK; /* The file system object is valid */ + } + } + + /* The logical drive must be re-mounted. */ + +#if _USE_DEFERRED_MOUNT != 0 + return mount_drv(drv,*rfs, chk_wp); +#else + return FR_NOT_READY; +#endif +} + + + + +/*-----------------------------------------------------------------------*/ +/* Check if the file/dir object is valid or not */ +/*-----------------------------------------------------------------------*/ + +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 */ +) +{ + if (!fs || !fs->fs_type /*|| fs->id != id */) + return FR_INVALID_OBJECT; + if (disk_status(fs->drive) & STA_NOINIT) + return FR_NOT_READY; + + return FR_OK; +} + + + + +#if _USE_LFN != 0 +static +BYTE compute_checksum(UCHAR *buf) { + BYTE i,rc; + rc=buf[0]; + for(i=1;i<11;i++) { + rc=(rc&1?(rc>>1)| 0x80:rc>>1); + rc+=buf[i]; + } + return rc; +} + + + + +static +BYTE dos_char( + BYTE x +) +{ + if(x>('a'-1) && x<('z'+1)) + x=x-'a'+'A'; + else if(x>(' '-1) && x<'+') { + } else if(x>('A'-1) && x<('Z'+1)) { + } else if(x>('0'-1) && x<('9'+1)) { + } else if(x&0x80) { + } else switch(x) { + case ',': + case '-': + case '^': + case '~': + case '=': + case '_': + case '`': + case '{': + case '}': + break; + default: + x='_'; + break; + } + return x; +} + + + + +static +void create_short_name( + const UCHAR* name, + UINT len, + UCHAR* buf +) +{ + BYTE i=0,k,l=0; + BYTE j=len; + + memset(buf,' ',11); + buf[11]=0; + if(name[0]!='.') { + while(--j) { /* find the last '.' */ + if(name[j]=='.') break; + } + k=j+1; + i=8; + if(k>1) { + while(kfs; + DWORD clust; + + /* Re-initialize directory object */ + clust = dj->sclust; + if (clust) { /* 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_fs_window(fs, dj->sect)) return FR_RW_ERROR; + dptr = &FSBUF.data[(dj->index & ((SS(fs) - 1) / 32)) * 32]; /* Pointer to the directory entry */ + if(dptr[DIR_Name] == 0) { /* if we got here, we have a match */ + return FR_OK; + } else if(*dptr!=0xe5 + && (dptr[DIR_Attr] & AM_LFN) != AM_LFN + && !(dptr[DIR_Attr] & AM_VOL) + && !memcmp(&dptr[DIR_Name], fn, 8+3) ) /* start over. */ + return FR_EXIST; + } while (next_dir_entry(dj)); /* Next directory pointer */ + /* Reached to end of the directory table */ + return FR_OK; +} + +FRESULT add_direntry( + DIR *dj, /* Target directory to create new entry */ + BYTE **dir, /* pointer to created entry */ + const UCHAR* spath, + UINT len, + UCHAR* fn +) +{ + FATFS *fs = dj->fs; + DWORD clust,sect; + WORD index; + BYTE entries,i,j,k; + BYTE chk; + + entries=i=(len+12)/13; + + fn[11]=0; /* ALL uppercase */ + + clust = dj->clust; /* save off entries needed. */ + index = dj->index; + sect = dj->sect; + create_short_name(spath,len,fn); + for(;;) { + if(!chk_filename(dj,fn)) break; + if(!fix_short_name(fn)) + return FR_EXIST; + } + chk=compute_checksum(fn); + dj->clust = clust; /* we now have a good name, use it */ + dj->index = index; + dj->sect = sect; + while(move_fs_window(fs, dj->sect)) { + *dir = &FSBUF.data[(dj->index & ((SS(fs) - 1) / 32)) * 32]; /* Pointer to the directory entry */ + if(!i) + return FR_OK; + FSBUF.dirty = TRUE; + memset(*dir, 0, 32); /* Initialize the new entry with open name */ + (*dir)[DIR_Name]=(i==entries?0x40:0) | (i); + j=0; + i--; + k=(i)*13; + while(j<13 && k= _LOGICAL_DRIVES) return FR_INVALID_DRIVE; + + if (FatFs[drv]) FatFs[drv]->fs_type = 0; /* Clear old object */ + + FatFs[drv] = fs; /* Register and clear new object */ +#endif + if (fs) fs->fs_type = 0; + +#if _USE_DEFERRED_MOUNT == 0 + return mount_drv(drv,fs,0); +#else + return FR_OK; +#endif +} + + + + + +/*-----------------------------------------------------------------------*/ +/* Open or Create a File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_open ( +#if _USE_DRIVE_PREFIX == 0 + FATFS *fs, /* Pointer to file system object */ +#endif + FIL *fp, /* Pointer to the blank file object */ + const UCHAR *path, /* Pointer to the file name */ + BYTE mode /* Access mode and file open mode flags */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir = NULL; + UCHAR fn[8+3+1]; +#if _USE_DRIVE_PREFIX != 0 + FATFS *fs = NULL; +#endif +#if _USE_LFN != 0 + UINT len; + DIR fileobj; + const UCHAR* spath; +#endif + + +#if _USE_FS_BUF == 0 + FPBUF.dirty=FALSE; +#endif + 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, &fs, (BYTE)(mode & (FA_WRITE|FA_CREATE_ALWAYS|FA_OPEN_ALWAYS|FA_CREATE_NEW))); +#else + mode &= FA_READ; + res = auto_mount(&path, &fs, 0); +#endif + if (res != FR_OK) return res; + dj.fs = fs; + +#if _USE_LFN != 0 + res = trace_path(&dj, fn, path, &dir, &fileobj, &spath, &len); /* Trace the file path */ +#else + res = trace_path(&dj, fn, path, &dir); /* Trace the file path */ +#endif +#if !_FS_READONLY + /* Create or Open a file */ +# if _USE_CHDIR != 0 + if(fn[0]=='.') + return FR_INVALID_NAME; +# endif + if (mode & (FA_CREATE_ALWAYS|FA_OPEN_ALWAYS|FA_CREATE_NEW)) { + DWORD ps, rs; + if (res != FR_OK) { /* No file, create new */ + if (res != FR_NO_FILE) return res; +# if _USE_LFN != 0 + res = reserve_direntry(&dj, &dir,len); + if (res != FR_OK) return res; + if(len && add_direntry(&dj,&dir,spath,len,fn)) return FR_RW_ERROR; +# else + res = reserve_direntry(&dj, &dir); + if (res != FR_OK) return res; +# endif + memset(dir, 0, 32); /* Initialize the new entry with open name */ + memcpy(&dir[DIR_Name], fn, 8+3); + dir[DIR_NTres] = fn[11]; + mode |= FA_CREATE_ALWAYS; + } + else { /* Any object is already existing */ + if (mode & FA_CREATE_NEW) /* Cannot create new */ + return FR_EXIST; + if (!dir) /* Cannot overwrite it */ + return FR_DENIED; + if (dir[DIR_Attr] & AM_DIR) + return FR_IS_DIRECTORY; + if (dir[DIR_Attr] & AM_RDO) + return FR_IS_READONLY; + 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 */ + FSBUF.dirty = TRUE; + ps = FSBUF.sect; /* Remove the cluster chain */ + if (!remove_chain(fs, rs) || !move_fs_window(fs, ps)) + return FR_RW_ERROR; + fs->last_clust = rs - 1; /* Reuse the cluster hole */ + } + } + if (mode & FA_CREATE_ALWAYS) { + dir[DIR_Attr] = 0; /* Reset attribute */ + ps = get_fattime(); + ST_DWORD(&dir[DIR_CrtTime], ps); /* Created time */ + sync(fs); /* not sure if this is needed in all cases, but kept */ + mode |= FA__WRITTEN; /* Set file changed flag */ + } + } + /* Open an existing file */ + else { +#endif /* !_FS_READONLY */ + if (res != FR_OK) return res; /* Trace failed */ + if (!dir || (dir[DIR_Attr] & AM_DIR)) /* It is a directory */ + return FR_NO_FILE; +#if !_FS_READONLY + if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */ + return FR_DENIED; + } + + fp->dir_sect = FSBUF.sect; /* Pointer to the directory entry */ + fp->dir_ptr = 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 */ + fp->fptr = 0; /* Initialize file pointer */ + fp->csect = 1; /* Sector counter */ + fp->fs = fs; //fp->id = fs->id; /* Owner file system object of the file */ + + return FR_OK; +} + +FRESULT l_opencluster ( + FATFS *fs, /* Pointer to file system object */ + FIL *fp, /* Pointer to the blank file object */ + DWORD clust /* Cluster number to be opened */ +) +{ + fp->flag = FA_READ; + fp->org_clust = clust; + fp->fsize = (DWORD)fs->csize * SS(fs); + fp->fptr = 0; + fp->csect = 1; + fp->fs = fs; + + return FR_OK; +} + + + +/*-----------------------------------------------------------------------*/ +/* Read File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_read ( + FIL *fp, /* Pointer to the file object */ + void *buff, /* Pointer to data buffer */ + UINT btr, /* Number of bytes to read */ + UINT *br /* Pointer to number of bytes read */ +) +{ + FRESULT res; + DWORD clust, sect, remain; + UINT rcnt, cc; + BYTE *rbuff = buff; + FATFS *fs = fp->fs; + + + *br = 0; + res = validate(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 */ + remain = fp->fsize - fp->fptr; + if (btr > remain) btr = (UINT)remain; /* Truncate read count by number of bytes left */ + + for ( ; btr; /* Repeat until all data transferred */ + rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) { + if ((fp->fptr & (SS(fs) - 1)) == 0) { /* On the sector boundary */ + if (--fp->csect) { /* Decrement left sector counter */ + sect = fp->curr_sect + 1; /* Get current sector */ + } else { /* On the cluster boundary, get next cluster */ + clust = (fp->fptr == 0) ? + fp->org_clust : get_cluster(fs, fp->curr_clust); + if (clust < 2 || clust >= fs->max_clust) + goto fr_error; + fp->curr_clust = clust; /* Current cluster */ + sect = clust2sect(fs, clust); /* Get current sector */ + fp->csect = fs->csize; /* Re-initialize the left sector counter */ + } +#if !_FS_READONLY + if(!move_fp_window(fp,0)) goto fr_error; +#endif + fp->curr_sect = sect; /* Update current sector */ + cc = btr / SS(fs); /* When left bytes >= SS(fs), */ + if (cc) { /* Read maximum contiguous sectors directly */ + if (cc > fp->csect) cc = fp->csect; + if (disk_read(fs->drive, rbuff, sect, (BYTE)cc) != RES_OK) + goto fr_error; + fp->csect -= (BYTE)(cc - 1); + fp->curr_sect += cc - 1; + rcnt = cc * SS(fs); + continue; + } + } + if(btr) { /* if we actually have bytes to read in singles, copy them in */ + rcnt = SS(fs) - ((WORD)fp->fptr & (SS(fs) - 1)); /* Copy fractional bytes from file I/O buffer */ + if (rcnt > btr) rcnt = btr; + if(!move_fp_window(fp,fp->curr_sect)) goto fr_error; /* are we there or not? */ + memcpy(rbuff, &FPBUF.data[fp->fptr & (SS(fs) - 1)], rcnt); + } + } + + return FR_OK; + +fr_error: /* Abort this file due to an unrecoverable error */ + fp->flag |= FA__ERROR; + return FR_RW_ERROR; +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Write File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_write ( + FIL *fp, /* Pointer to the file object */ + const void *buff, /* Pointer to the data to be written */ + UINT btw, /* Number of bytes to write */ + UINT *bw /* Pointer to number of bytes written */ +) +{ + FRESULT res; + DWORD clust, sect; + UINT wcnt, cc; + const BYTE *wbuff = buff; + FATFS *fs = fp->fs; + + + *bw = 0; + res = validate(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 */ + + for ( ; btw; /* Repeat until all data transferred */ + wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) { + if ((fp->fptr & (SS(fs) - 1)) == 0) { /* On the sector boundary */ + if (--fp->csect) { /* Decrement left sector counter */ + sect = fp->curr_sect + 1; /* Get current sector */ + } else { /* On the cluster boundary, get next cluster */ + if (fp->fptr == 0) { /* Is top of the file */ + clust = fp->org_clust; + if (clust == 0) /* No cluster is created yet */ + fp->org_clust = clust = create_chain(fs, 0); /* Create a new cluster chain */ + } else { /* Middle or end of file */ + clust = create_chain(fs, fp->curr_clust); /* Trace or streach cluster chain */ + } + if (clust == 0) break; /* Disk full */ + if (clust == 1 || clust >= fs->max_clust) goto fw_error; + fp->curr_clust = clust; /* Current cluster */ + sect = clust2sect(fs, clust); /* Get current sector */ + fp->csect = fs->csize; /* Re-initialize the left sector counter */ + } + if(!move_fp_window(fp,0)) goto fw_error; + fp->curr_sect = sect; /* Update current sector */ + cc = btw / SS(fs); /* When left bytes >= SS(fs), */ + if (cc) { /* Write maximum contiguous sectors directly */ + if (cc > fp->csect) cc = fp->csect; + if (disk_write(fs->drive, wbuff, sect, (BYTE)cc) != RES_OK) + goto fw_error; + fp->csect -= (BYTE)(cc - 1); + fp->curr_sect += cc - 1; + wcnt = cc * SS(fs); + continue; + } + } + if(btw) { + wcnt = SS(fs) - ((WORD)fp->fptr & (SS(fs) - 1)); /* Copy fractional bytes to file I/O buffer */ + if (wcnt > btw) wcnt = btw; + if ( +#if _USE_1_BUF == 0 + fp->fptr < fp->fsize && /* Fill sector buffer with file data if needed */ +#endif + !move_fp_window(fp,fp->curr_sect)) + goto fw_error; + memcpy(&FPBUF.data[fp->fptr & (SS(fs) - 1)], wbuff, wcnt); + FPBUF.dirty=TRUE; + } + } + + 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; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Synchronize the file object */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_sync ( + FIL *fp /* Pointer to the file object */ +) +{ + FRESULT res; + DWORD tim; + BYTE *dir; + FATFS *fs = fp->fs; + + + res = validate(fs /*, fp->id*/); /* Check validity of the object */ + if (res == FR_OK) { + if (fp->flag & FA__WRITTEN) { /* Has the file been written? */ + /* Write back data buffer if needed */ + if(!move_fp_window(fp,0)) return FR_RW_ERROR; + /* Update the directory entry */ + if (!move_fs_window(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(fs); + } + } + return res; +} + +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Close File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_close ( + FIL *fp /* Pointer to the file object to be closed */ +) +{ + FRESULT res; + + +#if !_FS_READONLY + res = f_sync(fp); +#else + res = validate(fp->fs /*, fp->id*/); +#endif + if (res == FR_OK) fp->fs = NULL; + return res; +} + + + + +#if _FS_MINIMIZE <= 2 +/*-----------------------------------------------------------------------*/ +/* Seek File R/W Pointer */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_lseek ( + FIL *fp, /* Pointer to the file object */ + DWORD ofs /* File pointer from top of file */ +) +{ + FRESULT res; + DWORD clust, csize; + CHAR csect; + FATFS *fs = fp->fs; + + res = validate(fs /*, fp->id*/); /* Check validity of the object */ + if (res != FR_OK) return res; + if (fp->flag & FA__ERROR) return FR_RW_ERROR; + if (fp->fptr == ofs) /* Don't seek if the target is the current position */ + return FR_OK; + if (!move_fp_window(fp,0)) goto fk_error; /* JLB not sure I need this. */ + if (ofs > fp->fsize /* In read-only mode, clip offset with the file size */ +#if !_FS_READONLY + && !(fp->flag & FA_WRITE) +#endif + ) ofs = fp->fsize; + + + /* Move file R/W pointer if needed */ + if (ofs) { + csize = (DWORD)fs->csize * SS(fs); /* Cluster size in unit of byte */ + if(ofs/csize == (fp->fptr-1)/csize) { + /* Source and Target are in the same cluster. Just reset sector fields */ + fp->fptr = ofs; + ofs-=(((DWORD)(ofs/csize))*csize); /* subtract off up to current cluster */ + } else { + fp->csect = 1; + + if(fp->fptr && ofs > fp->fptr) { + fp->fptr = (((DWORD)((fp->fptr-1)/csize))*csize); /* Set file R/W pointer to start of cluster */ + ofs-=fp->fptr; /* subtract off clusters traversed */ + clust = fp->curr_clust; /* Get current cluster */ + } else { + fp->fptr = 0; /* Set file R/W pointer to top of the file */ + clust = fp->org_clust; /* Get start cluster */ + } + +#if !_FS_READONLY + if (clust == 0) { /* If the file does not have a cluster chain, create new cluster chain */ + clust = create_chain(fs, 0); + if (clust == 1) goto fk_error; + fp->org_clust = clust; + } +#endif + if (clust) { /* If the file has a cluster chain, it can be followed */ + for (;;) { /* Loop to skip leading clusters */ + fp->curr_clust = clust; /* Update current cluster */ + if (ofs <= csize) break; +#if !_FS_READONLY + if (fp->flag & FA_WRITE) /* Check if in write mode or not */ + clust = create_chain(fs, clust); /* Force streached if in write mode */ + else +#endif + clust = get_cluster(fs, clust); /* Only follow cluster chain if not in write mode */ + if (clust == 0) { /* Stop if could not follow the cluster chain */ + ofs = csize; break; + } + if (clust < 2 || clust >= fs->max_clust) goto fk_error; + fp->fptr += csize; /* Update R/W pointer */ + ofs -= csize; + } + fp->fptr += ofs; /* Update file R/W pointer */ + } + } + csect = (CHAR)((ofs - 1) / SS(fs)); /* Sector offset in the cluster */ + fp->curr_sect = clust2sect(fs, fp->curr_clust) + csect; /* Current sector */ + fp->csect = fs->csize - csect; /* Left sector counter in the cluster */ + } else { + fp->fptr = 0; fp->csect = 1; /* Set file R/W pointer to top of the file */ + } +#if !_FS_READONLY + if (fp->fptr > fp->fsize) { /* Set changed flag if the file was 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; +} + + + + +#if _FS_MINIMIZE <= 1 +/*-----------------------------------------------------------------------*/ +/* Create a directroy object */ +/*-----------------------------------------------------------------------*/ + + +FRESULT f_opendir ( +#if _USE_DRIVE_PREFIX == 0 + FATFS *fs, /* Pointer to file system object */ +#endif + DIR *dj, /* Pointer to directory object to create */ + const UCHAR *path /* Pointer to the directory path */ +) +{ + FRESULT res; + BYTE *dir; + UCHAR fn[8+3+1]; +#if _USE_DRIVE_PREFIX != 0 + FATFS *fs; +#endif +#if _USE_LFN != 0 + DIR fileobj; + const UCHAR* spath; + UINT len; +#endif + + + res = auto_mount(&path, &fs, 0); + if (res == FR_OK) { + dj->fs = fs; +#if _USE_LFN != 0 + res = trace_path(dj, fn, path, &dir, &fileobj, &spath, &len); +#else + res = trace_path(dj, fn, path, &dir); /* trace the file path */ +#endif + if (res == FR_OK) { /* Trace completed */ + if (dir != NULL) { /* It is not the root dir */ + if (dir[DIR_Attr] & AM_DIR) { /* The entry is a directory */ + dj->clust = ((DWORD)LD_WORD(&dir[DIR_FstClusHI]) << 16) | LD_WORD(&dir[DIR_FstClusLO]); + dj->sect = clust2sect(fs, dj->clust); +#if _USE_CHDIR != 0 || _USE_CURR_DIR != 0 + dj->index = 0; +#else + dj->index = 2; +#endif + } else { /* The entry is not a directory */ + res = FR_NO_FILE; + } + } + //dj->id = fs->id; + } + } + return res; +} + + + + + +/** + * l_opendir - open a directory by cluster number + * @fs : Pointer to the FATFS structure of the target file system + * @dj : Pointer to the DIR structure to be filled + * @cluster: Number of the start cluster of the directory (0=root) + * + * This functions works like f_opendir, but instead of a path the directory + * to be opened is specified by the FATFS structure and the starting cluster + * number. Use 0 for the cluster to open the root directory. + * Always returns FR_OK. + */ +FRESULT l_opendir(FATFS* fs, DWORD cluster, DIR *dj) { + dj->fs = fs; + //dj->id = fs->id; + + if (cluster == 0) { + /* Open the root directory */ + cluster = fs->dirbase; + if (fs->fs_type == FS_FAT32) { + dj->clust = dj->sclust = cluster; + dj->sect = clust2sect(fs, cluster); + } else { + dj->clust = dj->sclust = 0; + dj->sect = cluster; + } + dj->index = 0; + } else { + /* Open a non-root directory */ + dj->clust = dj->sclust = cluster; + dj->sect = clust2sect(fs, cluster); +#if _USE_CHDIR != 0 || _USE_CURR_DIR != 0 + dj->index = 0; +#else + dj->index = 2; +#endif + } + return FR_OK; +} + + + + + +/*-----------------------------------------------------------------------*/ +/* Read Directory Entry in Sequense */ +/*-----------------------------------------------------------------------*/ + + +FRESULT f_readdir ( + DIR *dj, /* Pointer to the directory object */ + FILINFO *finfo /* Pointer to file information to return */ +) +{ + BYTE *dir, c, res; + FATFS *fs = dj->fs; +#if _USE_LFN != 0 + WORD len=0; + BYTE i,pos; +# ifdef _MAX_LFN_LENGTH + BOOL skiplfn = FALSE; +# endif + + if (finfo->lfn) { + finfo->lfn[0]=0; /* set first char to null */ +# if _USE_LFN_DBCS != 0 + finfo->lfn[1]=0; +# endif + } +#endif + + res = validate(fs /*, dj->id*/); /* Check validity of the object */ + if (res != FR_OK) return res; + + finfo->fname[0] = 0; + while (dj->sect) { + if (!move_fs_window(fs, dj->sect)) + return FR_RW_ERROR; + dir = &FSBUF.data[(dj->index & ((SS(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 _USE_LFN != 0 + if (c != 0xE5) { /* Is it a valid entry? */ + if(finfo->lfn && ((dir[DIR_Attr] & AM_LFN) == AM_LFN)) { + pos=((*dir & 0x1f)-1)*S_LFN_OFFSET; /* get offset */ +# ifdef _MAX_LFN_LENGTH + if (skiplfn || pos >= _MAX_LFN_LENGTH * S_LFN_INCREMENT) { + skiplfn = TRUE; + goto skippedlfn; + } +# endif + i=0; + while(i<13) { + if(!dir[pgm_read_byte(LFN_pos+i)] && !dir[pgm_read_byte(LFN_pos+i)+1]) + break; + if (pos >= _MAX_LFN_LENGTH) { + len = 0; + i = 0; + skiplfn = TRUE; + break; + } + finfo->lfn[pos]=dir[pgm_read_byte(LFN_pos+i)]; +# if _USE_LFN_DBCS != 0 + finfo->lfn[pos+1]=dir[pgm_read_byte(LFN_pos+i)+1]; +# endif + pos+=S_LFN_INCREMENT; + i++; + } + len+=i; + } else { +# ifdef _MAX_LFN_LENGTH + skiplfn = FALSE; +# endif + if (finfo->lfn) { + finfo->lfn[len*S_LFN_INCREMENT]=0; +# if _USE_LFN_DBCS != 0 + finfo->lfn[len*S_LFN_INCREMENT+1]=0; +# endif + } + get_fileinfo(finfo, dir); + } + } +# ifdef _MAX_LFN_LENGTH + skippedlfn: +#endif +#else + if (c != 0xE5 && ((dir[DIR_Attr] & AM_LFN) != AM_LFN)) /* Is it a valid entry? */ + get_fileinfo(finfo, dir); +#endif + if (!next_dir_entry(dj)) dj->sect = 0; /* Next entry */ + if (finfo->fname[0]) break; /* Found valid entry */ + } + + return FR_OK; +} + + + + +#if _FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Get File Status */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_stat ( +#if _USE_DRIVE_PREFIX == 0 + FATFS *fs, /* Pointer to file system object */ +#endif + const UCHAR *path, /* Pointer to the file path */ + FILINFO *finfo /* Pointer to file information to return */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir; + UCHAR fn[8+3+1]; +#if _USE_DRIVE_PREFIX != 0 + FATFS *fs; +#endif +#if _USE_LFN != 0 + DIR fileobj; + const UCHAR* spath; + UINT len; +#endif + + + res = auto_mount(&path, &fs, 0); + if (res == FR_OK) { + dj.fs = fs; + +#if _USE_LFN != 0 + res = trace_path(&dj, fn, path, &dir, &fileobj, &spath, &len); +#else + res = trace_path(&dj, fn, path, &dir); /* trace the file path */ +#endif + if (res == FR_OK) { /* Trace completed */ + if (dir) /* Found an object */ + get_fileinfo(finfo, dir); + else /* It is root dir */ + res = FR_INVALID_NAME; + } + } + + return res; +} + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Truncate File */ +/*-----------------------------------------------------------------------*/ +# if _USE_TRUNCATE +FRESULT f_truncate ( + FIL *fp /* Pointer to the file object */ +) +{ + FRESULT res; + DWORD ncl; + FATFS *fs=fp->fs; + + + res = validate(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 > 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; + 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; + } + } + } + + return FR_OK; + +ft_error: /* Abort this file due to an unrecoverable error */ + fp->flag |= FA__ERROR; + return FR_RW_ERROR; +} +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Get Number of Free Clusters, stop if maxclust found */ +/*-----------------------------------------------------------------------*/ + +FRESULT l_getfree ( + FATFS *fs, /* Pointer to file system object */ + const UCHAR *drv, /* Pointer to the logical drive number (root dir) */ + DWORD *nclust, /* Pointer to the variable to return number of free clusters */ + DWORD maxclust /* Stop after maxclust free clusters were found (0 = no limit) */ +) +{ + FRESULT res; + DWORD n, clust, sect; + BYTE fat, f, *p; + + /* Get drive number */ + res = auto_mount(&drv, &fs, 0); + if (res != FR_OK) return res; + + /* If number of free cluster is valid, return it without cluster scan. */ + if (fs->free_clust <= fs->max_clust - 2) { + *nclust = fs->free_clust; + return FR_OK; + } + + /* Get number of free clusters */ + fat = fs->fs_type; + n = 0; + if (fat == FS_FAT12) { + clust = 2; + do { + if ((WORD)get_cluster(fs, clust) == 0) n++; + } while (++clust < fs->max_clust); + } else { + clust = fs->max_clust; + sect = fs->fatbase; + f = 0; p = 0; + do { + if (maxclust && n >= maxclust) { + n = maxclust; + break; + } + if (!f) { + if (!move_fs_window(fs, sect++)) return FR_RW_ERROR; + p = FSBUF.data; + } + if (fat == FS_FAT16) { + if (LD_WORD(p) == 0) n++; + p += 2; f += 1; + } else { + if (LD_DWORD(p) == 0) n++; + p += 4; f += 2; + } + } while (--clust); + } + if (n < maxclust) { + fs->free_clust = n; +#if _USE_FSINFO + if (fat == FS_FAT32) fs->fsi_flag = 1; +#endif + } + + *nclust = n; + return FR_OK; +} + +/*-----------------------------------------------------------------------*/ +/* Get Number of Free Clusters */ +/*-----------------------------------------------------------------------*/ +FRESULT f_getfree ( +#if _USE_DRIVE_PREFIX == 0 + FATFS *fs, /* Pointer to file system object */ +#endif + const UCHAR *drv, /* Pointer to the logical drive number (root dir) */ + DWORD *nclust /* Pointer to the variable to return number of free clusters */ +#if _USE_DRIVE_PREFIX != 0 + ,FATFS **fatfs /* Pointer to pointer to corresponding file system object to return */ +#endif +) +{ + FRESULT res; +#if _USE_DRIVE_PREFIX != 0 + FATFS *fs; +#endif + + /* Get drive number */ + res = auto_mount(&drv, &fs, 0); + if (res != FR_OK) return res; +#if _USE_DRIVE_PREFIX != 0 + *fatfs = fs; +#endif + return l_getfree(fs, drv, nclust, 0); +} + + + +/*-----------------------------------------------------------------------*/ +/* Delete a File or Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_unlink ( +#if _USE_DRIVE_PREFIX == 0 + FATFS *fs, /* Pointer to file system object */ +#endif + const UCHAR *path /* Pointer to the file or directory path */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir, *sdir; + DWORD dclust, dsect; + UCHAR fn[8+3+1]; +#if _USE_DRIVE_PREFIX != 0 + FATFS *fs; +#endif +#if _USE_LFN != 0 + DIR fileobj; + const UCHAR* spath; + UINT len; +#endif + + + res = auto_mount(&path, &fs, 1); + if (res != FR_OK) return res; + dj.fs = fs; + +#if _USE_LFN != 0 + res = trace_path(&dj, fn, path, &dir, &fileobj, &spath, &len); +#else + res = trace_path(&dj, fn, path, &dir); /* trace the file path */ +#endif + if (res != FR_OK) return res; /* Trace failed */ + if (dir == NULL) return FR_INVALID_NAME; /* It is the root directory */ + if (dir[DIR_Attr] & AM_RDO) return FR_DENIED; /* It is a R/O object */ +# if _USE_CHDIR != 0 + if(fn[0]=='.') + return FR_INVALID_NAME; +# endif + dsect = FSBUF.sect; + dclust = ((DWORD)LD_WORD(&dir[DIR_FstClusHI]) << 16) | LD_WORD(&dir[DIR_FstClusLO]); + + if (dir[DIR_Attr] & AM_DIR) { /* It is a sub-directory */ +#if _USE_CHDIR != 0 || _USE_CURR_DIR != 0 + if (dclust == fs->curr_dir) /* Never delete the current directory */ + return FR_DIR_NOT_EMPTY; +#endif + dj.clust = dclust; /* Check if the sub-dir is empty or not */ + dj.sect = clust2sect(fs, dclust); + dj.index = 2; + do { + if (!move_fs_window(fs, dj.sect)) return FR_RW_ERROR; + sdir = &FSBUF.data[(dj.index & ((SS(fs) - 1) >> 5)) * 32]; + if (sdir[DIR_Name] == 0) break; + if (sdir[DIR_Name] != 0xE5 && !(sdir[DIR_Attr] & AM_VOL)) + return FR_DIR_NOT_EMPTY; /* The directory is not empty */ + } while (next_dir_entry(&dj)); + } + +#if _USE_LFN != 0 + len=(len+25)/13; + while(len--) { + if (!move_fs_window(fs, fileobj.sect)) return FR_RW_ERROR; /* Mark the directory entry 'deleted' */ + dir = &FSBUF.data[(fileobj.index & ((SS(fs) - 1) >> 5)) * 32]; + dir[DIR_Name] = 0xE5; + FSBUF.dirty = TRUE; + if (len && !next_dir_entry(&fileobj)) /* Next directory pointer */ + return FR_RW_ERROR; + } +#else + if (!move_fs_window(fs, dsect)) return FR_RW_ERROR; /* Mark the directory entry 'deleted' */ + dir[DIR_Name] = 0xE5; + FSBUF.dirty = TRUE; +#endif + if (!remove_chain(fs, dclust)) return FR_RW_ERROR; /* Remove the cluster chain */ + + return sync(fs); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Create a Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mkdir ( +#if _USE_DRIVE_PREFIX == 0 + FATFS *fs, /* Pointer to file system object */ +#endif + const UCHAR *path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir, *fw, n; + UCHAR fn[8+3+1]; + DWORD sect, dsect, dclust, pclust, tim; +#if _USE_DRIVE_PREFIX != 0 + FATFS *fs; +#endif +#if _USE_LFN != 0 + UINT len; + DIR fileobj; + const UCHAR* spath; +#endif + + + res = auto_mount(&path, &fs, 1); + if (res != FR_OK) return res; + dj.fs = fs; + +#if _USE_LFN != 0 + res = trace_path(&dj, fn, path, &dir, &fileobj, &spath, &len); +#else + res = trace_path(&dj, fn, path, &dir); /* trace the file path */ +#endif + if (res == FR_OK) return FR_EXIST; /* Any file or directory is already existing */ + if (res != FR_NO_FILE) return res; + +#if _USE_CHDIR != 0 + if(fn[0]=='.') + return FR_INVALID_NAME; +#endif +#if _USE_LFN != 0 + res = reserve_direntry(&dj, &dir,len); /* Reserve a directory entry */ +#else + res = reserve_direntry(&dj, &dir); /* Reserve a directory entry */ +#endif + if (res != FR_OK) return res; + sect = FSBUF.sect; + dclust = create_chain(fs, 0); /* Allocate a cluster for new directory table */ + if (dclust == 1) return FR_RW_ERROR; + dsect = clust2sect(fs, dclust); + if (!dsect) return FR_DENIED; + if (!move_fs_window(fs, dsect)) return FR_RW_ERROR; + + fw = FSBUF.data; + memset(fw, 0, SS(fs)); /* Clear the new directory table */ + for (n = 1; n < fs->csize; n++) { + if (disk_write(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; + 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 (fs->fs_type == FS_FAT32 && pclust == fs->dirbase) pclust = 0; + ST_WORD(&fw[32+DIR_FstClusLO], pclust); + ST_WORD(&fw[32+DIR_FstClusHI], pclust >> 16); + FSBUF.dirty = TRUE; + + if (!move_fs_window(fs, sect)) return FR_RW_ERROR; +#if _USE_LFN != 0 + if(len && add_direntry(&dj,&dir,spath,len,fn)) return FR_RW_ERROR; +#endif + memset(&dir[0], 0, 32); /* Initialize the new entry */ + memcpy(&dir[DIR_Name], fn, 8+3); /* Name */ + dir[DIR_NTres] = fn[11]; + FSBUF.dirty = TRUE; + 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); + + return sync(fs); +} + + + +#if _USE_CHDIR != 0 +/*-----------------------------------------------------------------------*/ +/* Change the current directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_chdir ( +#if _USE_DRIVE_PREFIX == 0 + FATFS *fs, /* Pointer to file system object */ +#endif + const UCHAR *path /* Pointer to the file name */ +) +{ +#if _USE_DRIVE_PREFIX != 0 + FATFS *fs; +#endif + FRESULT res; + BYTE *dir; + DIR dj; + UCHAR fn[8+3+1]; +#if _USE_LFN != 0 + UINT len; + DIR fileobj; + const UCHAR* spath; +#endif + + res = auto_mount(&path, &fs, 0); + if (res != FR_OK) return res; + dj.fs = fs; + + /* Trace the file path */ +#if _USE_LFN != 0 + res = trace_path(&dj, fn, path, &dir, &fileobj, &spath, &len); +#else + res = trace_path(&dj, fn, path, &dir); /* trace the file path */ +#endif + + if (res == FR_OK) { + if (dir == NULL) { + fs->curr_dir = 0; + } else if (dir[DIR_Attr] & AM_DIR) { + fs->curr_dir = + ((DWORD)LD_WORD(&dir[DIR_FstClusHI]) << 16) | + LD_WORD(&dir[DIR_FstClusLO]); + } else + return FR_NOT_DIRECTORY; + } + + return res; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Change File Attribute */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_chmod ( +#if _USE_DRIVE_PREFIX == 0 + FATFS *fs, /* Pointer to file system object */ +#endif + const UCHAR *path, /* Pointer to the file path */ + BYTE value, /* Attribute bits */ + BYTE mask /* Attribute mask to change */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir; + UCHAR fn[8+3+1]; +#if _USE_DRIVE_PREFIX != 0 + FATFS *fs; +#endif +#if _USE_LFN != 0 + UINT len; + DIR fileobj; + const UCHAR* spath; +#endif + + + res = auto_mount(&path, &fs, 1); + if (res == FR_OK) { + dj.fs = fs; +#if _USE_LFN != 0 + res = trace_path(&dj, fn, path, &dir, &fileobj, &spath, &len); /* trace the file path */ +#else + res = trace_path(&dj, fn, path, &dir); /* trace the file path */ +#endif + if (res == FR_OK) { /* Trace completed */ + if (!dir || fn[0]=='.') { + res = FR_INVALID_NAME; /* Root directory */ + } else { + mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ + dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + res = sync(fs); + } + } + } + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Timestamp */ +/*-----------------------------------------------------------------------*/ +#if _USE_UTIME +FRESULT f_utime ( +#if _USE_DRIVE_PREFIX == 0 + FATFS *fs, /* Pointer to file system object */ +#endif + const UCHAR *path, /* Pointer to the file/directory name */ + const FILINFO *finfo /* Pointer to the timestamp to be set */ +) +{ +#if _USE_DRIVE_PREFIX != 0 + FATFS *fs; +#endif + FRESULT res; + DIR dj; + BYTE *dir; + CHAR fn[8+3+1]; +#if _USE_LFN != 0 + UINT len; + DIR fileobj; + const UCHAR* spath; +#endif + + res = auto_mount(&path, &fs, 1); + if (res == FR_OK) { + dj.fs=fs; +#if _USE_LFN != 0 + res = trace_path(&dj, fn, path, &dir, &fileobj, &spath, &len); +#else + res = trace_path(&dj, fn, path, &dir); /* trace the file path */ +#endif + 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); + res = sync(fs); + } + } + } + return res; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Rename File/Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_rename ( +#if _USE_DRIVE_PREFIX == 0 + FATFS *fs, /* Pointer to file system object */ +#endif + const UCHAR *path_old, /* Pointer to the old name */ + const UCHAR *path_new /* Pointer to the new name */ +) +{ + FRESULT res; + DIR dj; + DWORD sect_old; + BYTE *dir_old, *dir_new, direntry[32-11]; + UCHAR fn[8+3+1]; +#if _USE_DRIVE_PREFIX != 0 + FATFS *fs; +#endif +#if _USE_LFN != 0 + UINT len_old, len_new; + DIR fileobj; + const UCHAR* spath; +#endif + + + res = auto_mount(&path_old, &fs, 1); + if (res != FR_OK) return res; + dj.fs = fs; + +#if _USE_LFN != 0 + res = trace_path(&dj, fn, path_old, &dir_old, &fileobj, &spath, &len_old); +#else + res = trace_path(&dj, fn, path_old, &dir_old); /* trace the file path */ +#endif + if (res != FR_OK) return res; /* The old object is not found */ + if (!dir_old) return FR_NO_FILE; + sect_old = FSBUF.sect; /* Save the object information */ + memcpy(direntry, &dir_old[DIR_Attr], 32-11); + +#if _USE_LFN != 0 + res = trace_path(&dj, fn, path_new, &dir_new, &fileobj, &spath, &len_new); +#else + res = trace_path(&dj, fn, path_new, &dir_new); /* trace the file path */ +#endif + 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? */ +#if _USE_CHDIR != 0 + if(fn[0]=='.') + return FR_INVALID_NAME; +#endif +#if _USE_LFN != 0 + res = reserve_direntry(&dj, &dir_new,len_new); /* Reserve a directory entry */ + if (res != FR_OK) return res; + + if(len_new && add_direntry(&dj,&dir_new,spath,len_new,fn)) /* need to get shortname, check it, and add all names */ + return FR_RW_ERROR; +#else + res = reserve_direntry(&dj, &dir_new); /* Reserve a directory entry */ + if (res != FR_OK) return res; +#endif + 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]; + FSBUF.dirty = TRUE; + +#if _USE_LFN != 0 + /* Trace it again, fileobj was clobbered while tracing the new path */ + res = trace_path(&dj, fn, path_old, &dir_old, &fileobj, &spath, &len_old); + len_old=(len_old+25)/13; + while(len_old--) { + if (!move_fs_window(fs, fileobj.sect)) return FR_RW_ERROR; /* Mark the directory entry 'deleted' */ + dir_old = &FSBUF.data[(fileobj.index & ((SS(fs) - 1) >> 5)) * 32]; + dir_old[DIR_Name] = 0xE5; + FSBUF.dirty = TRUE; + if (!next_dir_entry(&fileobj)) /* Next directory pointer */ + return FR_RW_ERROR; + } +#else + if (!move_fs_window(fs, sect_old)) return FR_RW_ERROR; /* Remove old entry */ + dir_old[DIR_Name] = 0xE5; +#endif + + return sync(fs); +} + +#endif /* !_FS_READONLY */ +#endif /* _FS_MINIMIZE == 0 */ +#endif /* _FS_MINIMIZE <= 1 */ +#endif /* _FS_MINIMIZE <= 2 */ + + + +#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 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] */ +) +{ + 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; + FATFS *fs; + DSTATUS stat; + + + /* Check validity of the parameters */ + if (drv >= _LOGICAL_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]; + if (!fs) return FR_NOT_ENABLED; + fs->fs_type = 0; + drv = LD2PD(drv); + + /* Get disk statics */ + stat = disk_initialize(drv); + if (stat & STA_NOINIT) return FR_NOT_READY; + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; + if (disk_ioctl(drv, GET_SECTOR_COUNT, &n_part) != RES_OK || n_part < MIN_SECTOR) + return FR_MKFS_ABORTED; + 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 (disk_ioctl(drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK + || SS(fs) > S_MAX_SIZ + || SS(fs) > allocsize) + return FR_MKFS_ABORTED; +#endif + allocsize /= SS(fs); /* Number of sectors per cluster */ + + /* Pre-compute number of clusters and FAT type */ + n_clust = n_part / allocsize; + fmt = FS_FAT12; + if (n_clust >= 0xFF5) fmt = FS_FAT16; + if (n_clust >= 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_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_rsv = 1 + partition; + n_dir = N_ROOTDIR * 32 / SS(fs); + break; + default: + n_fat = ((n_clust * 4) + 8 + SS(fs) - 1) / SS(fs); + n_rsv = 33 - partition; + n_dir = 0; + } + b_fat = b_part + n_rsv; /* FATs start sector */ + b_dir = b_fat + n_fat * N_FATS; /* Directory start sector */ + b_data = b_dir + n_dir; /* Data start sector */ + + /* Align data start sector to erase block boundary (for flash memory media) */ + if (disk_ioctl(drv, GET_BLOCK_SIZE, &n) != RES_OK) return FR_MKFS_ABORTED; + n = (b_data + n - 1) & ~(n - 1); + n_fat += (n - b_data) / N_FATS; + /* 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)) + return FR_MKFS_ABORTED; + + /* Create partition table if needed */ + if (!partition) { + DWORD n_disk = b_part + n_part; + + tbl = &FSBUF.data[MBR_Table]; + ST_DWORD(&tbl[0], 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; + tbl[6] = (BYTE)((n_disk >> 2) | 63); + } else { + ST_WORD(&tbl[6], 0xFFFF); + } + tbl[5] = 254; + if (fmt != FS_FAT32) /* System ID */ + 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 */ + if (disk_write(drv, FSBUF.data, 0, 1) != RES_OK) + return FR_RW_ERROR; + } + + /* Create boot record */ + tbl = FSBUF.data; /* 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 */ + tbl[BPB_SecPerClus] = (BYTE)allocsize; /* Sectors per cluster */ + 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 */ + if (n_part < 0x10000) { /* Number of total sectors */ + ST_WORD(&tbl[BPB_TotSec16], n_part); + } else { + 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 */ + 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 */ + 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 */ + } 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) */ + 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 */ + } + ST_WORD(&tbl[BS_55AA], 0xAA55); /* Signature */ + if (disk_write(drv, tbl, b_part+0, 1) != RES_OK) + return FR_RW_ERROR; + 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 */ + if (fmt != FS_FAT32) { + n = (fmt == FS_FAT12) ? 0x00FFFFF8 : 0xFFFFFFF8; + ST_DWORD(&tbl[0], 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 */ + } + 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 */ + for (n = 1; n < n_fat; n++) { + if (disk_write(drv, tbl, b_fat++, 1) != RES_OK) + return FR_RW_ERROR; + } + } + + /* Initialize Root directory */ + m = (BYTE)((fmt == FS_FAT32) ? allocsize : n_dir); + do { + if (disk_write(drv, tbl, b_fat++, 1) != RES_OK) + return FR_RW_ERROR; + } 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); + 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; +} + +#endif /* _USE_MKFS && !_FS_READONLY */ + + + + +#if _USE_STRFUNC >= 1 +/*-----------------------------------------------------------------------*/ +/* Get a string from the file */ +/*-----------------------------------------------------------------------*/ +char* fgets ( + char* buff, /* Pointer to the string buffer to read */ + int len, /* Size of string buffer */ + FIL* fil /* Pointer to the file object */ +) +{ + int i = 0; + char *p = buff; + UINT rc; + + + while (i < len - 1) { /* Read bytes until buffer gets filled */ + f_read(fil, p, 1, &rc); + if (rc != 1) break; /* Break when no data to read */ +#if _USE_STRFUNC >= 2 + if (*p == '\r') continue; /* Strip '\r' */ +#endif + i++; + 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. */ +} + + + +#if !_FS_READONLY +#include +/*-----------------------------------------------------------------------*/ +/* Put a character to the file */ +/*-----------------------------------------------------------------------*/ +int fputc ( + int chr, /* A character to be output */ + FIL* fil /* Ponter to the file object */ +) +{ + UINT bw; + char c; + + +#if _USE_STRFUNC >= 2 + if (chr == '\n') fputc ('\r', fil); /* LF -> CRLF conversion */ +#endif + if (!fil) { /* Special value may be used to switch the destination to any other device */ + /* put_console(chr); */ + return chr; + } + c = (char)chr; + f_write(fil, &c, 1, &bw); /* Write a byte to the file */ + return bw ? chr : EOF; /* Return the resulut */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a string to the file */ +/*-----------------------------------------------------------------------*/ +int fputs ( + const char* str, /* Pointer to the string to be output */ + FIL* fil /* Pointer to the file object */ +) +{ + int n; + + + for (n = 0; *str; str++, n++) { + if (fputc(*str, fil) == EOF) return EOF; + } + return n; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a formatted string to the file */ +/*-----------------------------------------------------------------------*/ +int fprintf ( + FIL* fil, /* Pointer to the file object */ + const char* str, /* Pointer to the format string */ + ... /* Optional arguments... */ +) +{ + va_list arp; + UCHAR c, f, r; + ULONG val; + char s[16]; + int i, w, res, cc; + + + va_start(arp, str); + + for (cc = res = 0; cc != EOF; res += cc) { + c = *str++; + if (c == 0) break; /* End of string */ + if (c != '%') { /* Non escape cahracter */ + cc = fputc(c, fil); + if (cc != EOF) cc = 1; + continue; + } + w = f = 0; + c = *str++; + if (c == '0') { /* Flag: '0' padding */ + f = 1; c = *str++; + } + while (c >= '0' && c <= '9') { /* Precision */ + w = w * 10 + (c - '0'); + c = *str++; + } + if (c == 'l') { /* Prefix: Size is long int */ + f |= 2; c = *str++; + } + if (c == 's') { /* Type is string */ + cc = fputs(va_arg(arp, char*), fil); + continue; + } + if (c == 'c') { /* Type is character */ + cc = fputc(va_arg(arp, char), fil); + if (cc != EOF) cc = 1; + continue; + } + r = 0; + if (c == 'd') r = 10; /* Type is signed decimal */ + if (c == 'u') r = 10; /* Type is unsigned decimal */ + if (c == 'X') r = 16; /* Type is unsigned hexdecimal */ + if (r == 0) break; /* Unknown type */ + if (f & 2) { /* Get the value */ + val = (ULONG)va_arg(arp, long); + } else { + val = (c == 'd') ? (ULONG)(long)va_arg(arp, int) : (ULONG)va_arg(arp, unsigned int); + } + /* Put numeral string */ + if (c == 'd') { + if (val >= 0x80000000) { + val = 0 - val; + f |= 4; + } + } + i = sizeof(s) - 1; s[i] = 0; + do { + c = (UCHAR)(val % r + '0'); + if (c > '9') c += 7; + s[--i] = c; + val /= r; + } while (i && val); + 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); + } + + va_end(arp); + return (cc == EOF) ? cc : res; +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_STRFUNC >= 1*/ diff --git a/src/ff.h b/src/ff.h index 567c853..b761a1f 100644 --- a/src/ff.h +++ b/src/ff.h @@ -1,547 +1,447 @@ -/*---------------------------------------------------------------------------/ -/ FatFs - FAT file system module include file R0.07a (C)ChaN, 2009 -/----------------------------------------------------------------------------/ -/ 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 commercial developments under license policy of following trems. -/ -/ Copyright (C) 2009, ChaN, all right reserved. -/ -/ * 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. -/----------------------------------------------------------------------------*/ - -#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 _FS_READONLY 0 -/* 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 0 -/* 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. */ - - -#define _FS_TINY 0 -/* 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 _USE_STRFUNC 0 -/* To enable string functions, set _USE_STRFUNC to 1 or 2. */ - - -#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 _DRIVES 1 -/* Number of volumes (logical drives) to be used. */ - - -#define _MAX_SS 512 -/* Maximum sector size to be handled. (512/1024/2048/4096) */ -/* 512 for memroy card and hard disk, 1024 for floppy disk, 2048 for MO disk */ - - -#define _MULTI_PARTITION 0 -/* When _MULTI_PARTITION is set to 0, each volume is bound to the same physical -/ drive number and can mount only first primaly partition. When it is set to 1, -/ each volume is tied to the partitions listed in Drives[]. */ - - -#define _CODE_PAGE 850 -/* 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 _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 REENTRANT. -/ 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 added to -/ the project. */ - - -#define _FS_REENTRANT 0 -#define _TIMEOUT 1000 /* Timeout period in unit of time ticks */ -#define _SYNC_t HANDLE /* Type of sync object used on the OS. */ - /* e.g. HANDLE, OS_EVENT*, ID and etc.. */ -/* To make the FatFs module re-entrant, set _FS_REENTRANT to 1 and add user -/ provided synchronization handlers, ff_req_grant, ff_rel_grant, -/ ff_del_syncobj and ff_cre_syncobj function to the project. */ - - - -/* End of configuration options. Do not change followings without care. */ -/*--------------------------------------------------------------------------*/ - - - -/* Definitions corresponds to multiple sector size */ - -#if _MAX_SS == 512 -#define SS(fs) 512 -#else -#if _MAX_SS == 1024 || _MAX_SS == 2048 || _MAX_SS == 4096 -#define SS(fs) ((fs)->s_size) -#else -#error Sector size must be 512, 1024, 2048 or 4096. -#endif -#endif - - - -/* File system object structure */ - -typedef struct _FATFS { - 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; - WORD id; /* File system mount ID */ - WORD n_rootdir; /* Number of root directory entries (0 on FAT32) */ -#if _FS_REENTRANT - _SYNC_t sobj; /* Identifier of sync object */ -#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 number */ - FATFS* fs; /* Pointer to the owner file system object */ - 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 { - 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 - 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 -} FIL; - - - -/* File status structure */ - -typedef struct _FILINFO { - 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 /* Multiple partition configuration */ - -typedef struct _PARTITION { - 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 configuration */ - -#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_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 */ - -FRESULT f_mount (BYTE, FATFS*); /* Mount/Unmount a logical drive */ -FRESULT f_open (FIL*, const char*, BYTE); /* Open or create a file */ -FRESULT f_read (FIL*, void*, UINT, UINT*); /* Read data from a file */ -FRESULT f_write (FIL*, const void*, UINT, UINT*); /* Write data to a file */ -FRESULT f_lseek (FIL*, DWORD); /* Move file pointer of a file object */ -FRESULT f_close (FIL*); /* Close an open file object */ -FRESULT f_opendir (DIR*, const char*); /* Open an existing directory */ -FRESULT f_readdir (DIR*, FILINFO*); /* Read a directory item */ -FRESULT f_stat (const char*, FILINFO*); /* Get file status */ -FRESULT f_getfree (const char*, DWORD*, FATFS**); /* Get number of free clusters on the drive */ -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 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 -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 -#endif -#endif - - - -/*--------------------------------------------------------------*/ -/* User defined functions */ - -/* Real time clock */ -#if !_FS_READONLY -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) */ -#endif - -/* Unicode - OEM code conversion */ -#if _USE_LFN -WCHAR ff_convert (WCHAR, UINT); -#endif - -/* Sync functions */ -#if _FS_REENTRANT -BOOL ff_cre_syncobj(BYTE, _SYNC_t*); -BOOL ff_del_syncobj(_SYNC_t); -BOOL ff_req_grant(_SYNC_t); -void ff_rel_grant(_SYNC_t); -#endif - - - -/*--------------------------------------------------------------*/ -/* Flags and offset address */ - - -/* File access control and file status flags (FIL.flag) */ - -#define FA_READ 0x01 -#define FA_OPEN_EXISTING 0x00 -#if _FS_READONLY == 0 -#define FA_WRITE 0x02 -#define FA_CREATE_NEW 0x04 -#define FA_CREATE_ALWAYS 0x08 -#define FA_OPEN_ALWAYS 0x10 -#define FA__WRITTEN 0x20 -#define FA__DIRTY 0x40 -#endif -#define FA__ERROR 0x80 - - -/* FAT sub type (FATFS.fs_type) */ - -#define FS_FAT12 1 -#define FS_FAT16 2 -#define FS_FAT32 3 - - -/* File attribute bits for directory entry */ - -#define AM_RDO 0x01 /* Read only */ -#define AM_HID 0x02 /* Hidden */ -#define AM_SYS 0x04 /* System */ -#define AM_VOL 0x08 /* Volume label */ -#define AM_LFN 0x0F /* LFN entry */ -#define AM_DIR 0x10 /* Directory */ -#define AM_ARC 0x20 /* Archive */ -#define AM_MASK 0x3F /* Mask of defined bits */ - - -/* 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 -#define BPB_BytsPerSec 11 -#define BPB_SecPerClus 13 -#define BPB_RsvdSecCnt 14 -#define BPB_NumFATs 16 -#define BPB_RootEntCnt 17 -#define BPB_TotSec16 19 -#define BPB_Media 21 -#define BPB_FATSz16 22 -#define BPB_SecPerTrk 24 -#define BPB_NumHeads 26 -#define BPB_HiddSec 28 -#define BPB_TotSec32 32 -#define BS_55AA 510 - -#define BS_DrvNum 36 -#define BS_BootSig 38 -#define BS_VolID 39 -#define BS_VolLab 43 -#define BS_FilSysType 54 - -#define BPB_FATSz32 36 -#define BPB_ExtFlags 40 -#define BPB_FSVer 42 -#define BPB_RootClus 44 -#define BPB_FSInfo 48 -#define BPB_BkBootSec 50 -#define BS_DrvNum32 64 -#define BS_BootSig32 66 -#define BS_VolID32 67 -#define BS_VolLab32 71 -#define BS_FilSysType32 82 - -#define FSI_LeadSig 0 -#define FSI_StrucSig 484 -#define FSI_Free_Count 488 -#define FSI_Nxt_Free 492 - -#define MBR_Table 446 - -#define DIR_Name 0 -#define DIR_Attr 11 -#define DIR_NTres 12 -#define DIR_CrtTime 14 -#define DIR_CrtDate 16 -#define DIR_FstClusHI 20 -#define DIR_WrtTime 22 -#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 _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) -#else /* Use byte-by-byte access to the FAT structure */ -#define LD_WORD(ptr) (WORD)(((WORD)*(BYTE*)((ptr)+1)<<8)|(WORD)*(BYTE*)(ptr)) -#define LD_DWORD(ptr) (DWORD)(((DWORD)*(BYTE*)((ptr)+3)<<24)|((DWORD)*(BYTE*)((ptr)+2)<<16)|((WORD)*(BYTE*)((ptr)+1)<<8)|*(BYTE*)(ptr)) -#define ST_WORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *(BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8) -#define ST_DWORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *(BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8); *(BYTE*)((ptr)+2)=(BYTE)((DWORD)(val)>>16); *(BYTE*)((ptr)+3)=(BYTE)((DWORD)(val)>>24) -#endif - - -#endif /* _FATFS */ +/*--------------------------------------------------------------------------/ +/ 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. +/ +/ Copyright (C) 2008, 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. +/ +/---------------------------------------------------------------------------*/ + +#ifndef _FATFS + +#define _MCU_ENDIAN 1 +/* 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 be set to 1 for good code efficiency. */ + +#define _FS_READONLY 0 +/* 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 0 +/* 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. */ + +#define _USE_STRFUNC 0 +/* To enable string functions, set _USE_STRFUNC to 1 or 2. */ + +#define _DRIVES 1 +/*MAX_DRIVES*/ +/* Number of physical drives to be used. This affects the size of internal + * table. (when using _USE_DRIVE_PREFIX */ + +#define _USE_MKFS 0 +/* When _USE_MKFS is set to 1 and _FS_READONLY is set to 0, f_mkfs function is +/ enabled. */ + +#define _MULTI_PARTITION 0 +/* When _MULTI_PARTITION is set to 0, each logical drive is bound to the same +/ physical drive number and can mount only 1st primary partition. +/ +/ When it is set to 1, the low _PARTITION_MASK bits of each partition represent +/ the partition and the high (8-_PARTITION_MASK) bits represent the physical +/ drive */ + +#define _PARTITION_MASK 4 + +#define _USE_FSINFO 1 +/* To enable FSInfo support on FAT32 volume, set _USE_FSINFO to 1. */ + +#define _USE_SJIS 0 +/* 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 0 +/* 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. */ + +#define _USE_CHDIR 0 + +#define _USE_CURR_DIR 1 + +#define _USE_LFN 1 + +/* Maximum number of characters to return for a LFN */ +/* The buffer used for FILINFO.lfn must be at least */ +/* _MAX_LFN_LENGTH+1 characters long! */ +/* Note that if _USE_LFN_DBCS is set, this value */ +/* represents the characters needed, not bytes */ +#define _MAX_LFN_LENGTH 255 + +/* When _USE_LFN_DBCS is set to 1, FILINFO.lfn will contain a DBCS string, not +/ a simple ASCII string */ +#define _USE_LFN_DBCS 0 + +/* When set to 1, All FIL objects will use the buffer. This reduces memory +/ requirements as open files will only require space for a FIL object, but +/ operate slower. When set, ff.c will behave like tff.c, but will allow +/ multiple filesystems. */ +#define _USE_FS_BUF 1 + +/* When set to 1, All objects will use a static buffer. This reduces memory +/ requirements to the absolute minimum ~512 bytes for the buffer, but will +/ operate slower. This option can only be set if _USE_FS_BUF is set. */ +#define _USE_1_BUF 1 + +/* If set to 1, FatFs will manage the FATFS structures after mounting. If +/ set to 0, the caller must send the correct drive FATFS structure for each +/ call. Normally, this should be set to 1, but if the caller wants to use +/ low level l_* functions or skip sending the drive number in the path string, +/ this must be turned off. */ +#define _USE_DRIVE_PREFIX 1 + +/* If set to 1, FatFS will delay mounting the drive until first use. Normally, +/ this should be turned on. However, it cannot be used with +/ _USE_DRIVE_PREFIX = 0 */ +#define _USE_DEFERRED_MOUNT 0 + +/* New features in 0.05a, not required yet */ +#define _USE_TRUNCATE 0 +#define _USE_UTIME 0 + +#include "integer.h" + +#if _USE_LFN_DBCS != 0 +#define S_LFN_OFFSET 26 +#define S_LFN_INCREMENT 2 +#else +#define S_LFN_OFFSET 13 +#define S_LFN_INCREMENT 1 +#endif + +/* Definitions corresponds to multiple sector size (not tested) */ +#define S_MAX_SIZ 512U /* Do not change */ +#if S_MAX_SIZ > 512U +#define SS(fs) ((fs)->s_size) +#else +#define SS(fs) 512U +#endif + +#if _USE_1_BUF == 1 && _USE_FS_BUF == 0 +#error You can only use 1_BUF with _USE_FS_BUF at present +#define _USE_1_BUF 0 +#endif + +typedef struct _BUF { + DWORD sect; + BYTE dirty; /* dirty flag (1:must be written back) */ +//BYTE pad1; +#if _USE_1_BUF != 0 + struct _FATFS *fs; +#endif + BYTE data[S_MAX_SIZ]; /* Disk access window for Directory/FAT */ +} BUF; + +/* File system object structure */ +typedef struct _FATFS { + //WORD id; /* File system mount ID */ + WORD n_rootdir; /* Number of root directory entries */ + 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 _USE_CHDIR != 0 || _USE_CURR_DIR != 0 + DWORD curr_dir; +#endif +#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 */ +#if _USE_1_BUF == 0 + BUF buf; +#endif +} FATFS; + +/* Directory object structure */ +typedef struct _DIR { + //WORD id; /* Owner file system mount ID */ + WORD index; /* Current index */ + FATFS* fs; /* Pointer to the owner file system object */ + DWORD sclust; /* Start cluster */ + DWORD clust; /* Current cluster */ + DWORD sect; /* Current sector */ +} 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 */ +#if _FS_READONLY == 0 + DWORD dir_sect; /* Sector containing the directory entry */ + BYTE* dir_ptr; /* Ponter to the directory entry in the window */ +#endif +#if _USE_LESS_BUF == 0 && _USE_1_BUF == 0 + BUF buf; /* File R/W buffer */ +#endif +} FIL; + + +/* File status structure */ +typedef struct _FILINFO { + DWORD fsize; /* Size */ + WORD fdate; /* Date */ + WORD ftime; /* Time */ + BYTE fattrib; /* Attribute */ + DWORD clust; /* Start cluster */ + UCHAR fname[8+1+3+1]; /* Name (8.3 format) */ +#if _USE_LFN != 0 + UCHAR* lfn; +#endif +} FILINFO; + + + +/* Definitions corresponds to multi partition */ + +#if _MULTI_PARTITION != 0 /* Multiple partition cfg */ + +#define LD2PD(drv) (drv >> (8-_PARTITION_MASK)) /* Get physical drive# */ +#define LD2PT(drv) (drv & ((1<<_PARTITION_MASK)-1)) /* Get partition# */ +#define _LOGICAL_DRIVES (_DRIVES * (1<<_PARTITION_MASK)) + +#else /* Single partition cfg */ + +#define LD2PD(drv) (drv) /* Physical drive# is equal to logical drive# */ +#define LD2PT(drv) 0 /* Always mounts the 1st partition */ +#define _LOGICAL_DRIVES _DRIVES + +#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_IS_DIRECTORY, /* 13 */ + FR_IS_READONLY, /* 14 */ + FR_DIR_NOT_EMPTY, /* 15 */ + FR_NOT_DIRECTORY /* 16 */ +} FRESULT; + + + +/*-----------------------------------------------------*/ +/* FatFs module application interface */ + +#if _USE_DRIVE_PREFIX == 0 +FRESULT f_mount (BYTE, FATFS*); /* Mount/Unmount a logical drive */ +FRESULT f_open (FATFS*, FIL*, const UCHAR*, BYTE); /* Open or create a file */ +FRESULT f_read (FIL*, void*, UINT, UINT*); /* Read data from a file */ +FRESULT f_write (FIL*, const void*, UINT, UINT*); /* Write data to a file */ +FRESULT f_lseek (FIL*, DWORD); /* Move file pointer of a file object */ +FRESULT f_close (FIL*); /* Close an open file object */ +FRESULT f_opendir (FATFS*, DIR*, const UCHAR*); /* Open an existing directory */ +FRESULT f_readdir (DIR*, FILINFO*); /* Read a directory item */ +FRESULT f_stat (FATFS*, const UCHAR*, FILINFO*); /* Get file status */ +FRESULT f_getfree (FATFS*, const UCHAR*, DWORD*); /* Get number of free clusters on the drive */ +FRESULT f_sync (FIL*); /* Flush cached data of a writing file */ +FRESULT f_unlink (FATFS*, const UCHAR*); /* Delete an existing file or directory */ +FRESULT f_mkdir (FATFS*, const UCHAR*); /* Create a new directory */ +FRESULT f_chmod (FATFS*, const UCHAR*, BYTE, BYTE); /* Change file/dir attriburte */ +FRESULT f_rename (FATFS*, const UCHAR*, const UCHAR*); /* Rename/Move a file or directory */ +FRESULT f_mkfs (BYTE, BYTE, WORD); /* Create a file system on the drive */ +FRESULT f_chdir (FATFS*, const UCHAR*); /* Change current directory */ + +#else + +FRESULT f_mount (BYTE, FATFS*); /* Mount/Unmount a logical drive */ +FRESULT f_open (FIL*, const UCHAR*, BYTE); /* Open or create a file */ +FRESULT f_read (FIL*, void*, UINT, UINT*); /* Read data from a file */ +FRESULT f_write (FIL*, const void*, UINT, UINT*); /* Write data to a file */ +FRESULT f_lseek (FIL*, DWORD); /* Move file pointer of a file object */ +FRESULT f_close (FIL*); /* Close an open file object */ +FRESULT f_opendir (DIR*, const UCHAR*); /* Open an existing directory */ +FRESULT f_readdir (DIR*, FILINFO*); /* Read a directory item */ +FRESULT f_stat (const UCHAR*, FILINFO*); /* Get file status */ +FRESULT f_getfree (const UCHAR*, DWORD*, FATFS**); /* Get number of free clusters on the drive */ +FRESULT f_sync (FIL*); /* Flush cached data of a writing file */ +FRESULT f_unlink (const UCHAR*); /* Delete an existing file or directory */ +FRESULT f_mkdir (const UCHAR*); /* Create a new directory */ +FRESULT f_chmod (const UCHAR*, BYTE, BYTE); /* Change file/dir attriburte */ +FRESULT f_rename (const UCHAR*, const UCHAR*); /* Rename/Move a file or directory */ +FRESULT f_mkfs (BYTE, BYTE, WORD); /* Create a file system on the drive */ +FRESULT f_chdir (const UCHAR*); /* Change current directory */ + +#endif + +/* Low Level functions */ +FRESULT l_opendir(FATFS* fs, DWORD cluster, DIR *dirobj); /* Open an existing directory by its start cluster */ +FRESULT l_opencluster(FATFS *fs, FIL *fp, DWORD clust); /* Open a cluster by number as a read-only file */ +FRESULT l_getfree (FATFS*, const UCHAR*, DWORD*, DWORD); /* Get number of free clusters on the drive, limited */ + +#if _USE_STRFUNC +#define feof(fp) ((fp)->fptr == (fp)->fsize) +#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 + +/* User defined function to give a current time to fatfs module */ + +#if CONFIG_RTC_VARIANT > 0 +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) */ +#else +/* Fixed time: 1982-08-31 0:00:00, same month as the introduction of the C64 */ +# define get_fattime() 0x51f0000 +#endif + +/* File access control and file status flags (FIL.flag) */ + +#define FA_READ 0x01 +#define FA_OPEN_EXISTING 0x00 +#if _FS_READONLY == 0 +#define FA_WRITE 0x02 +#define FA_CREATE_NEW 0x04 +#define FA_CREATE_ALWAYS 0x08 +#define FA_OPEN_ALWAYS 0x10 +#define FA__WRITTEN 0x20 +#define FA__DIRTY 0x40 +#endif +#define FA__ERROR 0x80 + + +/* FAT sub type (FATFS.fs_type) */ + +#define FS_FAT12 1 +#define FS_FAT16 2 +#define FS_FAT32 3 + + +/* File attribute bits for directory entry */ + +#define AM_RDO 0x01 /* Read only */ +#define AM_HID 0x02 /* Hidden */ +#define AM_SYS 0x04 /* System */ +#define AM_VOL 0x08 /* Volume label */ +#define AM_LFN 0x0F /* LFN entry */ +#define AM_DIR 0x10 /* Directory */ +#define AM_ARC 0x20 /* Archive */ + + + +/* Offset of FAT structure members */ + +#define BS_jmpBoot 0 +#define BS_OEMName 3 +#define BPB_BytsPerSec 11 +#define BPB_SecPerClus 13 +#define BPB_RsvdSecCnt 14 +#define BPB_NumFATs 16 +#define BPB_RootEntCnt 17 +#define BPB_TotSec16 19 +#define BPB_Media 21 +#define BPB_FATSz16 22 +#define BPB_SecPerTrk 24 +#define BPB_NumHeads 26 +#define BPB_HiddSec 28 +#define BPB_TotSec32 32 +#define BS_55AA 510 + +#define BS_DrvNum 36 +#define BS_BootSig 38 +#define BS_VolID 39 +#define BS_VolLab 43 +#define BS_FilSysType 54 + +#define BPB_FATSz32 36 +#define BPB_ExtFlags 40 +#define BPB_FSVer 42 +#define BPB_RootClus 44 +#define BPB_FSInfo 48 +#define BPB_BkBootSec 50 +#define BS_DrvNum32 64 +#define BS_BootSig32 66 +#define BS_VolID32 67 +#define BS_VolLab32 71 +#define BS_FilSysType32 82 + +#define FSI_LeadSig 0 +#define FSI_StrucSig 484 +#define FSI_Free_Count 488 +#define FSI_Nxt_Free 492 + +#define MBR_Table 446 + +#define DIR_Name 0 +#define DIR_Attr 11 +#define DIR_NTres 12 +#define DIR_Chksum 13 +#define DIR_CrtTime 14 +#define DIR_CrtDate 16 +#define DIR_FstClusHI 20 +#define DIR_WrtTime 22 +#define DIR_WrtDate 24 +#define DIR_FstClusLO 26 +#define DIR_FileSize 28 + + + +/* Multi-byte word access macros */ + +#if _MCU_ENDIAN == 1 /* Use word access */ +#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 */ +#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/src/fileops.c b/src/fileops.c index 3755b2b..6797290 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -6,12 +6,16 @@ #include "ff.h" #include "fileops.h" +WCHAR ff_convert(WCHAR w, UINT dir) { + return w; +} + void file_init() { f_mount(0, &fatfs); } void file_open(char* filename, BYTE flags) { - file_res = f_open(&file_handle, filename, flags); + file_res = f_open(&file_handle, (unsigned char*)filename, flags); } void file_close() { diff --git a/src/fpga.c b/src/fpga.c index 59eb110..240ae1d 100644 --- a/src/fpga.c +++ b/src/fpga.c @@ -39,15 +39,16 @@ #include "uart.h" #include "sdcard.h" #include "diskio.h" +#include "integer.h" #include "ff.h" #include "fileops.h" #include "fpga_spi.h" #include "spi.h" #include "avrcompat.h" -DWORD get_fattime(void) { +/*DWORD get_fattime(void) { return 0L; -} +}*/ void set_prog_b(uint8_t val) { if(val) { PORTD |= _BV(PD3); diff --git a/src/integer.h b/src/integer.h index c120dc7..bd6ce0f 100644 --- a/src/integer.h +++ b/src/integer.h @@ -17,6 +17,7 @@ typedef uint8_t BYTE; typedef int16_t SHORT; typedef uint16_t USHORT; typedef uint16_t WORD; +typedef uint16_t WCHAR; /* These types are assumed as 32-bit integer */ typedef int32_t LONG; diff --git a/src/main.c b/src/main.c index fa6af98..de6100c 100644 --- a/src/main.c +++ b/src/main.c @@ -47,8 +47,7 @@ #include "fpga_spi.h" #include "spi.h" #include "avrcompat.h" - -char stringbuf[100]; +#include "filetypes.h" /* Make sure the watchdog is disabled as soon as possible */ /* Copy this code to your bootloader if you use one and your */ @@ -140,7 +139,6 @@ int main(void) { uart_putcrlf(); file_init(); - FATFS fatfs; f_mount(0,&fatfs); set_busy_led(1); @@ -160,12 +158,29 @@ int main(void) { uart_putc('['); load_sram("/test.srm"); uart_putc(']'); + *fs_path=0; + uint16_t curr_dir_id = scan_dir(fs_path, 0); // generate files footprint + dprintf("curr dir id = %x\n", curr_dir_id); + uint16_t saved_dir_id; + if((get_db_id(&saved_dir_id) != FR_OK) // no database? + || saved_dir_id != curr_dir_id) { // files changed? + dprintf("saved dir id = %x\n", saved_dir_id); + _delay_ms(50); + dprintf("rebuilding database..."); + _delay_ms(50); + curr_dir_id = scan_dir(fs_path, 1); // then rebuild database + sram_writeblock(&curr_dir_id, 0x600000, 2); + save_sram("/sd2snes/sd2snes.db", 0x10000, 0x600000); + dprintf("done\n"); + } + set_busy_led(0); set_avr_ena(1); _delay_ms(100); - uart_puts_P(PSTR("SNES GO!")); + uart_puts_P(PSTR("SNES GO!\n")); snes_reset(0); - + + while(1) { snes_main_loop(); } @@ -173,14 +188,8 @@ int main(void) { /* HERE BE LIONS */ while(1) { - SPI_SS_HIGH(); - FPGA_SS_LOW(); - spiTransferByte(0x00); - spiTransferByte(0x00); - spiTransferByte(0x7f); - spiTransferByte(0xc0); - FPGA_SS_HIGH(); - FPGA_SS_LOW(); + set_avr_addr(0x600000); + spi_fpga(); spiTransferByte(0x81); // read w/ increment... hopefully spiTransferByte(0x00); // 1 dummy read uart_putcrlf(); @@ -202,7 +211,7 @@ while(1) { } // set_avr_bank(3); } - FPGA_SS_HIGH(); + spi_sd(); } while(1); } diff --git a/src/memory.c b/src/memory.c index 23bbd2a..325c2fd 100644 --- a/src/memory.c +++ b/src/memory.c @@ -14,32 +14,32 @@ #include "fpga_spi.h" #include "avrcompat.h" #include "led.h" -#include "filetypes.h" +#include "smc.h" #include "fpga_spi.h" char* hex = "0123456789ABCDEF"; void sram_readblock(void* buf, uint32_t addr, uint16_t size) { uint16_t count=size; - void* tgt = buf; + uint8_t* tgt = buf; set_avr_addr(addr); spi_fpga(); spiTransferByte(0x81); // READ spiTransferByte(0x00); // dummy while(count--) { - *((uint8_t*)tgt++) = spiTransferByte(0x00); + *(tgt++) = spiTransferByte(0x00); } spi_sd(); } void sram_writeblock(void* buf, uint32_t addr, uint16_t size) { uint16_t count=size; - void* src = buf; + uint8_t* src = buf; set_avr_addr(addr); spi_fpga(); spiTransferByte(0x91); // WRITE while(count--) { - spiTransferByte(*((uint8_t*)src++)); + spiTransferByte(*src++); } spiTransferByte(0x00); // dummy spi_sd(); @@ -157,6 +157,9 @@ void save_sram(char* filename, uint32_t sram_size, uint32_t base_addr) { } spi_sd(); num = file_write(); + if(file_res) { + uart_putc(0x30+file_res); + } } file_close(); } diff --git a/src/memory.h b/src/memory.h index 092843a..21e95b2 100644 --- a/src/memory.h +++ b/src/memory.h @@ -5,6 +5,8 @@ #define MEMORY_H uint32_t load_rom(char* filename); uint32_t load_sram(char* filename); + void sram_readblock(void* buf, uint32_t addr, uint16_t size); + void sram_writeblock(void* buf, uint32_t addr, uint16_t size); void save_sram(char* filename, uint32_t sram_size, uint32_t base_addr); uint32_t calc_sram_crc(uint32_t base_addr, uint32_t size); #endif diff --git a/src/smc.c b/src/smc.c new file mode 100644 index 0000000..2c3c777 --- /dev/null +++ b/src/smc.c @@ -0,0 +1,111 @@ +// insert cool lengthy disclaimer here +// filetypes.c: File support + +#include +#include +#include "fileops.h" +#include "config.h" +#include "uart.h" +#include "smc.h" + +uint32_t hdr_addr[6] = {0xffb0, 0x101b0, 0x7fb0, 0x81b0, 0x40ffb0, 0x4101b0}; +uint8_t countAllASCII(uint8_t* data, int size) { + uint8_t res = 0; + do { + size--; + if(data[size] >= 0x20 && data[size] <= 0x7e) { + res++; + } + } while (size); + return res; +} + +uint8_t countAllJISX0201(uint8_t* data, int size) { + uint8_t res = 0; + do { + size--; + if((data[size] >= 0x20 && data[size] <= 0x7e) + ||(data[size] >= 0xa1 && data[size] <= 0xdf)) { + res++; + } + } while (size); + return res; +} + +uint8_t isFixed(uint8_t* data, int size, uint8_t value) { + uint8_t res = 1; + do { + size--; + if(data[size] != value) { + res = 0; + } + } while (size); + return res; +} + +uint8_t checkChksum(uint16_t cchk, uint16_t chk) { + uint32_t sum = cchk + chk; + uint8_t res = 0; + if(sum==0x0000ffff) { + res = 0x10; + } + return res; +} + +void smc_id(snes_romprops_t* props) { + uint8_t score, maxscore=1, score_idx=0; + + snes_header_t* header = &(props->header); + + for(uint8_t num = 0; num < 6; num++) { + file_readblock(header, hdr_addr[num], sizeof(snes_header_t)); + if(file_res) { + score = 0; + } else { + score = smc_headerscore(header); + } + if(score>=maxscore) { + score_idx=num; + maxscore=score; + } + } + + if(score_idx & 1) { + props->offset = 0x200; + } else { + props->offset = 0; + } + + // restore the chosen one + file_readblock(header, hdr_addr[score_idx], sizeof(snes_header_t)); + switch(header->map & 0xef) { + case 0x20: + props->mapper_id = 1; + break; + case 0x21: + props->mapper_id = 0; + break; + case 0x25: + props->mapper_id = 2; + break; + default: + props->mapper_id = 0; // whatever + } + + props->ramsize_bytes = (uint32_t)1024 << header->ramsize; + props->romsize_bytes = (uint32_t)1024 << header->romsize; + props->expramsize_bytes = (uint32_t)1024 << header->expramsize; + f_lseek(&file_handle, 0); +} + +uint8_t smc_headerscore(snes_header_t* header) { + uint8_t score=0; + score += countAllASCII(header->maker, sizeof(header->maker)); + score += countAllASCII(header->gamecode, sizeof(header->gamecode)); + score += isFixed(header->fixed_00, sizeof(header->fixed_00), 0x00); + score += countAllJISX0201(header->name, sizeof(header->name)); + score += 3*isFixed(&header->fixed_33, sizeof(header->fixed_33), 0x33); + score += checkChksum(header->cchk, header->chk); + return score; +} + diff --git a/src/smc.h b/src/smc.h new file mode 100644 index 0000000..affff25 --- /dev/null +++ b/src/smc.h @@ -0,0 +1,46 @@ +// insert cool lengthy disclaimer here + +/* + * smc.h: data structures for SNES ROM images + */ + +#ifndef SMC_H +#define SMC_H + +typedef struct _snes_header { + uint8_t maker[2]; // 0xB0 + uint8_t gamecode[4]; // 0xB2 + uint8_t fixed_00[7]; // 0xB6 + uint8_t expramsize; // 0xBD + uint8_t specver; // 0xBE + uint8_t carttype2; // 0xBF + uint8_t name[21]; // 0xC0 + uint8_t map; // 0xD5 + uint8_t carttype; // 0xD6 + uint8_t romsize; // 0xD7 + uint8_t ramsize; // 0xD8 + uint8_t destcode; // 0xD9 + uint8_t fixed_33; // 0xDA + uint8_t ver; // 0xDB + uint16_t cchk; // 0xDC + uint16_t chk; // 0xDE +} snes_header_t; + +typedef struct _snes_romprops { + uint16_t offset; // start of actual ROM image + uint8_t mapper_id; // FPGA mapper + uint32_t expramsize_bytes; // ExpRAM size in bytes + uint32_t ramsize_bytes; // CartRAM size in bytes + uint32_t romsize_bytes; // ROM size in bytes (rounded up) + snes_header_t header; // original header from ROM image +} snes_romprops_t; + +void smc_id(snes_romprops_t*); +uint8_t smc_headerscore(snes_header_t*); + + +/*pedef struct { + +}*/ + +#endif diff --git a/src/snes.c b/src/snes.c index 98b789f..c050102 100644 --- a/src/snes.c +++ b/src/snes.c @@ -47,7 +47,7 @@ void snes_reset(int state) { void snes_main_loop() { if(initloop) { saveram_crc_old = calc_sram_crc(saveram_base_addr, saveram_size); - save_sram("/test.srm", saveram_size, saveram_base_addr); + save_sram("/quite a long test filename.srm", saveram_size, saveram_base_addr); initloop=0; } saveram_crc = calc_sram_crc(saveram_base_addr, saveram_size); @@ -56,7 +56,7 @@ void snes_main_loop() { uart_puthexshort(saveram_crc); uart_putcrlf(); set_busy_led(1); - save_sram("/test.srm", saveram_size, saveram_base_addr); + save_sram("/quite a long test filename.srm", saveram_size, saveram_base_addr); set_busy_led(0); } saveram_crc_old = saveram_crc; diff --git a/verilog/sd2snes/address.v b/verilog/sd2snes/address.v index edd3936..7015879 100644 --- a/verilog/sd2snes/address.v +++ b/verilog/sd2snes/address.v @@ -56,15 +56,16 @@ end Index Mapper 000 HiROM 001 LoROM - 010 ExHiROM + 010 ExHiROM (48-64Mbit) */ -/* HiROM: SRAM @ Bank 0x20-0x3f, 0xa0-0xbf +/* HiROM: SRAM @ Bank 0x30-0x3f, 0xb0-0xbf Offset 6000-7fff */ assign IS_SAVERAM = ((MAPPER == 3'b000 || MAPPER == 3'b010) ? (!SNES_ADDR[22] - & SNES_ADDR[21] + & SNES_ADDR[21:20] & &SNES_ADDR[14:13] & !SNES_ADDR[15] + & SNES_CS ) /* LoROM: SRAM @ Bank 0x70-0x7d, 0xf0-0xfd Offset 0000-7fff TODO: 0000-ffff for diff --git a/verilog/sd2snes/main.ucf b/verilog/sd2snes/main.ucf index 0efde38..4bace8f 100644 --- a/verilog/sd2snes/main.ucf +++ b/verilog/sd2snes/main.ucf @@ -182,3 +182,52 @@ NET "SRAM_DATA[8]" LOC = P96; NET "SRAM_DATA[9]" LOC = P98; NET "SRAM_OE" LOC = P93; NET "CLKIN" IOSTANDARD = LVCMOS33; +TIMESPEC TS_test = FROM "FFS" TO "FFS" 10 ns; +NET "SNES_ADDR<0>" MAXDELAY = 10 ns; +NET "SNES_ADDR<0>" MAXSKEW = 5 ns; +NET "SNES_ADDR<1>" MAXDELAY = 10 ns; +NET "SNES_ADDR<1>" MAXSKEW = 5 ns; +NET "SNES_ADDR<2>" MAXDELAY = 10 ns; +NET "SNES_ADDR<2>" MAXSKEW = 5 ns; +NET "SNES_ADDR<3>" MAXDELAY = 10 ns; +NET "SNES_ADDR<3>" MAXSKEW = 5 ns; +NET "SNES_ADDR<4>" MAXDELAY = 10 ns; +NET "SNES_ADDR<4>" MAXSKEW = 5 ns; +NET "SNES_ADDR<5>" MAXDELAY = 10 ns; +NET "SNES_ADDR<5>" MAXSKEW = 5 ns; +NET "SNES_ADDR<6>" MAXDELAY = 10 ns; +NET "SNES_ADDR<6>" MAXSKEW = 5 ns; +NET "SNES_ADDR<7>" MAXDELAY = 10 ns; +NET "SNES_ADDR<7>" MAXSKEW = 5 ns; +NET "SNES_ADDR<8>" MAXDELAY = 10 ns; +NET "SNES_ADDR<8>" MAXSKEW = 5 ns; +NET "SNES_ADDR<9>" MAXDELAY = 10 ns; +NET "SNES_ADDR<9>" MAXSKEW = 5 ns; +NET "SNES_ADDR<10>" MAXDELAY = 10 ns; +NET "SNES_ADDR<10>" MAXSKEW = 5 ns; +NET "SNES_ADDR<11>" MAXDELAY = 10 ns; +NET "SNES_ADDR<11>" MAXSKEW = 5 ns; +NET "SNES_ADDR<12>" MAXDELAY = 10 ns; +NET "SNES_ADDR<12>" MAXSKEW = 5 ns; +NET "SNES_ADDR<13>" MAXDELAY = 10 ns; +NET "SNES_ADDR<13>" MAXSKEW = 5 ns; +NET "SNES_ADDR<14>" MAXDELAY = 10 ns; +NET "SNES_ADDR<14>" MAXSKEW = 5 ns; +NET "SNES_ADDR<15>" MAXDELAY = 10 ns; +NET "SNES_ADDR<15>" MAXSKEW = 5 ns; +NET "SNES_ADDR<16>" MAXDELAY = 10 ns; +NET "SNES_ADDR<16>" MAXSKEW = 5 ns; +NET "SNES_ADDR<17>" MAXDELAY = 10 ns; +NET "SNES_ADDR<17>" MAXSKEW = 5 ns; +NET "SNES_ADDR<18>" MAXDELAY = 10 ns; +NET "SNES_ADDR<18>" MAXSKEW = 5 ns; +NET "SNES_ADDR<19>" MAXDELAY = 10 ns; +NET "SNES_ADDR<19>" MAXSKEW = 5 ns; +NET "SNES_ADDR<20>" MAXDELAY = 10 ns; +NET "SNES_ADDR<20>" MAXSKEW = 5 ns; +NET "SNES_ADDR<21>" MAXDELAY = 10 ns; +NET "SNES_ADDR<21>" MAXSKEW = 5 ns; +NET "SNES_ADDR<22>" MAXDELAY = 10 ns; +NET "SNES_ADDR<22>" MAXSKEW = 5 ns; +NET "SNES_ADDR<23>" MAXDELAY = 10 ns; +NET "SNES_ADDR<23>" MAXSKEW = 5 ns; diff --git a/verilog/sd2snes/main.v b/verilog/sd2snes/main.v index 0a3bf73..ff27a16 100644 --- a/verilog/sd2snes/main.v +++ b/verilog/sd2snes/main.v @@ -120,14 +120,20 @@ wire SNES_RW; reg [1:0] SNES_READr; reg [1:0] SNES_WRITEr; reg [1:0] SNES_CSr; -reg [1:0] SNES_CPU_CLKr; -reg [7:0] SNES_RWr; +reg [5:0] SNES_CPU_CLKr; +reg [5:0] SNES_RWr; +reg [23:0] SNES_ADDRr; +reg [23:0] SNES_ADDR_PREVr; +reg [3:0] SNES_ADDRCHGr; wire SNES_READs = (SNES_READr == 2'b11); wire SNES_WRITEs = (SNES_WRITEr == 2'b11); wire SNES_CSs = (SNES_CSr == 2'b11); wire SNES_CPU_CLKs = SNES_CPU_CLK; // (SNES_CPU_CLKr == 2'b11); -wire SNES_RW_start = (SNES_RWr == 8'b11111110); // falling edge marks beginning of cycle +wire SNES_RW_start = (SNES_RWr == 6'b111110); // falling edge marks beginning of cycle +wire SNES_cycle_start = (SNES_CPU_CLKr == 6'b000001); +wire SNES_ADDRCHG = (SNES_ADDRr != SNES_ADDR_PREVr); +wire SNES_addr_start = (SNES_ADDRCHGr[0] == 1'b1); assign SNES_RW = (SNES_READ & SNES_WRITE); @@ -135,11 +141,15 @@ always @(posedge CLK2) begin SNES_READr <= {SNES_READr[0], SNES_READ}; SNES_WRITEr <= {SNES_WRITEr[0], SNES_WRITE}; SNES_CSr <= {SNES_CSr[0], SNES_CS}; - SNES_CPU_CLKr <= {SNES_CPU_CLKr[0], SNES_CPU_CLK}; - SNES_RWr <= {SNES_RWr[6:0], SNES_RW}; + SNES_CPU_CLKr <= {SNES_CPU_CLKr[4:0], SNES_CPU_CLK}; + SNES_RWr <= {SNES_RWr[4:0], SNES_RW}; end + reg ADDR_WRITE; +//reg [23:0] SNES_ADDRr; +//wire [23:0] SNES_ADDRw = SNES_ADDR; + address snes_addr( .CLK(CLK2), @@ -157,6 +167,11 @@ address snes_addr( .SAVERAM_MASK(SAVERAM_MASK), .ROM_MASK(ROM_MASK) ); + +wire SNES_READ_CYCLEw; +wire SNES_WRITE_CYCLEw; +wire AVR_READ_CYCLEw; +wire AVR_WRITE_CYCLEw; data snes_data(.CLK(CLK2), .SNES_READ(SNES_READ), @@ -179,33 +194,36 @@ data snes_data(.CLK(CLK2), parameter MODE_SNES = 1'b0; parameter MODE_AVR = 1'b1; -parameter STATE_0 = 10'b0000000001; -parameter STATE_1 = 10'b0000000010; -parameter STATE_2 = 10'b0000000100; -parameter STATE_3 = 10'b0000001000; -parameter STATE_4 = 10'b0000010000; -parameter STATE_5 = 10'b0000100000; -parameter STATE_6 = 10'b0001000000; -parameter STATE_7 = 10'b0010000000; -parameter STATE_8 = 10'b0100000000; -parameter STATE_9 = 10'b1000000000; +parameter STATE_0 = 13'b0000000000001; +parameter STATE_1 = 13'b0000000000010; +parameter STATE_2 = 13'b0000000000100; +parameter STATE_3 = 13'b0000000001000; +parameter STATE_4 = 13'b0000000010000; +parameter STATE_5 = 13'b0000000100000; +parameter STATE_6 = 13'b0000001000000; +parameter STATE_7 = 13'b0000010000000; +parameter STATE_8 = 13'b0000100000000; +parameter STATE_9 = 13'b0001000000000; +parameter STATE_10 = 13'b0010000000000; +parameter STATE_11 = 13'b0100000000000; +parameter STATE_IDLE = 13'b1000000000000; -reg [9:0] STATE; +reg [12:0] STATE; reg [3:0] STATEIDX; reg [1:0] CYCLE_RESET; reg SRAM_WE_MASK; reg SRAM_OE_MASK; -reg [9:0] SRAM_WE_ARRAY [3:0]; -reg [9:0] SRAM_OE_ARRAY [3:0]; +reg [12:0] SRAM_WE_ARRAY [3:0]; +reg [12:0] SRAM_OE_ARRAY [3:0]; -reg [9:0] SNES_DATA_TO_MEM_ARRAY[1:0]; -reg [9:0] AVR_DATA_TO_MEM_ARRAY[1:0]; -reg [9:0] SRAM_DATA_TO_SNES_MEM_ARRAY[1:0]; -reg [9:0] SRAM_DATA_TO_AVR_MEM_ARRAY[1:0]; +reg [12:0] SNES_DATA_TO_MEM_ARRAY[1:0]; +reg [12:0] AVR_DATA_TO_MEM_ARRAY[1:0]; +reg [12:0] SRAM_DATA_TO_SNES_MEM_ARRAY[1:0]; +reg [12:0] SRAM_DATA_TO_AVR_MEM_ARRAY[1:0]; -reg [9:0] MODE_ARRAY; +reg [12:0] MODE_ARRAY; reg SNES_READ_CYCLE; reg SNES_WRITE_CYCLE; @@ -227,38 +245,37 @@ assign MODE = !AVR_ENA ? MODE_AVR : MODE_ARRAY[STATEIDX]; initial begin CYCLE_RESET = 2'b0; - STATE = STATE_9; - STATEIDX = 9; + STATE = STATE_IDLE; + STATEIDX = 12; SRAM_WE_MASK = 1'b1; SRAM_OE_MASK = 1'b1; SNES_READ_CYCLE = 1'b1; SNES_WRITE_CYCLE = 1'b1; AVR_READ_CYCLE = 1'b1; AVR_WRITE_CYCLE = 1'b1; + MODE_ARRAY = 13'b0000000111111; + + SRAM_WE_ARRAY[2'b00] = 13'b1000000000000; + SRAM_WE_ARRAY[2'b01] = 13'b1000000111111; + SRAM_WE_ARRAY[2'b10] = 13'b1111111000000; + SRAM_WE_ARRAY[2'b11] = 13'b1111111111111; - MODE_ARRAY = 10'b0000011111; + SRAM_OE_ARRAY[2'b00] = 13'b1111111111111; + SRAM_OE_ARRAY[2'b01] = 13'b1111111000000; + SRAM_OE_ARRAY[2'b10] = 13'b0000000111111; + SRAM_OE_ARRAY[2'b11] = 13'b0000000000000; - SRAM_WE_ARRAY[2'b00] = 10'b1000010000; - SRAM_WE_ARRAY[2'b01] = 10'b1000011111; - SRAM_WE_ARRAY[2'b10] = 10'b1111110000; - SRAM_WE_ARRAY[2'b11] = 10'b1111111111; - - SRAM_OE_ARRAY[2'b00] = 10'b1111111111; - SRAM_OE_ARRAY[2'b01] = 10'b1111100000; - SRAM_OE_ARRAY[2'b10] = 10'b0000011111; - SRAM_OE_ARRAY[2'b11] = 10'b0000000000; + SNES_DATA_TO_MEM_ARRAY[1'b0] = 13'b0001000000000; // SNES write + SNES_DATA_TO_MEM_ARRAY[1'b1] = 13'b0000000000000; // SNES read - SNES_DATA_TO_MEM_ARRAY[1'b0] = 10'b0010000000; - SNES_DATA_TO_MEM_ARRAY[1'b1] = 10'b0000000000; + AVR_DATA_TO_MEM_ARRAY[1'b0] = 13'b0000000010000; // AVR write + AVR_DATA_TO_MEM_ARRAY[1'b1] = 13'b0000000000000; // AVR read - AVR_DATA_TO_MEM_ARRAY[1'b0] = 10'b0000100000; - AVR_DATA_TO_MEM_ARRAY[1'b1] = 10'b0000000000; + SRAM_DATA_TO_SNES_MEM_ARRAY[1'b0] = 13'b0000000000000; // SNES write + SRAM_DATA_TO_SNES_MEM_ARRAY[1'b1] = 13'b0000100000000; // SNES read - SRAM_DATA_TO_SNES_MEM_ARRAY[1'b0] = 10'b0000000000; - SRAM_DATA_TO_SNES_MEM_ARRAY[1'b1] = 10'b0000100000; - - SRAM_DATA_TO_AVR_MEM_ARRAY[1'b0] = 10'b0000000000; - SRAM_DATA_TO_AVR_MEM_ARRAY[1'b1] = 10'b0000000001; + SRAM_DATA_TO_AVR_MEM_ARRAY[1'b0] = 13'b0000000000000; // AVR write + SRAM_DATA_TO_AVR_MEM_ARRAY[1'b1] = 13'b0000000000001; // AVR read end // falling edge of SNES /RD or /WR marks the beginning of a new cycle @@ -268,45 +285,65 @@ end // we have 24 internal cycles to work with. (CLKIN * 4) always @(posedge CLK2) begin - CYCLE_RESET <= {CYCLE_RESET[0], SNES_RW_start}; + CYCLE_RESET <= {CYCLE_RESET[0], SNES_cycle_start}; end always @(posedge CLK2) begin if (SNES_RW_start) begin - STATE <= STATE_0; SNES_READ_CYCLE <= SNES_READ; SNES_WRITE_CYCLE <= SNES_WRITE; AVR_READ_CYCLE <= AVR_READ; AVR_WRITE_CYCLE <= AVR_WRITE; + STATE <= STATE_0; + STATEIDX <= 11; end else begin case (STATE) - STATE_0: - STATE <= STATE_1; - STATE_1: - STATE <= STATE_2; - STATE_2: - STATE <= STATE_3; - STATE_3: - STATE <= STATE_4; - STATE_4: - STATE <= STATE_5; - STATE_5: - STATE <= STATE_6; - STATE_6: - STATE <= STATE_7; - STATE_7: - STATE <= STATE_8; - STATE_8: - STATE <= STATE_9; + STATE_0: begin + STATE <= STATE_1; STATEIDX <= 10; + end + STATE_1: begin + STATE <= STATE_2; STATEIDX <= 9; + end + STATE_2: begin + STATE <= STATE_3; STATEIDX <= 8; + end + STATE_3: begin + STATE <= STATE_4; STATEIDX <= 7; + end + STATE_4: begin + STATE <= STATE_5; STATEIDX <= 6; + end + STATE_5: begin + STATE <= STATE_6; STATEIDX <= 5; + end + STATE_6: begin + STATE <= STATE_7; STATEIDX <= 4; + end + STATE_7: begin + STATE <= STATE_8; STATEIDX <= 3; + end + STATE_8: begin + STATE <= STATE_9; STATEIDX <= 2; + end STATE_9: begin - STATE <= STATE_9; - end - default: - STATE <= STATE_9; + STATE <= STATE_10; STATEIDX <= 1; + end + STATE_10: begin + STATE <= STATE_11; STATEIDX <= 0; + end + STATE_11: begin + STATE <= STATE_IDLE; STATEIDX <= 12; + end + STATE_IDLE: begin + STATE <= STATE_IDLE; STATEIDX <= 12; + end + default: begin + STATE <= STATE_IDLE; STATEIDX <= 12; + end endcase end end - +/* always @(posedge CLK2) begin case (STATE) @@ -353,7 +390,7 @@ always @(posedge CLK2) begin STATEIDX <= 9; endcase end - +*/ // When in AVR mode, enable SRAM_WE according to AVR programming // else enable SRAM_WE according to state&cycle assign SRAM_WE = !AVR_ENA ? AVR_WRITE @@ -372,8 +409,8 @@ assign SRAM_BLE = !SRAM_WE ? !SRAM_ADDR0 : 1'b0; //assign SRAM_WE = !AVR_ENA ? AVR_WRITE : 1'b1; //assign SNES_DATABUS_OE = (!IS_SAVERAM & SNES_CS) | (SNES_READ & SNES_WRITE); -assign SNES_DATABUS_OE = (IS_ROM & SNES_CSs) | (!IS_ROM & !IS_SAVERAM) | (SNES_READs & SNES_WRITEs); -assign SNES_DATABUS_DIR = !SNES_WRITEs ? 1'b0 : 1'b1; +assign SNES_DATABUS_OE = (IS_ROM & SNES_CS) | (!IS_ROM & !IS_SAVERAM) | (SNES_READ & SNES_WRITE); +assign SNES_DATABUS_DIR = !SNES_READ ? 1'b1 : 1'b0; assign SNES_DATA_TO_MEM = SNES_DATA_TO_MEM_ARRAY[SNES_WRITE_CYCLE][STATEIDX]; assign AVR_DATA_TO_MEM = AVR_DATA_TO_MEM_ARRAY[AVR_WRITE_CYCLE][STATEIDX]; @@ -381,6 +418,8 @@ assign AVR_DATA_TO_MEM = AVR_DATA_TO_MEM_ARRAY[AVR_WRITE_CYCLE][STATEIDX]; assign SRAM_DATA_TO_SNES_MEM = SRAM_DATA_TO_SNES_MEM_ARRAY[SNES_WRITE_CYCLE][STATEIDX]; assign SRAM_DATA_TO_AVR_MEM = SRAM_DATA_TO_AVR_MEM_ARRAY[AVR_WRITE_CYCLE][STATEIDX]; +assign SNES_READ_CYCLEw = SNES_READ_CYCLE; +assign SNES_WRITE_CYCLEw = SNES_WRITE_CYCLE; assign IRQ_DIR = 1'b0; assign SNES_IRQ = 1'bZ; diff --git a/verilog/sd2snes/sd2snes.xise b/verilog/sd2snes/sd2snes.xise index db31e95..9f378ec 100644 --- a/verilog/sd2snes/sd2snes.xise +++ b/verilog/sd2snes/sd2snes.xise @@ -69,21 +69,30 @@ + + + + + + + + +