From 309e1062b9af204cb22f04fd7211d8ce164da08d Mon Sep 17 00:00:00 2001 From: david Date: Mon, 8 Jun 2009 11:00:27 +0200 Subject: [PATCH] cleanup code --- snes/fatfstest/Makefile | 55 +- snes/fatfstest/PPU.c | 158 +- snes/fatfstest/PPU.h | 22 +- snes/fatfstest/config.h | 4 +- snes/fatfstest/crc.h | 5 +- snes/fatfstest/data.h | 16 +- snes/fatfstest/debug.c | 370 +-- snes/fatfstest/debug.h | 17 +- snes/fatfstest/diskio.c | 398 +-- snes/fatfstest/diskio.h | 154 +- snes/fatfstest/event.c | 202 +- snes/fatfstest/event.h | 33 +- snes/fatfstest/ff.c | 5872 ++++++++++++++++++------------------ snes/fatfstest/ff.h | 1131 +++---- snes/fatfstest/integer.h | 84 +- snes/fatfstest/main.c | 613 ++-- snes/fatfstest/myEvents.c | 192 +- snes/fatfstest/myEvents.h | 12 +- snes/fatfstest/pad.c | 46 +- snes/fatfstest/pad.h | 40 +- snes/fatfstest/ressource.h | 4 +- 21 files changed, 4723 insertions(+), 4705 deletions(-) diff --git a/snes/fatfstest/Makefile b/snes/fatfstest/Makefile index 271a0e9..b2a0e98 100644 --- a/snes/fatfstest/Makefile +++ b/snes/fatfstest/Makefile @@ -29,22 +29,15 @@ LIBS=-L$(SDK)/lib/cl #-L$(SDK)/lib/c134 -<<<<<<< HEAD:snes/fatfstest/Makefile -OBJS=StartupSnes.obj main.obj pad.obj PPU.obj debug.obj ressource.obj diskio.obj ff.obj -======= OBJS=hook.obj StartupSnes.obj main.obj pad.obj PPU.obj debug.obj ressource.obj diskio.obj ff.obj crc.obj ->>>>>>> c1203c3519d95a92e82a6f2ebf741ad663e46e33:snes/fatfstest/Makefile APP=fatfs.smc GFX=debugfont -all: $(APP) repair +all: $(APP) -run: +run: $(EMU) $(APP) -debugger: - $(EMU_DEBUG) $(APP) - upload: ucon64 -chk $(APP) cp -rv $(APP) /Volumes/SNES @@ -55,6 +48,10 @@ disasm: $(APP) rm -rf fatfs.asm $(DISASM) fatfs.smc > fatfs.asm +repair: $(APP) + $(UCON) -snes -chk $(APP) 2>&1 >/dev/null + rm -rf fatfs.bak + StartupSnes.obj: StartupSnes.asm $(AS) -V $? @@ -65,45 +62,17 @@ hook.obj: hook.asm $(AS) -V $? %.obj: %.c -<<<<<<< HEAD:snes/fatfstest/Makefile $(CC) -wl -wp -sop -ML -I $(INC) $? -#$(APP): $(OBJS) -# $(LD) -HB -M21 -V -T -Pff \ -# -C008000,0000 -U0000,0000 \ -# -Avectors=FFE4,7FE4 \ -# -Aregistration_data=FFB0,7FB0 \ -# -Aressource=18000,8000 \ -# -N $(OBJS) $(LIBS) -O $@ - -$(APP): $(OBJS) hook.obj +$(APP): $(OBJS) + echo "test" $(LD) -B -HB -M21 -V -T -Pff \ -C3e8000,1f0000 -U000000,000000 \ -Avectors=FFE4,7FE4 \ -Aregistration_data=FFB0,7FB0 \ - -Aressource=3f8000,1f8000 \ - -Ahook=008000,0000 \ - -N $(OBJS) hook.obj $(LIBS) -O $@ - - -repair: $(APP) - $(UCON) -snes -chk $(APP) 2>&1 >/dev/null - rm -rf fatfs.bak -======= - $(CC) -wl -wp -sop -ML -I $(INC) $? - -$(APP): $(OBJS) - $(LD) -HB -M21 -V -T -Pff \ - -C3e8000,1f0000 -U000000,000000 \ - -Avectors=FFE4,7FE4 \ - -Aregistration_data=FFB0,7FB0 \ - -Aressource=18000,8000 \ - -Ahook=8000,0000 \ - -N $(OBJS) $(LIBS) -O $@ - ucon64 -snes -chk $(APP) 2>&1 >/dev/null ->>>>>>> c1203c3519d95a92e82a6f2ebf741ad663e46e33:snes/fatfstest/Makefile - - + -Aressource=18000,8000 \ + -Ahook=008000,0000 \ + -N $(OBJS) $(LIBS) -O $@ clean: - rm -vf $(APP) *.obj *.TMP + rm -vf $(APP) *.obj *.TMP diff --git a/snes/fatfstest/PPU.c b/snes/fatfstest/PPU.c index e61b481..bfc0956 100644 --- a/snes/fatfstest/PPU.c +++ b/snes/fatfstest/PPU.c @@ -1,80 +1,80 @@ -#include "data.h" - -byte tileMapLocation[4]; -word characterLocation[4]; - -void waitForVBlank(void) { - byte Status; - do { - Status = *(byte*)0x4210; - } while (!(Status & 0x80)); -} - -void setTileMapLocation(word vramDst, byte screenProp, byte bgNumber) { - tileMapLocation[bgNumber] = ((vramDst >> 8) & 0xfc) | ( screenProp & 0x03 ); - *(byte*) (0x2107+bgNumber) = tileMapLocation[bgNumber]; -} - -void restoreTileMapLocation(byte bgNumber) { - *(byte*) (0x2107+bgNumber) = tileMapLocation[bgNumber]; -} - -void setCharacterLocation(word vramDst, byte bgNumber) { - characterLocation[bgNumber] = vramDst; - if(bgNumber < 2) { - *(byte*) 0x210b = (characterLocation[1] >> 8 & 0xf0 ) + (characterLocation[0] >> 12); - } else { - *(byte*) 0x210c = (characterLocation[3] >> 8 & 0xf0 ) + (characterLocation[2] >> 12); - } -} - -void restoreCharacterLocation(byte bgNumber) { - setCharacterLocation(characterLocation[bgNumber], bgNumber); -} - -void VRAMByteWrite(byte value, word vramDst) { - *(byte*)0x2115 = 0x80; - *(word*)0x2116 = vramDst; - - *(byte*)0x2118 = value; -} - -void VRAMLoad(word src, word vramDst, word size) { - // set address in VRam for read or write ($2116) + block size transfer ($2115) - *(byte*)0x2115 = 0x80; - *(word*)0x2116 = vramDst; - - *(word*)0x4300 = 0x1801; // set DMA control register (1 word inc) - // and destination ($21xx xx -> 0x18) - *(word*)0x4302 = src; // DMA channel x source address offset - // (low $4302 and high $4303 optimisation) - *(byte*)0x4304 = 0x01; // DMA channel x source address bank - *(word*)0x4305 = size; // DMA channel x transfer size - // (low $4305 and high $4306 optimisation) - - // Turn on DMA transfer for this channel - waitForVBlank(); - *(byte*)0x2100 = 0x80; - *(byte*)0x420b = 0x01; - *(byte*)0x2100 = 0x00; -} - -void CGRAMLoad(word src, byte cgramDst, word size) { - - // set address in VRam for read or write + block size - *(byte*)0x2121 = cgramDst; - - *(word*)0x4300 = 0x2200; // set DMA control register (1 byte inc) - // and destination ($21xx xx -> 022) - *(word*)0x4302 = src; // DMA channel x source address offset - // (low $4302 and high $4303 optimisation) - *(byte*)0x4304 = 0x01; // DMA channel x source address bank - *(word*)0x4305 = size; // DMA channel x transfer size - // (low $4305 and high $4306 optimisation) - - // Turn on DMA transfer for this channel - waitForVBlank(); - *(byte*)0x2100 = 0x80; - *(byte*)0x420b = 0x01; - *(byte*)0x2100 = 0x00; +#include "data.h" + +byte tileMapLocation[4]; +word characterLocation[4]; + +void waitForVBlank(void) { + byte Status; + do { + Status = *(byte*)0x4210; + } while (!(Status & 0x80)); +} + +void setTileMapLocation(word vramDst, byte screenProp, byte bgNumber) { + tileMapLocation[bgNumber] = ((vramDst >> 8) & 0xfc) | ( screenProp & 0x03 ); + *(byte*) (0x2107+bgNumber) = tileMapLocation[bgNumber]; +} + +void restoreTileMapLocation(byte bgNumber) { + *(byte*) (0x2107+bgNumber) = tileMapLocation[bgNumber]; +} + +void setCharacterLocation(word vramDst, byte bgNumber) { + characterLocation[bgNumber] = vramDst; + if(bgNumber < 2) { + *(byte*) 0x210b = (characterLocation[1] >> 8 & 0xf0 ) + (characterLocation[0] >> 12); + } else { + *(byte*) 0x210c = (characterLocation[3] >> 8 & 0xf0 ) + (characterLocation[2] >> 12); + } +} + +void restoreCharacterLocation(byte bgNumber) { + setCharacterLocation(characterLocation[bgNumber], bgNumber); +} + +void VRAMByteWrite(byte value, word vramDst) { + *(byte*)0x2115 = 0x80; + *(word*)0x2116 = vramDst; + + *(byte*)0x2118 = value; +} + +void VRAMLoad(word src, word vramDst, word size) { + // set address in VRam for read or write ($2116) + block size transfer ($2115) + *(byte*)0x2115 = 0x80; + *(word*)0x2116 = vramDst; + + *(word*)0x4300 = 0x1801; // set DMA control register (1 word inc) + // and destination ($21xx xx -> 0x18) + *(word*)0x4302 = src; // DMA channel x source address offset + // (low $4302 and high $4303 optimisation) + *(byte*)0x4304 = 0x01; // DMA channel x source address bank + *(word*)0x4305 = size; // DMA channel x transfer size + // (low $4305 and high $4306 optimisation) + + // Turn on DMA transfer for this channel + waitForVBlank(); + *(byte*)0x2100 = 0x80; + *(byte*)0x420b = 0x01; + *(byte*)0x2100 = 0x00; +} + +void CGRAMLoad(word src, byte cgramDst, word size) { + + // set address in VRam for read or write + block size + *(byte*)0x2121 = cgramDst; + + *(word*)0x4300 = 0x2200; // set DMA control register (1 byte inc) + // and destination ($21xx xx -> 022) + *(word*)0x4302 = src; // DMA channel x source address offset + // (low $4302 and high $4303 optimisation) + *(byte*)0x4304 = 0x01; // DMA channel x source address bank + *(word*)0x4305 = size; // DMA channel x transfer size + // (low $4305 and high $4306 optimisation) + + // Turn on DMA transfer for this channel + waitForVBlank(); + *(byte*)0x2100 = 0x80; + *(byte*)0x420b = 0x01; + *(byte*)0x2100 = 0x00; } \ No newline at end of file diff --git a/snes/fatfstest/PPU.h b/snes/fatfstest/PPU.h index 36ecef9..a0a3899 100644 --- a/snes/fatfstest/PPU.h +++ b/snes/fatfstest/PPU.h @@ -1,11 +1,11 @@ -extern byte tileMapLocation[4]; -extern word characterLocation[4]; - -void waitForVBlank(void); -void setTileMapLocation(word vramDst, byte screenProp, byte bgNumber); -void restoreTileMapLocation(byte bgNumber); -void setCharacterLocation(word vramDst, byte bgNumber); -void restoreCharacterLocation(byte bgNumber); -void VRAMByteWrite(byte value, word vramDst); -void VRAMLoad(word src, word vramDst, word size); -void CGRAMLoad(word src, byte cgramDst, word size); \ No newline at end of file +extern byte tileMapLocation[4]; +extern word characterLocation[4]; + +void waitForVBlank(void); +void setTileMapLocation(word vramDst, byte screenProp, byte bgNumber); +void restoreTileMapLocation(byte bgNumber); +void setCharacterLocation(word vramDst, byte bgNumber); +void restoreCharacterLocation(byte bgNumber); +void VRAMByteWrite(byte value, word vramDst); +void VRAMLoad(word src, word vramDst, word size); +void CGRAMLoad(word src, byte cgramDst, word size); diff --git a/snes/fatfstest/config.h b/snes/fatfstest/config.h index c870802..93cacbd 100644 --- a/snes/fatfstest/config.h +++ b/snes/fatfstest/config.h @@ -4,7 +4,9 @@ #define CMD_WRITE 0x02 #define CMD_NONE 0xff -/* Memory Map */ +/* + * Memory Map + */ #define MMIO_CMD 0x3010 #define MMIO_SECTOR01 0x3011 #define MMIO_SECTOR02 0x3012 diff --git a/snes/fatfstest/crc.h b/snes/fatfstest/crc.h index 0dd4edf..8108d80 100644 --- a/snes/fatfstest/crc.h +++ b/snes/fatfstest/crc.h @@ -1,4 +1,3 @@ -word crc_update (byte *data, word size); -word crc_update_mem (unsigned long, word size); - +word crc_update(byte * data, word size); +word crc_update_mem(unsigned long, word size); diff --git a/snes/fatfstest/data.h b/snes/fatfstest/data.h index fbe7072..673102c 100644 --- a/snes/fatfstest/data.h +++ b/snes/fatfstest/data.h @@ -1,8 +1,8 @@ - -#ifndef _DATA - -typedef unsigned char byte; -typedef unsigned short word; - -#define _DATA -#endif + +#ifndef _DATA + +typedef unsigned char byte; +typedef unsigned short word; + +#define _DATA +#endif diff --git a/snes/fatfstest/debug.c b/snes/fatfstest/debug.c index 3b30309..8b6c061 100644 --- a/snes/fatfstest/debug.c +++ b/snes/fatfstest/debug.c @@ -1,185 +1,185 @@ -#include -#include -#include - -#include "debug.h" -#include "data.h" -#include "pad.h" -#include "PPU.h" -#include "ressource.h" - - - - -word debugMap[0x400]; -static char debug_buffer[512]; -static char screen_buffer[512]; - - -void debug_init(void) { - word i; - for(i=0; i<0x400; i++) { - debugMap[i] = 0x00; - } - memset(debug_buffer,0,255); -} - - -void debug_enable(void){ - VRAMLoad((word) debugFont_pic, 0x5000, 2048); - VRAMLoad((word) debugMap, 0x4000, 0x0800); - setTileMapLocation(0x4000, (byte) 0x00, (byte) 0); - setCharacterLocation(0x5000, (byte) 0); - *(byte*) 0x2100 = 0x0f; // enable background - - // hex(24 << 10 | 24 << 5 | 24 ) - // '0x6318' - - *(byte*) 0x2121 = 0x00; - *(byte*) 0x2122 = 0x18; - *(byte*) 0x2122 = 0x63; - - -} - -void clears(void) { - word i,y; - for(y=0; y<20; y++){ - waitForVBlank(); - for(i=0; i<32; i++){ - *(byte*)0x2115 = 0x80; - *(word*)0x2116 = 0x4000+i+(y*0x20); - *(byte*)0x2118 = 0; - } - } -} - -void _print_char(word y,word x, char c){ - waitForVBlank(); - VRAMByteWrite((byte) (c-32), (word) (0x4000+x+(y*0x20))); -} - -void _print_screen(word y, char *buffer){ - char l; - char x = 0; - l = strlen(buffer); - waitForVBlank(); - while(*buffer){ - if (*buffer == '\n' ) { - while(x++<32){ - *(byte*)0x2115 = 0x80; - *(word*)0x2116 = 0x4000+x+(y*0x20); - *(byte*)0x2118 = 0; - } - x = 0; - y++; - buffer++; - waitForVBlank(); - continue; - } - *(byte*)0x2115 = 0x80; - *(word*)0x2116 = 0x4000+x+(y*0x20); - *(byte*)0x2118 = *buffer-32; - x++; - buffer++; - //waitForVBlank(); - } -} -void _print_console(const char *buffer){ - while(*buffer) - *(byte*) 0x3000=*buffer++; -} - - - - -void printfc(char *fmt,...){ - va_list ap; - va_start(ap,fmt); - vsprintf(debug_buffer,fmt,ap); - va_end(ap); - _print_console(debug_buffer); - //memset(debug_buffer,0,255); - -} - -void printfs(word y,char *fmt,...){ - va_list ap; - va_start(ap,fmt); - vsprintf(screen_buffer,fmt,ap); - va_end(ap); - _print_screen(y,screen_buffer); - memset(screen_buffer,0,255); -} - -void printc_packet(unsigned long addr,unsigned int len,byte *packet){ - unsigned int i,j; - unsigned int sum = 0; - unsigned int clear=0; - - for (i=0;i=33 && packet[i+j]<=126 ) - printfc("%c", packet[i+j]); - else - printfc("."); - } - printfc("|\n"); - } -} - -/* keep the linker happy */ -int open(const char * _name, int _mode){ - _print_console("open called\n"); - return -1; -} - -int close(int fd){ - _print_console("close called\n"); - return -1; - -} - -size_t read(int fd, void * buff, size_t len){ - _print_console("read called\n"); - return 0; -} - -size_t write(int fd, void * buffer, size_t len){ - _print_console("write called\n"); - return 0; -} - -long lseek(int fd, long off, int count){ - _print_console("lseek called\n"); - return 0; -} - -int unlink(const char * name){ - _print_console("unlink called\n"); - return -1; -} - -int isatty(){ - _print_console("isatty called\n"); - return 1; -} - +#include +#include +#include + +#include "debug.h" +#include "data.h" +#include "pad.h" +#include "PPU.h" +#include "ressource.h" + + + + +word debugMap[0x400]; +static char debug_buffer[512]; +static char screen_buffer[512]; + + +void debug_init(void) { + word i; + for(i=0; i<0x400; i++) { + debugMap[i] = 0x00; + } + memset(debug_buffer,0,255); +} + + +void debug_enable(void){ + VRAMLoad((word) debugFont_pic, 0x5000, 2048); + VRAMLoad((word) debugMap, 0x4000, 0x0800); + setTileMapLocation(0x4000, (byte) 0x00, (byte) 0); + setCharacterLocation(0x5000, (byte) 0); + *(byte*) 0x2100 = 0x0f; // enable background + + // hex(24 << 10 | 24 << 5 | 24 ) + // '0x6318' + + *(byte*) 0x2121 = 0x00; + *(byte*) 0x2122 = 0x18; + *(byte*) 0x2122 = 0x63; + + +} + +void clears(void) { + word i,y; + for(y=0; y<20; y++){ + waitForVBlank(); + for(i=0; i<32; i++){ + *(byte*)0x2115 = 0x80; + *(word*)0x2116 = 0x4000+i+(y*0x20); + *(byte*)0x2118 = 0; + } + } +} + +void _print_char(word y,word x, char c){ + waitForVBlank(); + VRAMByteWrite((byte) (c-32), (word) (0x4000+x+(y*0x20))); +} + +void _print_screen(word y, char *buffer){ + char l; + char x = 0; + l = strlen(buffer); + waitForVBlank(); + while(*buffer){ + if (*buffer == '\n' ) { + while(x++<32){ + *(byte*)0x2115 = 0x80; + *(word*)0x2116 = 0x4000+x+(y*0x20); + *(byte*)0x2118 = 0; + } + x = 0; + y++; + buffer++; + waitForVBlank(); + continue; + } + *(byte*)0x2115 = 0x80; + *(word*)0x2116 = 0x4000+x+(y*0x20); + *(byte*)0x2118 = *buffer-32; + x++; + buffer++; + //waitForVBlank(); + } +} +void _print_console(const char *buffer){ + while(*buffer) + *(byte*) 0x3000=*buffer++; +} + + + + +void printfc(char *fmt,...){ + va_list ap; + va_start(ap,fmt); + vsprintf(debug_buffer,fmt,ap); + va_end(ap); + _print_console(debug_buffer); + //memset(debug_buffer,0,255); + +} + +void printfs(word y,char *fmt,...){ + va_list ap; + va_start(ap,fmt); + vsprintf(screen_buffer,fmt,ap); + va_end(ap); + _print_screen(y,screen_buffer); + memset(screen_buffer,0,255); +} + +void printc_packet(unsigned long addr,unsigned int len,byte *packet){ + unsigned int i,j; + unsigned int sum = 0; + unsigned int clear=0; + + for (i=0;i=33 && packet[i+j]<=126 ) + printfc("%c", packet[i+j]); + else + printfc("."); + } + printfc("|\n"); + } +} + +/* keep the linker happy */ +int open(const char * _name, int _mode){ + _print_console("open called\n"); + return -1; +} + +int close(int fd){ + _print_console("close called\n"); + return -1; + +} + +size_t read(int fd, void * buff, size_t len){ + _print_console("read called\n"); + return 0; +} + +size_t write(int fd, void * buffer, size_t len){ + _print_console("write called\n"); + return 0; +} + +long lseek(int fd, long off, int count){ + _print_console("lseek called\n"); + return 0; +} + +int unlink(const char * name){ + _print_console("unlink called\n"); + return -1; +} + +int isatty(){ + _print_console("isatty called\n"); + return 1; +} + diff --git a/snes/fatfstest/debug.h b/snes/fatfstest/debug.h index e8ba412..69d2771 100644 --- a/snes/fatfstest/debug.h +++ b/snes/fatfstest/debug.h @@ -1,9 +1,8 @@ -#include "data.h" - -void debug_init(void); -void debug_enable(void); -void printfs(word y,char* fmt,...); -void printfc(char* fmt,...); -void clears(void); -void printc_packet(unsigned long addr,unsigned int len,byte *packet); - +#include "data.h" + +void debug_init(void); +void debug_enable(void); +void printfs(word y, char *fmt, ...); +void printfc(char *fmt, ...); +void clears(void); +void printc_packet(unsigned long addr, unsigned int len, byte * packet); diff --git a/snes/fatfstest/diskio.c b/snes/fatfstest/diskio.c index bf811db..ffb0385 100644 --- a/snes/fatfstest/diskio.c +++ b/snes/fatfstest/diskio.c @@ -1,199 +1,199 @@ - -#include "integer.h" -#include "diskio.h" -#include "config.h" -#include "data.h" -#include "debug.h" - - - -/* Interface -** Scratch Buffer -addr 3 byte -size 1 byte - -** Call Interface -cmd 1 byte -sector 4 bytes -count 1 byte -return 1 byte - -** Commands - * disk_init - * disk_read - * disk_write -*/ - -/*-----------------------------------------------------------------------*/ -/* Initialize Disk Drive */ -/*-----------------------------------------------------------------------*/ - -#include - - -// TODO: Figure out gobal vars ?!? - -DSTATUS Stat = 0 ; //STA_NOINIT; /* Disk status */ - - -DSTATUS disk_initialize (BYTE drv) { - - byte retval; - #ifdef MMIO_DEBUG - printfc("SNES::disk_initialize called drv=%i stat=%i\n",drv,Stat); - #endif - if (drv) return STA_NOINIT; /* Supports only single drive */ - Stat |= STA_NOINIT; - #ifdef MMIO_DEBUG - printfc("SNES::disk_initialize stat=%i\n",Stat); - #endif - *(byte*) MMIO_RETVAL = STA_VOID; - *(byte*) MMIO_CMD = CMD_INIT; - #ifdef MMIO_DEBUG - printfc("SNES::disk_initialize poll retval\n"); - #endif - while((retval = *(byte*) MMIO_RETVAL) == STA_VOID); - Stat &= ~STA_NOINIT; /* When device goes ready, clear STA_NOINIT */ - #ifdef MMIO_DEBUG - printfc("SNES::disk_initialize done Stat=%i\n",Stat); - #endif - return Stat; -} - - -/*-----------------------------------------------------------------------*/ -/* Return Disk Status */ -/*-----------------------------------------------------------------------*/ - -DSTATUS disk_status (BYTE drv){ - if (drv) return STA_NOINIT; /* Supports only single drive */ - return Stat; -} - - -/*-----------------------------------------------------------------------*/ -/* Read Sector(s) */ -/*-----------------------------------------------------------------------*/ - -DRESULT disk_read ( - BYTE drv, /* Physical drive nmuber (0) */ - BYTE *buff, /* Data buffer to store read data */ - DWORD sector, /* Sector number (LBA) */ - BYTE count /* Sector count (1..255) */ -) -{ - DWORD offset; - INT size; - byte retval; - word i; - - //#ifdef MMIO_DEBUG - printfc("SNES::disk_read called sector=%li count=%i\n",sector,count); - //#endif - if (drv || !count) return RES_PARERR; - #ifdef MMIO_DEBUG - printfc("SNES::disk_read drv ok\n"); - #endif - if (Stat & STA_NOINIT) return RES_NOTRDY; - #ifdef MMIO_DEBUG - printfc("SNES::disk_read sta ok\n"); - #endif - - *(byte*) MMIO_RETVAL = STA_VOID; - *(byte*) MMIO_CMD = CMD_READ; - - *(byte*) MMIO_SECTOR01 = (sector >> 24) & 0xff; - *(byte*) MMIO_SECTOR02 = (sector >> 16) & 0xff; - *(byte*) MMIO_SECTOR03 = (sector >> 8) & 0xff; - *(byte*) MMIO_SECTOR04 = (sector) & 0xff; - - *(byte*) MMIO_COUNT = count; - - #ifdef MMIO_DEBUG - printfc("SNES::disk_read poll retval\n"); - #endif - while((retval = *(byte*) MMIO_RETVAL) == STA_VOID); - - - #ifdef MMIO_DEBUG - printfc("SNES::disk_read copy buffer to %06lx\n",SHARED_ADDR); - #endif - for (i=0;i<(count*512);i++){ - buff[i] = *(byte*)(SHARED_ADDR+i); - #ifdef MMIO_DEBUG - if ( i < 8) - printfc("0x%02x ",buff[i]); - #endif - } - #ifdef MMIO_DEBUG - printfc("\n"); - #endif - return retval; -} - - - -/*-----------------------------------------------------------------------*/ -/* Write Sector(s) */ -/*-----------------------------------------------------------------------*/ - -#if _READONLY == 0 -DRESULT disk_write ( - BYTE drv, /* Physical drive nmuber (0) */ - const BYTE *buff, /* Data to be written */ - DWORD sector, /* Sector number (LBA) */ - BYTE count /* Sector count (1..255) */ -) -{ - DWORD offset; - INT size; - - if (drv || !count) return RES_PARERR; - if (Stat & STA_NOINIT) return RES_NOTRDY; - - offset = sector * 512; - size = count * 512; - return RES_OK; -} -#endif /* _READONLY */ - -/*-----------------------------------------------------------------------*/ -/* Miscellaneous Functions */ -/*-----------------------------------------------------------------------*/ - -#if _USE_IOCTL != 0 -DRESULT disk_ioctl ( - BYTE drv, /* Physical drive nmuber (0) */ - BYTE ctrl, /* Control code */ - void *buff /* Buffer to send/receive data block */ -) -{ - BYTE n, w, ofs, dl, dh, *ptr = (BYTE*)buff; - - - if (drv) return RES_PARERR; - if (Stat & STA_NOINIT) return RES_NOTRDY; - - switch (ctrl) { - case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */ - ofs = 60; w = 2; n = 0; - break; - - case GET_SECTOR_SIZE : /* Get sectors on the disk (WORD) */ - *(WORD*)buff = 512; - return RES_OK; - - case GET_BLOCK_SIZE : /* Get erase block size in sectors (DWORD) */ - *(DWORD*)buff = 32; - return RES_OK; - - default: - return RES_PARERR; - } - return RES_OK; -} -#endif /* _USE_IOCTL != 0 */ - - - - + +#include "integer.h" +#include "diskio.h" +#include "config.h" +#include "data.h" +#include "debug.h" + + + +/* Interface +** Scratch Buffer +addr 3 byte +size 1 byte + +** Call Interface +cmd 1 byte +sector 4 bytes +count 1 byte +return 1 byte + +** Commands + * disk_init + * disk_read + * disk_write +*/ + +/*-----------------------------------------------------------------------*/ +/* Initialize Disk Drive */ +/*-----------------------------------------------------------------------*/ + +#include + + +// TODO: Figure out gobal vars ?!? + +DSTATUS Stat = 0 ; //STA_NOINIT; /* Disk status */ + + +DSTATUS disk_initialize (BYTE drv) { + + byte retval; + #ifdef MMIO_DEBUG + printfc("SNES::disk_initialize called drv=%i stat=%i\n",drv,Stat); + #endif + if (drv) return STA_NOINIT; /* Supports only single drive */ + Stat |= STA_NOINIT; + #ifdef MMIO_DEBUG + printfc("SNES::disk_initialize stat=%i\n",Stat); + #endif + *(byte*) MMIO_RETVAL = STA_VOID; + *(byte*) MMIO_CMD = CMD_INIT; + #ifdef MMIO_DEBUG + printfc("SNES::disk_initialize poll retval\n"); + #endif + while((retval = *(byte*) MMIO_RETVAL) == STA_VOID); + Stat &= ~STA_NOINIT; /* When device goes ready, clear STA_NOINIT */ + #ifdef MMIO_DEBUG + printfc("SNES::disk_initialize done Stat=%i\n",Stat); + #endif + return Stat; +} + + +/*-----------------------------------------------------------------------*/ +/* Return Disk Status */ +/*-----------------------------------------------------------------------*/ + +DSTATUS disk_status (BYTE drv){ + if (drv) return STA_NOINIT; /* Supports only single drive */ + return Stat; +} + + +/*-----------------------------------------------------------------------*/ +/* Read Sector(s) */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_read ( + BYTE drv, /* Physical drive nmuber (0) */ + BYTE *buff, /* Data buffer to store read data */ + DWORD sector, /* Sector number (LBA) */ + BYTE count /* Sector count (1..255) */ +) +{ + DWORD offset; + INT size; + byte retval; + word i; + + //#ifdef MMIO_DEBUG + printfc("SNES::disk_read called sector=%li count=%i\n",sector,count); + //#endif + if (drv || !count) return RES_PARERR; + #ifdef MMIO_DEBUG + printfc("SNES::disk_read drv ok\n"); + #endif + if (Stat & STA_NOINIT) return RES_NOTRDY; + #ifdef MMIO_DEBUG + printfc("SNES::disk_read sta ok\n"); + #endif + + *(byte*) MMIO_RETVAL = STA_VOID; + *(byte*) MMIO_CMD = CMD_READ; + + *(byte*) MMIO_SECTOR01 = (sector >> 24) & 0xff; + *(byte*) MMIO_SECTOR02 = (sector >> 16) & 0xff; + *(byte*) MMIO_SECTOR03 = (sector >> 8) & 0xff; + *(byte*) MMIO_SECTOR04 = (sector) & 0xff; + + *(byte*) MMIO_COUNT = count; + + #ifdef MMIO_DEBUG + printfc("SNES::disk_read poll retval\n"); + #endif + while((retval = *(byte*) MMIO_RETVAL) == STA_VOID); + + + #ifdef MMIO_DEBUG + printfc("SNES::disk_read copy buffer to %06lx\n",SHARED_ADDR); + #endif + for (i=0;i<(count*512);i++){ + buff[i] = *(byte*)(SHARED_ADDR+i); + #ifdef MMIO_DEBUG + if ( i < 8) + printfc("0x%02x ",buff[i]); + #endif + } + #ifdef MMIO_DEBUG + printfc("\n"); + #endif + return retval; +} + + + +/*-----------------------------------------------------------------------*/ +/* Write Sector(s) */ +/*-----------------------------------------------------------------------*/ + +#if _READONLY == 0 +DRESULT disk_write ( + BYTE drv, /* Physical drive nmuber (0) */ + const BYTE *buff, /* Data to be written */ + DWORD sector, /* Sector number (LBA) */ + BYTE count /* Sector count (1..255) */ +) +{ + DWORD offset; + INT size; + + if (drv || !count) return RES_PARERR; + if (Stat & STA_NOINIT) return RES_NOTRDY; + + offset = sector * 512; + size = count * 512; + return RES_OK; +} +#endif /* _READONLY */ + +/*-----------------------------------------------------------------------*/ +/* Miscellaneous Functions */ +/*-----------------------------------------------------------------------*/ + +#if _USE_IOCTL != 0 +DRESULT disk_ioctl ( + BYTE drv, /* Physical drive nmuber (0) */ + BYTE ctrl, /* Control code */ + void *buff /* Buffer to send/receive data block */ +) +{ + BYTE n, w, ofs, dl, dh, *ptr = (BYTE*)buff; + + + if (drv) return RES_PARERR; + if (Stat & STA_NOINIT) return RES_NOTRDY; + + switch (ctrl) { + case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */ + ofs = 60; w = 2; n = 0; + break; + + case GET_SECTOR_SIZE : /* Get sectors on the disk (WORD) */ + *(WORD*)buff = 512; + return RES_OK; + + case GET_BLOCK_SIZE : /* Get erase block size in sectors (DWORD) */ + *(DWORD*)buff = 32; + return RES_OK; + + default: + return RES_PARERR; + } + return RES_OK; +} +#endif /* _USE_IOCTL != 0 */ + + + + diff --git a/snes/fatfstest/diskio.h b/snes/fatfstest/diskio.h index 0b69159..1de335d 100644 --- a/snes/fatfstest/diskio.h +++ b/snes/fatfstest/diskio.h @@ -1,71 +1,83 @@ -/*----------------------------------------------------------------------- -/ Low level disk interface modlue include file R0.05 (C)ChaN, 2007 -/-----------------------------------------------------------------------*/ - - - #ifdef __cplusplus - extern "C" { - #endif - -#ifndef _DISKIO - -#define _READONLY 0 /* 1: Read-only mode */ -#define _USE_IOCTL 1 - -#include "integer.h" - - -/* Status of Disk Functions */ -typedef BYTE DSTATUS; - -/* Results of Disk Functions */ -typedef enum { - RES_OK = 0, /* 0: Successful */ - RES_ERROR, /* 1: R/W Error */ - RES_WRPRT, /* 2: Write Protected */ - RES_NOTRDY, /* 3: Not Ready */ - RES_PARERR /* 4: Invalid Parameter */ -} DRESULT; - - -/*---------------------------------------*/ -/* Prototypes for disk control functions */ - -DSTATUS disk_initialize (BYTE); -DSTATUS disk_status (BYTE); -DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE); -#if _READONLY == 0 -DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE); -#endif -DRESULT disk_ioctl (BYTE, BYTE, void*); -void disk_timerproc (void); - - - - -/* Disk Status Bits (DSTATUS) */ - -#define STA_NOINIT 0x01 /* Drive not initialized */ -#define STA_NODISK 0x02 /* No medium in the drive */ -#define STA_PROTECT 0x04 /* Write protected */ -#define STA_VOID 0xff - - -/* Command code for disk_ioctrl() */ - -/* Generic command */ -#define CTRL_SYNC 0 /* Mandatory for write functions */ -#define GET_SECTOR_COUNT 1 /* Mandatory for only f_mkfs() */ -#define GET_SECTOR_SIZE 2 -#define GET_BLOCK_SIZE 3 /* Mandatory for only f_mkfs() */ -#define CTRL_POWER 4 -#define CTRL_LOCK 5 -#define CTRL_EJECT 6 - -#define _DISKIO -#endif - - - #ifdef __cplusplus - } - #endif \ No newline at end of file +/*----------------------------------------------------------------------- +/ Low level disk interface modlue include file R0.05 (C)ChaN, 2007 +/-----------------------------------------------------------------------*/ + + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _DISKIO + +#define _READONLY 0 /* 1: Read-only mode */ +#define _USE_IOCTL 1 + +#include "integer.h" + + + /* + * Status of Disk Functions + */ + typedef BYTE DSTATUS; + + /* + * Results of Disk Functions + */ + typedef enum { + RES_OK = 0, /* 0: Successful */ + RES_ERROR, /* 1: R/W Error */ + RES_WRPRT, /* 2: Write Protected */ + RES_NOTRDY, /* 3: Not Ready */ + RES_PARERR /* 4: Invalid Parameter */ + } DRESULT; + + +/*---------------------------------------*/ + /* + * Prototypes for disk control functions + */ + + DSTATUS disk_initialize(BYTE); + DSTATUS disk_status(BYTE); + DRESULT disk_read(BYTE, BYTE *, DWORD, BYTE); +#if _READONLY == 0 + DRESULT disk_write(BYTE, const BYTE *, DWORD, BYTE); +#endif + DRESULT disk_ioctl(BYTE, BYTE, void *); + void disk_timerproc(void); + + + + + /* + * Disk Status Bits (DSTATUS) + */ + +#define STA_NOINIT 0x01 /* Drive not initialized */ +#define STA_NODISK 0x02 /* No medium in the drive */ +#define STA_PROTECT 0x04 /* Write protected */ +#define STA_VOID 0xff + + + /* + * Command code for disk_ioctrl() + */ + + /* + * Generic command + */ +#define CTRL_SYNC 0 /* Mandatory for write functions */ +#define GET_SECTOR_COUNT 1 /* Mandatory for only f_mkfs() */ +#define GET_SECTOR_SIZE 2 +#define GET_BLOCK_SIZE 3 /* Mandatory for only f_mkfs() */ +#define CTRL_POWER 4 +#define CTRL_LOCK 5 +#define CTRL_EJECT 6 + +#define _DISKIO +#endif + + +#ifdef __cplusplus +} +#endif diff --git a/snes/fatfstest/event.c b/snes/fatfstest/event.c index 1c62c4a..6e241f6 100644 --- a/snes/fatfstest/event.c +++ b/snes/fatfstest/event.c @@ -1,101 +1,101 @@ -#include - -#include "data.h"; -#include "event.h"; - -event *events; - -void initEvents(void) { - events = NULL; -} - -event *createEvent(char (*callback)(word counter)) { - event *myEvent; - - myEvent = (event*) malloc(sizeof(event)); - - myEvent->VBlankCount = 0; - myEvent->callback = callback; - myEvent->nextEvent = NULL; - myEvent->previousEvent = NULL; - - - return myEvent; -} - -event* addEvent(char (*callback)(word counter), int noDuplicateCallback) { - - event *lastEvent; - event *myEvent; - - if(events == NULL) { - events = createEvent(callback); - return events; - } else { - lastEvent = events; - // TODO optimise this with noduplicate - while(lastEvent->nextEvent != NULL) { - if(noDuplicateCallback == 1 && lastEvent->callback == *callback) { - return NULL; - } - lastEvent = lastEvent->nextEvent; - } - if(noDuplicateCallback == 1 && lastEvent->callback == *callback) { - return NULL; - } - myEvent = createEvent(callback); - myEvent->previousEvent = lastEvent; - lastEvent->nextEvent = myEvent; - return myEvent; - } - - -} - -void removeEvent(event *eventElement) { - - byte alone = 0; - event *next, *previous; - - next = eventElement->nextEvent; - previous = eventElement->previousEvent; - - if(eventElement->nextEvent != NULL && eventElement->previousEvent != NULL) { - alone++; - next->previousEvent = previous; - previous->nextEvent = next; - - } else if(eventElement->nextEvent != NULL) { - alone++; - next->previousEvent = NULL; - events = next; - - } else if(eventElement->previousEvent != NULL) { - alone++; - previous->nextEvent = NULL; - } - - free(eventElement); - - if(alone == 0) { - events = NULL; - } -} - -void processEvents(void) { - - event *currentEvent; - char returnValue; - - currentEvent = events; - while(currentEvent != NULL) { - returnValue = currentEvent->callback(currentEvent->VBlankCount); - if(returnValue == EVENT_CONTINUE) { - currentEvent->VBlankCount++; - } else { - removeEvent(currentEvent); - } - currentEvent = currentEvent->nextEvent; - } - -} +#include + +#include "data.h"; +#include "event.h"; + +event *events; + +void initEvents(void) { + events = NULL; +} + +event *createEvent(char (*callback)(word counter)) { + event *myEvent; + + myEvent = (event*) malloc(sizeof(event)); + + myEvent->VBlankCount = 0; + myEvent->callback = callback; + myEvent->nextEvent = NULL; + myEvent->previousEvent = NULL; + + + return myEvent; +} + +event* addEvent(char (*callback)(word counter), int noDuplicateCallback) { + + event *lastEvent; + event *myEvent; + + if(events == NULL) { + events = createEvent(callback); + return events; + } else { + lastEvent = events; + // TODO optimise this with noduplicate + while(lastEvent->nextEvent != NULL) { + if(noDuplicateCallback == 1 && lastEvent->callback == *callback) { + return NULL; + } + lastEvent = lastEvent->nextEvent; + } + if(noDuplicateCallback == 1 && lastEvent->callback == *callback) { + return NULL; + } + myEvent = createEvent(callback); + myEvent->previousEvent = lastEvent; + lastEvent->nextEvent = myEvent; + return myEvent; + } + + +} + +void removeEvent(event *eventElement) { + + byte alone = 0; + event *next, *previous; + + next = eventElement->nextEvent; + previous = eventElement->previousEvent; + + if(eventElement->nextEvent != NULL && eventElement->previousEvent != NULL) { + alone++; + next->previousEvent = previous; + previous->nextEvent = next; + + } else if(eventElement->nextEvent != NULL) { + alone++; + next->previousEvent = NULL; + events = next; + + } else if(eventElement->previousEvent != NULL) { + alone++; + previous->nextEvent = NULL; + } + + free(eventElement); + + if(alone == 0) { + events = NULL; + } +} + +void processEvents(void) { + + event *currentEvent; + char returnValue; + + currentEvent = events; + while(currentEvent != NULL) { + returnValue = currentEvent->callback(currentEvent->VBlankCount); + if(returnValue == EVENT_CONTINUE) { + currentEvent->VBlankCount++; + } else { + removeEvent(currentEvent); + } + currentEvent = currentEvent->nextEvent; + } + +} diff --git a/snes/fatfstest/event.h b/snes/fatfstest/event.h index 76b07dc..0f38195 100644 --- a/snes/fatfstest/event.h +++ b/snes/fatfstest/event.h @@ -1,16 +1,17 @@ -typedef struct event{ - word VBlankCount; - char (*callback)(word counter); - struct event *previousEvent; - struct event *nextEvent; -} event; - -#define EVENT_STOP 0 -#define EVENT_CONTINUE 1 - -extern event *events; - -void initEvents(void); -extern event* addEvent(char (*callback)(word counter), int noDuplicateCallback); -extern void removeEvent(event *eventElement); -extern void processEvents(void); +typedef struct event { + word VBlankCount; + char (*callback) (word counter); + struct event *previousEvent; + struct event *nextEvent; +} event; + +#define EVENT_STOP 0 +#define EVENT_CONTINUE 1 + +extern event *events; + +void initEvents(void); +extern event *addEvent(char (*callback) (word counter), + int noDuplicateCallback); +extern void removeEvent(event * eventElement); +extern void processEvents(void); diff --git a/snes/fatfstest/ff.c b/snes/fatfstest/ff.c index e174f25..ff71e09 100644 --- a/snes/fatfstest/ff.c +++ b/snes/fatfstest/ff.c @@ -1,2936 +1,2936 @@ -/*----------------------------------------------------------------------------/ -/ 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 */ -#include "debug.h" /* FatFs configurations and declarations */ - - -/*-------------------------------------------------------------------------- - - 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.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 */ +#include "debug.h" /* FatFs configurations and declarations */ + + +/*-------------------------------------------------------------------------- + + 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 */ diff --git a/snes/fatfstest/ff.h b/snes/fatfstest/ff.h index 57786c9..0c52e8d 100644 --- a/snes/fatfstest/ff.h +++ b/snes/fatfstest/ff.h @@ -1,547 +1,584 @@ -/*---------------------------------------------------------------------------/ -/ 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 437 -/* The _CODE_PAGE specifies the OEM code page to be used on the target system. -/ When it is non LFN configuration, there is no difference between SBCS code -/ pages. When LFN is enabled, the code page must always be set correctly. -/ 437 - U.S. -/ 720 - Arabic -/ 737 - Greek -/ 775 - Baltic -/ 850 - Multilingual Latin 1 -/ 852 - Latin 2 -/ 855 - Cyrillic -/ 857 - Turkish -/ 858 - Multilingual Latin 1 + Euro -/ 862 - Hebrew -/ 866 - Russian -/ 874 - Thai -/ 932 - Japanese Shift-JIS (DBCS) -/ 936 - Simplified Chinese GBK (DBCS) -/ 949 - Korean (DBCS) -/ 950 - Traditional Chinese Big5 (DBCS) -/ 1258 - Vietnam -*/ - - -#define _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.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 437 +/* + * The _CODE_PAGE specifies the OEM code page to be used on the target system. / When it is non LFN configuration, there is no difference + * between SBCS code / pages. When LFN is enabled, the code page must always be set correctly. / 437 - U.S. / 720 - Arabic / 737 - Greek / + * 775 - Baltic / 850 - Multilingual Latin 1 / 852 - Latin 2 / 855 - Cyrillic / 857 - Turkish / 858 - Multilingual Latin 1 + Euro / 862 - + * Hebrew / 866 - Russian / 874 - Thai / 932 - Japanese Shift-JIS (DBCS) / 936 - Simplified Chinese GBK (DBCS) / 949 - Korean (DBCS) / 950 + * - Traditional Chinese Big5 (DBCS) / 1258 - Vietnam + */ + + +#define _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 */ diff --git a/snes/fatfstest/integer.h b/snes/fatfstest/integer.h index 266856d..0d7742c 100644 --- a/snes/fatfstest/integer.h +++ b/snes/fatfstest/integer.h @@ -1,36 +1,48 @@ -/*-------------------------------------------*/ -/* Integer type definitions for FatFs module */ -/*-------------------------------------------*/ - -#ifndef _INTEGER - - -/* These types must be 16-bit, 32-bit or larger integer */ -typedef int INT; -typedef unsigned int UINT; - -/* These types must be 8-bit integer */ -typedef signed char CHAR; -typedef unsigned char UCHAR; -typedef unsigned char BYTE; - -/* These types must be 16-bit integer */ -typedef short SHORT; -typedef unsigned short USHORT; -typedef unsigned short WORD; -typedef unsigned short WCHAR; - -/* These types must be 32-bit integer */ -typedef long LONG; -typedef unsigned long ULONG; -typedef unsigned long DWORD; - -/* Boolean type */ -// enum { false = 0 , true } bool; - -typedef int BOOL; -#define FALSE 0 -#define TRUE 1 - -#define _INTEGER -#endif +/*-------------------------------------------*/ +/* + * Integer type definitions for FatFs module + */ +/*-------------------------------------------*/ + +#ifndef _INTEGER + + +/* + * These types must be 16-bit, 32-bit or larger integer + */ +typedef int INT; +typedef unsigned int UINT; + +/* + * These types must be 8-bit integer + */ +typedef signed char CHAR; +typedef unsigned char UCHAR; +typedef unsigned char BYTE; + +/* + * These types must be 16-bit integer + */ +typedef short SHORT; +typedef unsigned short USHORT; +typedef unsigned short WORD; +typedef unsigned short WCHAR; + +/* + * These types must be 32-bit integer + */ +typedef long LONG; +typedef unsigned long ULONG; +typedef unsigned long DWORD; + +/* + * Boolean type + */ +// enum { false = 0 , true } bool; + +typedef int BOOL; +#define FALSE 0 +#define TRUE 1 + +#define _INTEGER +#endif diff --git a/snes/fatfstest/main.c b/snes/fatfstest/main.c index d376d38..1984ad6 100644 --- a/snes/fatfstest/main.c +++ b/snes/fatfstest/main.c @@ -1,313 +1,300 @@ -#include "ff.h" - -#include -#include -#include - -#include "data.h"; -#include "pad.h"; -#include "event.h"; -#include "myEvents.h"; -#include "ressource.h"; -#include "PPU.h" -#include "debug.h" -#include "crc.h" - -/* - -o debug STA global -o optimize internal transfer buffer -o direct write to mempage -o relocate main code -o exec loaded file -*/ - -//#pragma section CODE=BANK2,offset $2:0000 - -#define ROM_NAME "MRDO.SMC" -//#define ROM_NAME "TEST.TXT" -#define BLOCK_SIZE 512 -#define BASE_ADDR 0x008000 - - -padStatus pad1; - -DWORD acc_size; /* Work register for fs command */ -WORD acc_files, acc_dirs; - -FILINFO finfo; -FATFS fatfs[2]; /* File system object for each logical drive */ -BYTE Buff[512]; /* Working buffer */ - -DWORD p1, p2, p3; -DWORD addr; -DWORD crc_addr; -UINT crc; - -BYTE res,bank; -WORD w1; -UINT s1, s2, cnt; - -FATFS *fs; -DIR dir; /* Directory object */ -FIL file1,file2; - - - - -void initInternalRegisters(void) { - characterLocation[0] = 0x0000; - characterLocation[1] = 0x0000; - characterLocation[2] = 0x0000; - characterLocation[3] = 0x0000; - debug_init(); -} - -void preInit(void) { - // For testing purpose ... - // Insert code here to be executed before register init -} - -DWORD get_fattime () -{ - time_t rawtime; - struct tm * ptm; - //time ( &rawtime ); - ptm = gmtime ( &rawtime ); - - return ((DWORD)(ptm->tm_year - 80) << 25) - | ((DWORD)(ptm->tm_mon +1) << 21) - | ((DWORD)ptm->tm_mday << 16) - | ((DWORD)ptm->tm_hour << 11) - | ((DWORD)ptm->tm_min << 5) - | ((DWORD)ptm->tm_sec >> 1); -} - -void halt(void) { - while(1); - -} - -void put_rc (FRESULT rc){ - const char *p; - static const char str[] = - "OK\0" "NOT_READY\0" "NO_FILE\0" "NO_PATH\0" "INVALID_NAME\0" "INVALID_DRIVE\0" - "DENIED\0" "EXIST\0" "RW_ERROR\0" "WRITE_PROTECTED\0" "NOT_ENABLED\0" - "NO_FILESYSTEM\0" "INVALID_OBJECT\0" "MKFS_ABORTED\0"; - FRESULT i; - - for (p = str, i = 0; i != rc && *p; i++) { - while(*p++); - } - printfc("rc=%u FR_%s\n", (WORD)rc, p); -} - - -FRESULT scan_files (char* path){ - DIR dirs; - FRESULT res; - int i; - - if ((res = f_opendir(&dirs, path)) == FR_OK) { - i = strlen(path); - while (((res = f_readdir(&dirs, &finfo)) == FR_OK) && finfo.fname[0]) { - if (finfo.fattrib & AM_DIR) { - acc_dirs++; - *(path+i) = '/'; strcpy(path+i+1, &finfo.fname[0]); - res = scan_files(path); - *(path+i) = '\0'; - if (res != FR_OK) break; - } else { - acc_files++; - acc_size += finfo.fsize; - } - } - } - return res; -} - -void wait(void){ - printfc("SNES::wait: press A to continue\n"); - enablePad(); - //waitForVBlank(); - pad1 = readPad((byte) 0); - while(!pad1.A) { - waitForVBlank(); - pad1 = readPad((byte) 0); - } - printfc("SNES::wait: done\n"); - //disablePad(); -} - -void boot(void){ - #asm - jsl $008000 - #endasm -} - -void main(void) { - word i,j; - BYTE res; - initInternalRegisters(); - - *(byte*) 0x2105 = 0x01; // MODE 1 value - *(byte*) 0x212c = 0x01; // Plane 0 (bit one) enable register - *(byte*) 0x212d = 0x00; // All subPlane disable - *(byte*) 0x2100 = 0x0f; // enable background - - debug_enable(); - - printfs(0,"FATFS OPTIXX.ORG "); - wait(); - printfc("SNES::main: Try to init disk\n"); - - put_rc(f_mount(0, &fatfs[0])); - -#if 0 - printfc("SNES::main: Try to get free\n"); - res = f_getfree("", &p2, &fs); - if (res) - put_rc(res); - - - printfc("SNES::main: printf fs results\n"); - printfs(1,"FAT TYPE = %u\nBYTES/CLUSTER = %lu\nNUMBER OF FATS = %u\n" - "ROOT DIR ENTRIES = %u\nSECTORS/FAT = %lu\nNUMBER OF CLUSTERS = %lu\n" - "FAT START = %lu\nDIR START LBA,CLUSTER = %lu\nDATA START LBA = %lu\n", - (WORD)fs->fs_type, (DWORD)fs->csize * 512, (WORD)fs->n_fats, - fs->n_rootdir, (DWORD)fs->sects_fat, (DWORD)fs->max_clust - 2, - fs->fatbase, fs->dirbase, fs->database); - - acc_size = acc_files = acc_dirs = 0; - printfc("SNES::main: scan files\n"); - res = scan_files(""); - if (res) - put_rc(res); - - printfs(12,"%u FILES, %lu BYTES\n%u FOLDERS\n" - "%lu KB TOTAK DISK SPACE\n%lu KB AVAILABLE\n", - acc_files, acc_size, acc_dirs, - (fs->max_clust - 2) * (fs->csize / 2), p2 * (fs->csize / 2)); - - res = f_opendir(&dir, ""); - if (res) - put_rc(res); - - p1 = s1 = s2 = 0; - cnt = 0; - wait(); - clears(); - printfc("SNES::main: read dir\n"); - for(;;) { - res = f_readdir(&dir, &finfo); - if ((res != FR_OK) || !finfo.fname[0]) break; - if (finfo.fattrib & AM_DIR) { - s2++; - } else { - s1++; p1 += finfo.fsize; - } - ; - - printfs(cnt,"%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu\n%s\n", - (finfo.fattrib & AM_DIR) ? 'D' : '-', - (finfo.fattrib & AM_RDO) ? 'R' : '-', - (finfo.fattrib & AM_HID) ? 'H' : '-', - (finfo.fattrib & AM_SYS) ? 'S' : '-', - (finfo.fattrib & AM_ARC) ? 'A' : '-', - (finfo.fdate >> 9) + 1980, (finfo.fdate >> 5) & 15, finfo.fdate & 31, - (finfo.ftime >> 11), (finfo.ftime >> 5) & 63, - finfo.fsize, &(finfo.fname[0])); - cnt+=2; - /* - printfs(cnt,"%u/%02u/%02u %02u:%02u %9lu\n%s\n", - (finfo.fdate >> 9) + 1980, (finfo.fdate >> 5) & 15, finfo.fdate & 31, - (finfo.ftime >> 11), (finfo.ftime >> 5) & 63, - finfo.fsize, &(finfo.fname[0])); - */ - if (cnt && cnt==20){ - cnt = 0; - wait(); - clears(); - } - } - - printfs(20,"%4u FILES\n%10lu BYTES TOTAL\n%4u DIRS", s1, p1, s2); - if (f_getfree("", &p1, &fs) == FR_OK) - printfs(23,"%10luK BYTES FREE\n", p1 * fs->csize / 2); - - wait(); - clears(); -#endif - printfc("SNES::main: open %s \n",ROM_NAME); - printfs(0,"OPEN %s",ROM_NAME); - put_rc(f_open(&file1, ROM_NAME, FA_READ)); - - - p1 = 32768L * 8; - p2 = 0 ; - p3 = 0; - cnt = 0; - bank =0; - addr = BASE_ADDR; - crc_addr = BASE_ADDR; - while (p1) { - cnt = BLOCK_SIZE; - p1 -= BLOCK_SIZE; - res = f_read(&file1, (byte*)(addr) , cnt, &s2); - printfc("SNES::main: read cnt=%i p1=%li p2=%li s2=%i\n",cnt,p1,p2,s2); - //printfc("SNES::main: file %x %x %x %x\n",Buff[0],Buff[1],Buff[2],Buff[3]); - if (res != FR_OK) { - printfc("SNES::main: read failed\n"); - put_rc(res); - break; - } - p2 += s2; - if (cnt != s2){ - printfc("SNES::main: read cnt=%i s2=%i\n",cnt,s2); - break; - } -<<<<<<< HEAD:snes/fatfstest/main.c - printfs(1,"%lu BYTES READ", p2); -======= - printfs(1 + bank,"BANK %X ADDR %LX",bank,addr); ->>>>>>> c1203c3519d95a92e82a6f2ebf741ad663e46e33:snes/fatfstest/main.c - /* - for (i=0; i>>>>>> c1203c3519d95a92e82a6f2ebf741ad663e46e33:snes/fatfstest/main.c - } - put_rc(f_close(&file1)); - boot(); - while(1){ - wait(); - } - while(1); -} - -void IRQHandler(void) { -} - -void NMIHandler(void) { - //processEvents(); -} +#include "ff.h" + +#include +#include +#include + +#include "data.h"; +#include "pad.h"; +#include "event.h"; +#include "myEvents.h"; +#include "ressource.h"; +#include "PPU.h" +#include "debug.h" +#include "crc.h" + + /* + * o debug STA global o optimize internal transfer buffer o direct write to mempage o relocate main code o exec loaded file + */ + + // #pragma section CODE=BANK2,offset $2:0000 + +#define ROM_NAME "MRDO.SMC" + // #define ROM_NAME "TEST.TXT" +#define BLOCK_SIZE 512 +#define BASE_ADDR 0x008000 +padStatus pad1; +DWORD acc_size; /* Work register for fs command */ +WORD acc_files, + acc_dirs; +FILINFO finfo; +FATFS fatfs[2]; /* File system object for each logical * drive */ +BYTE Buff[512]; /* Working buffer */ +DWORD p1, + p2, + p3; +DWORD addr; +DWORD crc_addr; +UINT crc; +BYTE res, + bank; +WORD w1; +UINT s1, + s2, + cnt; +FATFS *fs; +DIR dir; /* Directory object */ +FIL file1, + file2; +void initInternalRegisters(void) +{ + characterLocation[0] = 0x0000; + characterLocation[1] = 0x0000; + characterLocation[2] = 0x0000; + characterLocation[3] = 0x0000; + debug_init(); +} void + preInit(void) +{ + + // For testing purpose ... + // Insert code here to be executed before register init +} DWORD get_fattime() +{ + time_t rawtime; + struct tm *ptm; + + // time ( &rawtime ); + ptm = gmtime(&rawtime); + return ((DWORD) (ptm->tm_year - 80) << 25) + | ((DWORD) (ptm->tm_mon + 1) << 21) + | ((DWORD) ptm->tm_mday << 16) + | ((DWORD) ptm->tm_hour << 11) + | ((DWORD) ptm->tm_min << 5) | ((DWORD) ptm->tm_sec >> 1); +} + +void halt(void) +{ + while (1); +} + +void put_rc(FRESULT rc) +{ + const char *p; + static const char str[] = + "OK\0" "NOT_READY\0" "NO_FILE\0" "NO_PATH\0" "INVALID_NAME\0" + "INVALID_DRIVE\0" "DENIED\0" "EXIST\0" "RW_ERROR\0" + "WRITE_PROTECTED\0" "NOT_ENABLED\0" "NO_FILESYSTEM\0" + "INVALID_OBJECT\0" "MKFS_ABORTED\0"; + FRESULT i; + for (p = str, i = 0; i != rc && *p; i++) { + while (*p++); + } + printfc("rc=%u FR_%s\n", (WORD) rc, p); +} + +FRESULT scan_files(char *path) +{ + DIR dirs; + FRESULT res; + int i; + if ((res = f_opendir(&dirs, path)) == FR_OK) { + i = strlen(path); + while (((res = f_readdir(&dirs, &finfo)) == FR_OK) + && finfo.fname[0]) { + if (finfo.fattrib & AM_DIR) { + acc_dirs++; + *(path + i) = '/'; + strcpy(path + i + 1, &finfo.fname[0]); + res = scan_files(path); + *(path + i) = '\0'; + if (res != FR_OK) + break; + } else { + acc_files++; + acc_size += finfo.fsize; + } + } + } + return res; +} + +void wait(void) +{ + printfc("SNES::wait: press A to continue\n"); + enablePad(); + + // waitForVBlank(); + pad1 = readPad((byte) 0); + while (!pad1.A) { + waitForVBlank(); + pad1 = readPad((byte) 0); + } + printfc("SNES::wait: done\n"); + + // disablePad(); +} + +void boot(void) +{ + +#asm + jsl $008000 +#endasm +} void + main(void) +{ + word i, + j; + BYTE res; + initInternalRegisters(); + *(byte *) 0x2105 = 0x01; // MODE 1 value + *(byte *) 0x212c = 0x01; // Plane 0 (bit one) enable register + *(byte *) 0x212d = 0x00; // All subPlane disable + *(byte *) 0x2100 = 0x0f; // enable background + debug_enable(); + printfs(0, "FATFS OPTIXX.ORG "); + + printfc("SNES::main: Try to init disk\n"); + put_rc(f_mount(0, &fatfs[0])); + +#if 0 + printfc("SNES::main: Try to get free\n"); + res = f_getfree("", &p2, &fs); + if (res) + put_rc(res); + printfc("SNES::main: printf fs results\n"); + printfs(1, + "FAT TYPE = %u\nBYTES/CLUSTER = %lu\nNUMBER OF FATS = %u\n" + "ROOT DIR ENTRIES = %u\nSECTORS/FAT = %lu\nNUMBER OF CLUSTERS = %lu\n" + "FAT START = %lu\nDIR START LBA,CLUSTER = %lu\nDATA START LBA = %lu\n", + (WORD) fs->fs_type, (DWORD) fs->csize * 512, + (WORD) fs->n_fats, fs->n_rootdir, (DWORD) fs->sects_fat, + (DWORD) fs->max_clust - 2, fs->fatbase, fs->dirbase, fs->database); + acc_size = acc_files = acc_dirs = 0; + printfc("SNES::main: scan files\n"); + res = scan_files(""); + if (res) + put_rc(res); + printfs(12, "%u FILES, %lu BYTES\n%u FOLDERS\n" + "%lu KB TOTAK DISK SPACE\n%lu KB AVAILABLE\n", acc_files, + acc_size, acc_dirs, (fs->max_clust - 2) * (fs->csize / 2), + p2 * (fs->csize / 2)); + res = f_opendir(&dir, ""); + if (res) + put_rc(res); + p1 = s1 = s2 = 0; + cnt = 0; + wait(); + clears(); + printfc("SNES::main: read dir\n"); + for (;;) { + res = f_readdir(&dir, &finfo); + if ((res != FR_OK) || !finfo.fname[0]) + break; + if (finfo.fattrib & AM_DIR) { + s2++; + } else { + s1++; + p1 += finfo.fsize; + } + ; + printfs(cnt, "%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu\n%s\n", + (finfo.fattrib & AM_DIR) ? 'D' : '-', + (finfo.fattrib & AM_RDO) ? 'R' : '-', + (finfo.fattrib & AM_HID) ? 'H' : '-', + (finfo.fattrib & AM_SYS) ? 'S' : '-', + (finfo.fattrib & AM_ARC) ? 'A' : '-', + (finfo.fdate >> 9) + 1980, (finfo.fdate >> 5) & 15, + finfo.fdate & 31, (finfo.ftime >> 11), + (finfo.ftime >> 5) & 63, finfo.fsize, &(finfo.fname[0])); + cnt += 2; + + /* + * printfs(cnt,"%u/%02u/%02u %02u:%02u %9lu\n%s\n", (finfo.fdate >> 9) + 1980, (finfo.fdate >> 5) & 15, finfo.fdate & 31, + * (finfo.ftime >> 11), (finfo.ftime >> 5) & 63, finfo.fsize, &(finfo.fname[0])); + */ + if (cnt && cnt == 20) { + cnt = 0; + wait(); + clears(); + } + } + printfs(20, "%4u FILES\n%10lu BYTES TOTAL\n%4u DIRS", s1, p1, s2); + if (f_getfree("", &p1, &fs) == FR_OK) + printfs(23, "%10luK BYTES FREE\n", p1 * fs->csize / 2); + wait(); + clears(); + +#endif /* */ + printfc("SNES::main: open %s \n", ROM_NAME); + printfs(0, "OPEN %s", ROM_NAME); + put_rc(f_open(&file1, ROM_NAME, FA_READ)); + p1 = 32768L * 8; + p2 = 0; + p3 = 0; + cnt = 0; + bank = 0; + addr = BASE_ADDR; + crc_addr = BASE_ADDR; + while (p1) { + cnt = BLOCK_SIZE; + p1 -= BLOCK_SIZE; + res = f_read(&file1, (byte *) (addr), cnt, &s2); + printfc("SNES::main: read cnt=%i p1=%li p2=%li s2=%i\n", cnt, + p1, p2, s2); + + // printfc("SNES::main: file %x %x %x + // %x\n",Buff[0],Buff[1],Buff[2],Buff[3]); + if (res != FR_OK) { + printfc("SNES::main: read failed\n"); + put_rc(res); + break; + } + p2 += s2; + if (cnt != s2) { + printfc("SNES::main: read cnt=%i s2=%i\n", cnt, s2); + break; + } + printfs(1 + bank, "BANK %X ADDR %LX", bank, addr); + + /* + * for (i=0; i= 0x0f) { - return EVENT_STOP; - } else { - return EVENT_CONTINUE; - } -} - -char mosaicOut(word counter) { - static byte mosaicOutValue; - - if(counter == 0) { - // init fade value - mosaicOutValue = 0xff; - } else { - mosaicOutValue -= 0x10; - } - - *(byte*) 0x2106 = mosaicOutValue; - - if(mosaicOutValue == 0x0f) { - return EVENT_STOP; - } else { - return EVENT_CONTINUE; - } -} - -char mosaicIn(word counter) { - static byte mosaicInValue; - - if(counter == 0) { - // init fade value - mosaicInValue = 0x0f; - } else { - mosaicInValue += 0x10; - } - - *(byte*) 0x2106 = mosaicInValue; - - if(mosaicInValue == 0xff) { - return EVENT_STOP; - } else { - return EVENT_CONTINUE; - } -} - -char NMIReadPad(word counter) { - pad1 = readPad((byte) 0); - - return EVENT_CONTINUE; -} - -char scrollLeft(word counter) { - scrollValue++; - - *(byte*) 0x210d = (byte) scrollValue; - *(byte*) 0x210d = (byte) (scrollValue>>8); - - return EVENT_CONTINUE; +#include "data.h"; +#include "pad.h"; +#include "event.h"; + +extern padStatus pad1; +extern word scrollValue; + +char fadeOut(word counter) { + static byte fadeOutValue; + + if(counter == 0) { + // init fade value + fadeOutValue = 0x0f; + } else { + fadeOutValue--; + } + + *(byte*) 0x2100 = fadeOutValue; + + if(fadeOutValue == 0x00) { + return EVENT_STOP; + } else { + return EVENT_CONTINUE; + } +} + +char fadeIn(word counter) { + static byte fadeInValue; + + if(counter == 0) { + // init fade value + fadeInValue = 0x00; + } else { + fadeInValue++; + } + + *(byte*) 0x2100 = fadeInValue; + + if(fadeInValue >= 0x0f) { + return EVENT_STOP; + } else { + return EVENT_CONTINUE; + } +} + +char mosaicOut(word counter) { + static byte mosaicOutValue; + + if(counter == 0) { + // init fade value + mosaicOutValue = 0xff; + } else { + mosaicOutValue -= 0x10; + } + + *(byte*) 0x2106 = mosaicOutValue; + + if(mosaicOutValue == 0x0f) { + return EVENT_STOP; + } else { + return EVENT_CONTINUE; + } +} + +char mosaicIn(word counter) { + static byte mosaicInValue; + + if(counter == 0) { + // init fade value + mosaicInValue = 0x0f; + } else { + mosaicInValue += 0x10; + } + + *(byte*) 0x2106 = mosaicInValue; + + if(mosaicInValue == 0xff) { + return EVENT_STOP; + } else { + return EVENT_CONTINUE; + } +} + +char NMIReadPad(word counter) { + pad1 = readPad((byte) 0); + + return EVENT_CONTINUE; +} + +char scrollLeft(word counter) { + scrollValue++; + + *(byte*) 0x210d = (byte) scrollValue; + *(byte*) 0x210d = (byte) (scrollValue>>8); + + return EVENT_CONTINUE; } \ No newline at end of file diff --git a/snes/fatfstest/myEvents.h b/snes/fatfstest/myEvents.h index 719abe7..0f2412a 100644 --- a/snes/fatfstest/myEvents.h +++ b/snes/fatfstest/myEvents.h @@ -1,6 +1,6 @@ -char fadeOut(word counter); -char fadeIn(word counter); -char mosaicOut(word counter); -char mosaicIn(word counter); -char NMIReadPad(word counter); -char scrollLeft(word counter); \ No newline at end of file +char fadeOut(word counter); +char fadeIn(word counter); +char mosaicOut(word counter); +char mosaicIn(word counter); +char NMIReadPad(word counter); +char scrollLeft(word counter); diff --git a/snes/fatfstest/pad.c b/snes/fatfstest/pad.c index a88b7f1..69aefa3 100644 --- a/snes/fatfstest/pad.c +++ b/snes/fatfstest/pad.c @@ -1,23 +1,23 @@ -#include "data.h"; -#include "pad.h"; -#include "debug.h"; - -void enablePad(void) { - // Enable pad reading and NMI - *(byte*)0x4200 = 0x01; -} - -void disablePad(void) { - // Enable pad reading and NMI - *(byte*)0x4200 = 0x00; -} - -padStatus readPad(byte padNumber) { - word test; - padStatus *status; - padNumber = padNumber << 1; - test = (word) *(byte*)0x4218+padNumber << 8; - test |= (word) *(byte*)0x4219+padNumber; - status = (padStatus *) &test; - return *status; -} +#include "data.h"; +#include "pad.h"; +#include "debug.h"; + +void enablePad(void) { + // Enable pad reading and NMI + *(byte*)0x4200 = 0x01; +} + +void disablePad(void) { + // Enable pad reading and NMI + *(byte*)0x4200 = 0x00; +} + +padStatus readPad(byte padNumber) { + word test; + padStatus *status; + padNumber = padNumber << 1; + test = (word) *(byte*)0x4218+padNumber << 8; + test |= (word) *(byte*)0x4219+padNumber; + status = (padStatus *) &test; + return *status; +} diff --git a/snes/fatfstest/pad.h b/snes/fatfstest/pad.h index 595761f..33fbde5 100644 --- a/snes/fatfstest/pad.h +++ b/snes/fatfstest/pad.h @@ -1,20 +1,20 @@ -typedef struct padStatus{ - byte right:1; - byte left:1; - byte down:1; - byte up:1; - byte start:1; // Enter - byte select:1; // Space - byte Y:1; // X - byte B:1; // C - //-------------------------------- - byte Dummy:4; - byte R:1; // Z - byte L:1; // A - byte X:1; // S - byte A:1; // D -} padStatus; - -extern void enablePad(void); -extern void disablePad(void); -extern padStatus readPad(byte padNumber); +typedef struct padStatus { + byte right:1; + byte left:1; + byte down:1; + byte up:1; + byte start:1; // Enter + byte select:1; // Space + byte Y:1; // X + byte B:1; // C + // -------------------------------- + byte Dummy:4; + byte R:1; // Z + byte L:1; // A + byte X:1; // S + byte A:1; // D +} padStatus; + +extern void enablePad(void); +extern void disablePad(void); +extern padStatus readPad(byte padNumber); diff --git a/snes/fatfstest/ressource.h b/snes/fatfstest/ressource.h index c4c8d96..7f65ed4 100644 --- a/snes/fatfstest/ressource.h +++ b/snes/fatfstest/ressource.h @@ -1,2 +1,2 @@ - -extern word debugFont_pic[]; + +extern word debugFont_pic[];