From 7f75f5eb1e79cc39f100a1cd963297cc4742d1a6 Mon Sep 17 00:00:00 2001 From: "crediar@rypp.net" Date: Sat, 23 Jun 2012 18:08:56 +0000 Subject: [PATCH] Cheating: *Due space limitations only the debug version of kenobigc is supported, this means there is a bit less space for cheats. git-svn-id: svn://localhost/Users/andi/Downloads/code/trunk@2 be6c1b03-d731-4111-a574-e37d80d43941 --- Card.c | 949 +++++++++ Card.h | 114 ++ CardPatches.c | 30 + CheatCode.c | 448 ++++ Config.c | 74 + Config.h | 97 + DVD.c | 398 ++++ DVD.h | 29 + DVDPatches.c | 10 + Drive.c | 124 ++ Drive.h | 16 + FwritePatches.c | 42 + GCPad.h | 47 + HW.c | 1045 ++++++++++ HW.h | 89 + Makefile | 53 + Patches.c | 874 ++++++++ Patches.h | 52 + alloc.c | 205 ++ alloc.h | 23 + asm/CARDCheck.S | 23 + asm/CARDCheckAsync.S | 43 + asm/CARDCheckEX.S | 60 + asm/CARDClose.S | 47 + asm/CARDCreate.S | 88 + asm/CARDDelete.S | 80 + asm/CARDFastDelete.S | 69 + asm/CARDFastOpen.S | 62 + asm/CARDFreeBlocks.S | 32 + asm/CARDGetEncoding.S | 30 + asm/CARDGetMemSize.S | 30 + asm/CARDGetResultCode.S | 19 + asm/CARDGetSerialNo.S | 29 + asm/CARDGetStats.S | 74 + asm/CARDGetXferredBytes.S | 17 + asm/CARDMount.S | 26 + asm/CARDMountAsync.S | 42 + asm/CARDOpen.S | 71 + asm/CARDProbe.S | 21 + asm/CARDProbeEX.S | 41 + asm/CARDRead.S | 82 + asm/CARDRename.S | 87 + asm/CARDSetStats.S | 83 + asm/CARDWrite.S | 80 + asm/DVDInquiryAsync.S | 60 + asm/DVDSeekAbsAsyncPrio.S | 47 + asm/__CARDSync.S | 9 + asm/make.cmd | 4 + asm/padipc.S | 11 + bsdtypes.h | 32 + common.c | 2 + dip.c | 292 +++ dip.h | 138 ++ diskio.c | 93 + diskio.h | 70 + dol.h | 16 + ehci-mem.c | 85 + ehci.c | 811 ++++++++ ehci.h | 282 +++ ehci_defs.h | 160 ++ ehci_types.h | 55 + ff.c | 4048 +++++++++++++++++++++++++++++++++++++ ff.h | 335 +++ ffconf.h | 189 ++ global.h | 62 + hollywood.h | 180 ++ integer.h | 37 + iosmodule.ld | 81 + ipc.h | 67 + main.c | 299 +++ memory.c | 251 +++ memory.h | 108 + memory_asm.S | 70 + start.s | 213 ++ string.c | 125 ++ string.h | 23 + tiny_ehci_glue.c | 92 + tiny_ehci_glue.h | 25 + usb.c | 233 +++ usb.h | 176 ++ usb_os.c | 78 + usbstorage.c | 745 +++++++ usbstorage.h | 74 + utils.h | 204 ++ utils_asm.S | 116 ++ vsprintf.c | 442 ++++ vsprintf.h | 8 + 87 files changed, 16403 insertions(+) create mode 100644 Card.c create mode 100644 Card.h create mode 100644 CardPatches.c create mode 100644 CheatCode.c create mode 100644 Config.c create mode 100644 Config.h create mode 100644 DVD.c create mode 100644 DVD.h create mode 100644 DVDPatches.c create mode 100644 Drive.c create mode 100644 Drive.h create mode 100644 FwritePatches.c create mode 100644 GCPad.h create mode 100644 HW.c create mode 100644 HW.h create mode 100644 Makefile create mode 100644 Patches.c create mode 100644 Patches.h create mode 100644 alloc.c create mode 100644 alloc.h create mode 100644 asm/CARDCheck.S create mode 100644 asm/CARDCheckAsync.S create mode 100644 asm/CARDCheckEX.S create mode 100644 asm/CARDClose.S create mode 100644 asm/CARDCreate.S create mode 100644 asm/CARDDelete.S create mode 100644 asm/CARDFastDelete.S create mode 100644 asm/CARDFastOpen.S create mode 100644 asm/CARDFreeBlocks.S create mode 100644 asm/CARDGetEncoding.S create mode 100644 asm/CARDGetMemSize.S create mode 100644 asm/CARDGetResultCode.S create mode 100644 asm/CARDGetSerialNo.S create mode 100644 asm/CARDGetStats.S create mode 100644 asm/CARDGetXferredBytes.S create mode 100644 asm/CARDMount.S create mode 100644 asm/CARDMountAsync.S create mode 100644 asm/CARDOpen.S create mode 100644 asm/CARDProbe.S create mode 100644 asm/CARDProbeEX.S create mode 100644 asm/CARDRead.S create mode 100644 asm/CARDRename.S create mode 100644 asm/CARDSetStats.S create mode 100644 asm/CARDWrite.S create mode 100644 asm/DVDInquiryAsync.S create mode 100644 asm/DVDSeekAbsAsyncPrio.S create mode 100644 asm/__CARDSync.S create mode 100644 asm/make.cmd create mode 100644 asm/padipc.S create mode 100644 bsdtypes.h create mode 100644 common.c create mode 100644 dip.c create mode 100644 dip.h create mode 100644 diskio.c create mode 100644 diskio.h create mode 100644 dol.h create mode 100644 ehci-mem.c create mode 100644 ehci.c create mode 100644 ehci.h create mode 100644 ehci_defs.h create mode 100644 ehci_types.h create mode 100644 ff.c create mode 100644 ff.h create mode 100644 ffconf.h create mode 100644 global.h create mode 100644 hollywood.h create mode 100644 integer.h create mode 100644 iosmodule.ld create mode 100644 ipc.h create mode 100644 main.c create mode 100644 memory.c create mode 100644 memory.h create mode 100644 memory_asm.S create mode 100644 start.s create mode 100644 string.c create mode 100644 string.h create mode 100644 tiny_ehci_glue.c create mode 100644 tiny_ehci_glue.h create mode 100644 usb.c create mode 100644 usb.h create mode 100644 usb_os.c create mode 100644 usbstorage.c create mode 100644 usbstorage.h create mode 100644 utils.h create mode 100644 utils_asm.S create mode 100644 vsprintf.c create mode 100644 vsprintf.h diff --git a/Card.c b/Card.c new file mode 100644 index 0000000..0631113 --- /dev/null +++ b/Card.c @@ -0,0 +1,949 @@ +#include "Card.h" + +FIL CardStat; + +void CardInit( void ) +{ + FILINFO f; + u32 i,wrote; + CARDStat CStat; + + memset32( (void*)CARD_BASE, 0xdeadbeef, 0x20 ); + memset32( (void*)CARD_SHADOW, 0, 0x20 ); + +//Create savefile dirs for the current game + if( f_chdir("/saves") != FR_OK ) + { + f_mkdir("/saves"); + f_chdir("/saves"); + } + + if( f_chdir((const TCHAR*)0) != FR_OK ) + { + f_mkdir((const TCHAR*)0); + f_chdir((const TCHAR*)0); + } + + switch( f_stat( "stats.bin", &f ) ) + { + case FR_NO_FILE: + { + if( f_open( &CardStat, "stats.bin", FA_CREATE_ALWAYS | FA_READ | FA_WRITE ) != FR_OK ) + { + //dbgprintf("MC:Could not create stats file!\n"); + + } else { + + memset32( &CStat, 0, sizeof( CARDStat ) ); + for( i=0; i < CARD_MAX_FILES; ++i ) + { + f_write( &CardStat, &CStat, sizeof( CARDStat ), &wrote ); + } + f_sync( &CardStat ); + } + } break; + case FR_OK: + { + if( f_open( &CardStat, "stats.bin", FA_OPEN_EXISTING | FA_READ | FA_WRITE ) != FR_OK ) + { + ;//dbgprintf("MC:Could not create stats file!\n"); + } + } break; + default: + { + ;//dbgprintf("MC:CardInit fuck up\n"); + } break; + } + + write32( 0x2FA0, 0 ); +} +s32 CardFindFreeEntry( void ) +{ + CARDStat CStat; + u32 i; + u32 read; + + for( i=0; i < CARD_MAX_FILES; ++i ) + { + f_lseek( &CardStat, sizeof(CARDStat) * i ); + f_read( &CardStat, &CStat, sizeof(CARDStat), &read ); + + if( CStat.length == 0 ) + { + //dbgprintf("CardFindFreeEntry(%d)\n", i ); + return i; + } + } + + return -1; +} + +s32 CardFindEntryByName( char *Filename ) +{ + CARDStat CStat; + u32 i; + u32 read; + + for( i=0; i < CARD_MAX_FILES; ++i ) + { + f_lseek( &CardStat, sizeof(CARDStat) * i ); + f_read( &CardStat, &CStat, sizeof(CARDStat), &read ); + + if( memcmp( Filename, CStat.fileName, strlen(Filename) ) == 0 ) + { + //dbgprintf("CardFindEntryByName(%d,%s,%s)\n", i, Filename, CStat.fileName ); + return i; + } + } + return -1; +} + + +s32 CardOpenFile( char *Filename, CARDFileInfo *CFInfo ) +{ + FIL savefile; + s32 Slot,fres; + + Slot = CardFindEntryByName(Filename); + if( Slot < 0 ) + { + write32( CARD_SCMD_4, -1 ); + write32( CARD_SRETURN, CARD_NO_FILE ); + return -4; + } + + fres = f_open( &savefile, Filename, FA_READ|FA_WRITE|FA_OPEN_EXISTING ) ; + switch( fres ) + { + case FR_NO_PATH: + case FR_NO_FILE: + { + ;//dbgprintf("MC:Failed to open:\"%s\":%d\n", Filename, fres ); + + write32( CARD_SCMD_4, -1 ); + write32( CARD_SRETURN, CARD_NO_FILE ); + } break; + default: + { + EXIControl(1); + ;//dbgprintf("MC:Failed to open:\"%s\":%d\n", Filename, fres ); + Shutdown(); + } break; + case FR_OK: + { + f_close( &savefile ); + + write32( CARD_SCMD_4, Slot ); + write32( CARD_SRETURN, CARD_SUCCESS ); + } break; + } + + return 0; +} +s32 CardFastOpenFile( u32 FileNo, CARDFileInfo *CFInfo ) +{ + CARDStat CStat; + FIL savefile; + s32 fres; + u32 read; + + if( FileNo >= CARD_MAX_FILES ) + { + write32( CARD_SRETURN, CARD_NO_FILE ); + return 0; + } + + f_lseek( &CardStat, sizeof(CARDStat) * FileNo ); + f_read( &CardStat, &CStat, sizeof(CARDStat), &read ); + + if( CStat.length == 0 ) + { + write32( CARD_SRETURN, CARD_NO_FILE ); + return 0; + } + + fres = f_open( &savefile, CStat.fileName, FA_READ|FA_WRITE|FA_OPEN_EXISTING ) ; + switch( fres ) + { + case FR_NO_PATH: + case FR_NO_FILE: + { + ;//dbgprintf("MC:Failed to open:\"%s\":%d\n", CStat.fileName, fres ); + + write32( CARD_SRETURN, CARD_NO_FILE ); + } break; + default: + { + EXIControl(1); + ;//dbgprintf("MC:Failed to open:\"%s\":%d\n", CStat.fileName, fres ); + Shutdown(); + } break; + case FR_OK: + { + write32( CARD_SCMD_4, savefile.fsize ); + + f_close( &savefile ); + + write32( CARD_SRETURN, CARD_SUCCESS ); + } break; + } + + return 0; +} +void CardDeleteFile( char *Filename ) +{ + CARDStat CStat; + u32 wrote; + s32 Slot; + + Slot = CardFindEntryByName( Filename ); + if( Slot < 0 ) + { + dbgprintf("MC:\"%s\" doesn't exists\n", Filename ); + write32( CARD_SRETURN, CARD_NO_FILE ); + return; + } + + memset32( &CStat, 0, sizeof(CARDStat) ); + + f_lseek( &CardStat, sizeof(CARDStat) * Slot ); + f_write( &CardStat, &CStat, sizeof(CARDStat), &wrote ); + f_sync( &CardStat ); + + f_unlink( Filename ); + + write32( CARD_SRETURN, CARD_SUCCESS ); +} +void CardFastDelete( u32 FileNo ) +{ + CARDStat CStat; + u32 read; + + if( FileNo >= CARD_MAX_FILES ) + { + write32( CARD_SRETURN, CARD_NO_FILE ); + return; + } + + f_lseek( &CardStat, sizeof(CARDStat) * FileNo ); + f_read( &CardStat, &CStat, sizeof(CARDStat), &read ); + + if( f_unlink( CStat.fileName ) == FR_OK ) + { + memset32( &CStat, 0, sizeof(CARDStat) ); + + f_lseek( &CardStat, sizeof(CARDStat) * FileNo ); + f_write( &CardStat, &CStat, sizeof(CARDStat), &read ); + f_sync( &CardStat ); + + write32( CARD_SRETURN, CARD_SUCCESS ); + + } else { + write32( CARD_SRETURN, CARD_NO_FILE ); + } + + return; +} +void CardRename( char *NameSrc, char *NameDst ) +{ + CARDStat CStat; + u32 wrote; + s32 Slot; + + Slot = CardFindEntryByName( NameDst ); + if( Slot != -1 ) + { + f_unlink( NameDst ); + + f_lseek( &CardStat, sizeof(CARDStat) * Slot ); + f_read( &CardStat, &CStat, sizeof(CARDStat), &wrote ); + + memset32( &CStat, 0, sizeof(CARDStat) ); + + f_lseek( &CardStat, sizeof(CARDStat) * Slot ); + f_write( &CardStat, &CStat, sizeof(CARDStat), &wrote ); + f_sync( &CardStat ); + } + + Slot = CardFindEntryByName( NameSrc ); + if( Slot == -1 ) + { + write32( CARD_SRETURN, CARD_NO_FILE ); + return; + } + + switch( f_rename( NameSrc, NameDst ) ) + { + case FR_OK: + { + f_lseek( &CardStat, sizeof(CARDStat) * Slot ); + f_read( &CardStat, &CStat, sizeof(CARDStat), &wrote ); + + memcpy( CStat.fileName, NameDst, 32 ); + + f_lseek( &CardStat, sizeof(CARDStat) * Slot ); + f_write( &CardStat, &CStat, sizeof(CARDStat), &wrote ); + f_sync( &CardStat ); + + write32( CARD_SRETURN, CARD_SUCCESS ); + + } break; + //case FR_EXIST: + //{ + // write32( CARD_SRETURN, CARD_FILE_EXISTS ); + //} break; + default: + { + write32( CARD_SRETURN, CARD_FATAL_ERROR ); + } break; + } +} +void CardCreateFile( char *Filename, u32 Size, CARDFileInfo *CFInfo ) +{ + CARDStat CStat; + u32 i,fres,read; + s32 Slot; + FIL savefile; + + Slot = CardFindEntryByName( Filename ); + if( Slot >= 0 ) + { + ;//dbgprintf("MC:\"%s\" already exists\n", Filename ); + write32( CARD_SRETURN, CARD_FILE_EXISTS ); + return; + } + + Slot = CardFindFreeEntry(); + if( Slot < 0 ) + return; + + fres = f_open( &savefile, Filename, FA_READ|FA_WRITE|FA_CREATE_NEW ); + switch( fres ) + { + case FR_EXIST: + { + write32( CARD_SCMD_4, 0 ); + write32( CARD_SRETURN, CARD_FILE_EXISTS ); + } break; + default: + { + EXIControl(1); + ;//dbgprintf("MC:Failed to create:\"%s\":%d\n", Filename, fres ); + Shutdown(); + } break; + case FR_OK: + { + f_lseek( &CardStat, sizeof(CARDStat) * Slot ); + f_read( &CardStat, &CStat, sizeof(CARDStat), &read ); + + memcpy( CStat.fileName, Filename, strlen(Filename) ); + + CStat.length = Size; + CStat.time = 0x15745a1a; + memcpy( CStat.gameName, (void*)0, 4 ); + memcpy( CStat.company, (void*)4, 2 ); + + CStat.bannerFormat = 0; + CStat.iconAddr = -1; + CStat.iconFormat = 0; + CStat.iconSpeed = 0; + CStat.commentAddr = -1; + + CStat.offsetBanner = -1; + CStat.offsetBannerTlut = -1; + for( i=0; i < 8; ++i ) + CStat.offsetIcon[i] = -1; + CStat.offsetIconTlut = -1; + CStat.offsetData = 0; + + f_lseek( &CardStat, sizeof(CARDStat) * Slot ); + f_write( &CardStat, &CStat, sizeof(CARDStat), &read ); + f_sync( &CardStat ); + + CFInfo->chan = 0; + CFInfo->fileNo = Slot; + CFInfo->iBlock = 0; + CFInfo->length = Size; + CFInfo->offset = 0; + + char *buf = (char*)malloc(512); + memset32( buf, 0, 512 ); + + //Create full file + for( i=0; i < Size; i+=512 ) + { + f_write( &savefile, buf, 512, &read); + } + + f_close( &savefile ); + + write32( CARD_SCMD_4, Slot ); + write32( CARD_SRETURN, CARD_SUCCESS ); + } break; + } +} +void CardReadFile( u32 FileNo, u8 *Buffer, u32 Length, u32 Offset ) +{ + u32 read; + CARDStat CStat; + + FIL savefile; + + f_lseek( &CardStat, sizeof(CARDStat) * FileNo ); + f_read( &CardStat, &CStat, sizeof(CARDStat), &read ); + + if( f_open( &savefile, CStat.fileName, FA_OPEN_EXISTING | FA_READ ) == FR_OK ) + { + f_lseek( &savefile, Offset ); + f_read( &savefile, (void*)Buffer, Length, &read ); + f_close( &savefile ); + } +} +void CardWriteFile( u32 FileNo, u8 *Buffer, u32 Length, u32 Offset ) +{ + u32 read; + CARDStat CStat; + + FIL savefile; + + f_lseek( &CardStat, sizeof(CARDStat) * FileNo ); + f_read( &CardStat, &CStat, sizeof(CARDStat), &read ); + + switch( f_open( &savefile, CStat.fileName, FA_OPEN_EXISTING | FA_WRITE ) ) + { + case FR_OK: + { +#ifdef CARD_DEBUG + if( f_lseek( &savefile, Offset ) != FR_OK ) + { + EXIControl(1); + dbgprintf("Failed to seek to %08x\n", Offset ); + Shutdown(); + } else { + if( f_write( &savefile, (void*)Buffer, Length, &read ) != FR_OK ) + { + EXIControl(1); + dbgprintf("Failed to write %d bytes to %p\n", Length, Buffer ); + Shutdown(); + } + if( read != Length ) + { + EXIControl(1); + dbgprintf("Short write; %d of %d bytes written!\n", read, Length ); + Shutdown(); + } + } +#else + f_lseek( &savefile, Offset ); + f_write( &savefile, (void*)Buffer, Length, &read ); +#endif + f_close( &savefile ); + } break; + default: + { + EXIControl(1); + dbgprintf("Failed to open:\"%s\"\n", CStat.fileName ); + Shutdown(); + } break; + } +} +void CardUpdateStats( CARDStat *CStat ) +{ + u32 IconSize,BannerSize,Format,Offset,i,TLut=0; + + BannerSize = CARD_BANNER_WIDTH * CARD_BANNER_HEIGHT; + IconSize = CARD_ICON_WIDTH * CARD_ICON_HEIGHT; + Offset = CStat->iconAddr; + + if( CStat->bannerFormat & CARD_STAT_BANNER_C8 ) + { + CStat->offsetBanner = Offset; + CStat->offsetBannerTlut = Offset + BannerSize; + + Offset += BannerSize + 512; + + } else if( CStat->bannerFormat & CARD_STAT_BANNER_RGB5A3 ) { + + CStat->offsetBanner = Offset; + CStat->offsetBannerTlut = -1; + + Offset += BannerSize * 2; + + } else { + + CStat->offsetBanner = -1; + CStat->offsetBannerTlut = -1; + } + + for( i=0; i < CARD_ICON_MAX; ++i ) + { + Format = CStat->iconFormat >> ( i * 2 ); + + if( Format & CARD_STAT_ICON_C8 ) + { + CStat->offsetIcon[i] = Offset; + Offset += IconSize; + TLut = 1; + + } else if ( Format & CARD_STAT_ICON_RGB5A3 ) { + + CStat->offsetIcon[i] = Offset; + Offset += IconSize * 2; + + } else { + CStat->offsetIcon[i] = -1; + } + } + + if( TLut ) + { + CStat->offsetIconTlut = Offset; + Offset += 512; + } else { + CStat->offsetIconTlut = -1; + } + + CStat->offsetData = Offset; +} +u32 Device = 0; +void CARDUpdateRegisters( void ) +{ + u32 read; + + if( read32(CARD_CONTROL) != 0xdeadbeef ) + { + if( read32( CARD_CONTROL ) & (~3) ) + { + write32( CARD_CONTROL, 0xdeadbeef ); + return; + } + + write32( CARD_SCONTROL, read32(CARD_CONTROL) & 3 ); + + clear32( CARD_SSTATUS, 0x14 ); + + write32( CARD_CONTROL, 0xdeadbeef ); + +#ifdef ACTIVITYLED + set32( HW_GPIO_OUT, 1<<5 ); +#endif + + while( read32(CARD_CMD) == 0xdeadbeef ); + write32( CARD_SCMD, read32(CARD_CMD) ); + write32( CARD_CMD, 0xdeadbeef ); + + if( read32(CARD_CMD_1) != 0xdeadbeef ) + { + write32( CARD_SCMD_1, read32(CARD_CMD_1) ); + write32( CARD_CMD_1, 0xdeadbeef ); + } + + if( read32(CARD_CMD_2) != 0xdeadbeef ) + { + write32( CARD_SCMD_2, read32(CARD_CMD_2) ); + write32( CARD_CMD_2, 0xdeadbeef ); + } + + if( read32(CARD_CMD_3) != 0xdeadbeef ) + { + write32( CARD_SCMD_3, read32(CARD_CMD_3) ); + write32( CARD_CMD_3, 0xdeadbeef ); + } + + if( read32(CARD_CMD_4) != 0xdeadbeef ) + { + write32( CARD_SCMD_4, read32(CARD_CMD_4) ); + write32( CARD_CMD_4, 0xdeadbeef ); + } + + switch( read32(CARD_SCMD) >> 24 ) + { + case 0x00: + { + ;//dbgprintf("CARD:Warning unknown command!\n"); + } break; + default: + { + //EXIControl(1); + + dbgprintf("CARD:Unknown CMD:%08X %08X %08X %08X %08X %08X\n", read32(CARD_SCMD), read32(CARD_SCMD_1), read32(CARD_SCMD_2), read32(CARD_SCMD_3), read32(CARD_SCMD_4), read32(CARD_SCONTROL) ); + + Shutdown(); + } break; + + /* CARDOpen( char *FileName ) */ + case 0xC0: + { + char FileName[32]; + + u32 FInfo = P2C(read32(CARD_SCMD_2)); + + memcpy( FileName, (void*)0x17E0, 32 ); + +#ifdef CARDDEBUG + dbgprintf("MC:CARDOpen( \"%s\", 0x%08x )", FileName, FInfo ); +#endif + + CardOpenFile( (char*)FileName, (CARDFileInfo*)FInfo ); + + while( read32(CARD_CONTROL) & 1 ) + clear32( CARD_CONTROL, 1 ); + + while( (read32(CARD_SSTATUS) & 0x10) != 0x10 ) + set32( CARD_SSTATUS, 0x10 ); + +#ifdef CARDDEBUG + dbgprintf(":%d\n", read32( CARD_SRETURN ) ); +#endif + } break; + case 0xC1: + { + u32 FileNo = read32(CARD_SCMD_1); + +#ifdef CARDDEBUG + dbgprintf("MC:CARDClose( %d )", FileNo ); +#endif + if( FileNo < 0 ) + write32( CARD_SRETURN, -128 ); + else + write32( CARD_SRETURN, 0 ); + + while( read32(CARD_SCONTROL) & 1 ) + clear32( CARD_SCONTROL, 1 ); + + set32( CARD_SSTATUS, 0x10 ); +#ifdef CARDDEBUG + dbgprintf(":%d\n", read32(CARD_SRETURN) ); +#endif + } break; + case 0xC2: + { + char FileName[32]; + u32 Size = read32(CARD_SCMD_2); + u32 FInfo = P2C(read32(CARD_SCMD_3)); + + memcpy( FileName, (void*)0x17E0, 32 ); + +#ifdef CARDDEBUG + dbgprintf("MC:CARDCreate( \"%s\", 0x%04x, 0x%08x )", FileName, Size, FInfo ); +#endif + + CardCreateFile( (char*)FileName, Size, (CARDFileInfo*)FInfo ); + + write32( 0x2FA0, read32(0x2FA0) + CARD_XFER_CREATE ); + + while( read32(CARD_SCONTROL) & 1 ) + clear32( CARD_SCONTROL, 1 ); + + set32( CARD_SSTATUS, 0x10 ); +#ifdef CARDDEBUG + dbgprintf(":%d\n", read32( CARD_SRETURN ) ); +#endif + } break; + case 0xC3: + { + CARDStat CS; + +#ifdef CARDDEBUG + // dbgprintf("MC:CARDGetState( %d, 0x%08x, ", read32(CARD_SCMD_1), P2C(read32(CARD_SCMD_2)) ); +#endif + + if( read32(CARD_SCMD_1) >= CARD_MAX_FILES ) + { + EXIControl(1); + dbgprintf("MC: Invalid file slot!:%d\n", read32(CARD_SCMD_1) ); + Shutdown(); + } + + f_lseek( &CardStat, sizeof(CARDStat) * read32(CARD_SCMD_1) ); + f_read( &CardStat, &CS, sizeof(CARDStat), &read ); + + if( CS.length == 0 ) + { +#ifdef CARDDEBUG + // dbgprintf(")"); +#endif + write32( CARD_SRETURN, CARD_NO_FILE ); + } else { + +#ifdef CARDDEBUG + // dbgprintf("\"%s\")", CS.fileName ); + dbgprintf("MC:CARDGetState( %d, 0x%08x, \"%s\"):0", read32(CARD_SCMD_1), P2C(read32(CARD_SCMD_2)), CS.fileName ); +#endif + + CardUpdateStats( &CS ); + + memcpy( (void*)0x1780, &CS, sizeof(CARDStat) ); + +#ifdef CARDDEBUG + CARDStat *CSTAT = (CARDStat*)0x1780; + + dbgprintf("\nMC:Card Status:\n"); + dbgprintf("\tFilename:%.32s\n", CSTAT->fileName ); + dbgprintf("\tGameName:%.4s\n", CSTAT->gameName ); + dbgprintf("\tCompany :%.2s\n", CSTAT->company ); + dbgprintf("\tLength :%d\n", CSTAT->length ); + dbgprintf("\tTime :%d\n\n", CSTAT->time ); + + dbgprintf("\tBannerFormat:%d\n", CSTAT->bannerFormat ); + dbgprintf("\tIconAddress :0x%04X\n", CSTAT->iconAddr ); + dbgprintf("\tIconFormat :0x%02X\n", CSTAT->iconFormat ); + dbgprintf("\tIconSpeed :0x%02X\n", CSTAT->iconSpeed ); + dbgprintf("\tComntAddress:0x%04X\n\n", CSTAT->commentAddr ); + + dbgprintf("\tOffsetBanner :0x%04X\n", CSTAT->offsetBanner ); + dbgprintf("\tOffsetBnrTlt :0x%04X\n", CSTAT->offsetBannerTlut ); + + for( i=0; i < CARD_ICON_MAX; ++i) + dbgprintf("\tOffsetIcon[%d]:0x%04X\n", i, CSTAT->offsetIcon[i] ); + + dbgprintf("\tOffsetIconTlt:0x%04X\n", CSTAT->offsetIconTlut ); + dbgprintf("\tOffsetData :0x%04X\n", CSTAT->offsetData ); +#endif + + write32( CARD_SRETURN, CARD_SUCCESS ); + } + + while( read32(CARD_SCONTROL) & 1 ) + clear32( CARD_SCONTROL, 1 ); + + set32( CARD_SSTATUS, 0x10 ); +#ifdef CARDDEBUG + // dbgprintf("MC:CARDGetState( %d, 0x%08x, ):%d\n", read32(CARD_SCMD_1), P2C(read32(CARD_SCMD_2)), read32( CARD_SRETURN) ); +#endif + } break; + case 0xC4: + { + CARDStat CS; + +#ifdef CARDDEBUG + dbgprintf("MC:CARDSetState( %d, 0x%08x )", read32(CARD_SCMD_1), P2C(read32(CARD_SCMD_2)) ); +#endif + + if( read32(CARD_SCMD_1) >= CARD_MAX_FILES ) + { + EXIControl(1); + dbgprintf("\nMC: Invalid file slot!\n"); + Shutdown(); + } + + CARDStat *CStat = (CARDStat *) P2C(read32(CARD_SCMD_2)); + + f_lseek( &CardStat, sizeof(CARDStat) * read32(CARD_SCMD_1) ); + f_read( &CardStat, &CS, sizeof(CARDStat), &read ); + + if( CS.length == 0 ) + { +#ifdef CARDDEBUG + dbgprintf(")"); +#endif + write32( CARD_SRETURN, CARD_NO_FILE ); + + } else { + + CS.bannerFormat = CStat->bannerFormat; + CS.iconAddr = CStat->iconAddr; + CS.iconFormat = CStat->iconFormat; + CS.iconSpeed = CStat->iconSpeed; + CS.commentAddr = CStat->commentAddr; + +#ifdef CARDDEBUG + dbgprintf("\nMC:Card Status:\n"); + dbgprintf("\tFilename:%.32s\n", CS.fileName ); + dbgprintf("\tGameName:%.4s\n", CS.gameName ); + dbgprintf("\tCompany :%.2s\n", CS.company ); + dbgprintf("\tLength :%d\n", CS.length ); + dbgprintf("\tTime :%d\n\n", CS.time ); + + dbgprintf("\tBannerFormat:%d\n", CS.bannerFormat ); + dbgprintf("\tIconAddress :0x%04X\n", CS.iconAddr ); + dbgprintf("\tIconFormat :0x%02X\n", CS.iconFormat ); + dbgprintf("\tIconSpeed :0x%02X\n", CS.iconSpeed ); + dbgprintf("\tComntAddress:0x%04X\n\n", CS.commentAddr ); +#endif + + CardUpdateStats( &CS ); + +#ifdef CARDDEBUG + dbgprintf("\tOffsetBanner :0x%04X\n", CS.offsetBanner ); + dbgprintf("\tOffsetBnrTlt :0x%04X\n", CS.offsetBannerTlut ); + + for( i=0; i < CARD_ICON_MAX; ++i) + dbgprintf("\tOffsetIcon[%d]:0x%04X\n", i, CS.offsetIcon[i] ); + + dbgprintf("\tOffsetIconTlt:0x%04X\n", CS.offsetIconTlut ); + dbgprintf("\tOffsetData :0x%04X\n", CS.offsetData ); +#endif + + f_lseek( &CardStat, sizeof(CARDStat) * read32(CARD_SCMD_1) ); + f_write( &CardStat, &CS, sizeof(CARDStat), &read ); + f_sync( &CardStat ); + + write32( 0x2FA0, read32(0x2FA0) + CARD_XFER_SETSTATUS ); + + write32( CARD_SRETURN, CARD_SUCCESS ); + } + + while( read32(CARD_SCONTROL) & 1 ) + clear32( CARD_SCONTROL, 1 ); + + set32( CARD_SSTATUS, 0x10 ); +#ifdef CARDDEBUG + dbgprintf(":1\n"); +#endif + } break; + /* CARDFastOpen( u32 FileNO, CARDFileInfo *CFInfo ) */ + case 0xC5: + { + u32 FileNo = read32(CARD_SCMD_1); + u32 FInfo = P2C(read32(CARD_SCMD_2)); + +#ifdef CARDDEBUG + dbgprintf("MC:CARDFastOpen( %d, 0x%08X )", FileNo, FInfo ); +#endif + + CardFastOpenFile( FileNo, (CARDFileInfo*)FInfo ); + + while( read32(CARD_CONTROL) & 1 ) + clear32( CARD_CONTROL, 1 ); + + while( (read32(CARD_SSTATUS) & 0x10) != 0x10 ) + set32( CARD_SSTATUS, 0x10 ); + +#ifdef CARDDEBUG + dbgprintf(":%d\n", read32( CARD_SRETURN ) ); +#endif + + } break; + case 0xC6: + { + char FileName[32]; + + memcpy( FileName, (void*)0x17E0, 32 ); +#ifdef CARDDEBUG + dbgprintf("MC:CARDDelete( \"%s\" )", FileName ); +#endif + + CardDeleteFile( (char*)FileName ); + + write32( 0x2FA0, read32(0x2FA0) + CARD_XFER_DELETE ); + + while( read32(CARD_SCONTROL) & 1 ) + clear32( CARD_SCONTROL, 1 ); + + set32( CARD_SSTATUS, 0x10 ); +#ifdef CARDDEBUG + dbgprintf(":%d\n", read32( CARD_SRETURN ) ); +#endif + } break; + case 0xC8: + { + u32 Buffer = P2C(read32(CARD_SCMD_1)); + u32 Length = read32(CARD_SCMD_2); + u32 Offset = read32(CARD_SCMD_3); + u32 FileNo = read32(CARD_SCMD_4); + +#ifdef CARDDEBUG + dbgprintf("MC:CARDWrite( %d, 0x%08x, 0x%04x, 0x%04x )", FileNo, Buffer, Offset, Length ); +#endif + + if( FileNo >= CARD_MAX_FILES ) + { + EXIControl(1); + dbgprintf("\nMC: Invalid file slot!:%d\n", FileNo ); + Shutdown(); + } + + CardWriteFile( FileNo, (u8*)Buffer, Length, Offset ); + + write32( 0x2FA0, read32(0x2FA0) + CARD_XFER_WRITE + Length ); + + write32( CARD_SRETURN, 0 ); + + while( read32(CARD_SCONTROL) & 1 ) + clear32( CARD_SCONTROL, 1 ); + + set32( CARD_SSTATUS, 0x10 ); + +#ifdef CARDDEBUG + dbgprintf(":%u\n", read32(CARD_SRETURN) ); +#endif + } break; + case 0xC9: + { + u32 Buffer = P2C(read32(CARD_SCMD_1)); + u32 Length = read32(CARD_SCMD_2); + u32 Offset = read32(CARD_SCMD_3); + u32 FileNo = read32(CARD_SCMD_4); + +#ifdef CARDDEBUG + dbgprintf("MC:CARDRead( %d, 0x%08x, 0x%04x, 0x%04x )", FileNo, Buffer, Offset, Length ); +#endif + + if( FileNo >= CARD_MAX_FILES ) + { + EXIControl(1); + dbgprintf("\nMC: Invalid file slot!:%d\n", FileNo ); + Shutdown(); + } + + CardReadFile( FileNo, (u8*)Buffer, Length, Offset ); + + write32( 0x2FA0, read32(0x2FA0) + Length ); + + write32( CARD_SRETURN, 0 ); + + while( read32(CARD_SCONTROL) & 1 ) + clear32( CARD_SCONTROL, 1 ); + + set32( CARD_SSTATUS, 0x10 ); + +#ifdef CARDDEBUG + dbgprintf(":%u\n", read32(CARD_SRETURN) ); +#endif + } break; + case 0xCA: + { + u32 FileNo = read32( CARD_SCMD_1 ); +#ifdef CARDDEBUG + dbgprintf("MC:CARDFastDelete( %u )", FileNo ); +#endif + CardFastDelete( FileNo ); + + write32( 0x2FA0, read32(0x2FA0) + CARD_XFER_DELETE ); + + while( read32(CARD_SCONTROL) & 1 ) + clear32( CARD_SCONTROL, 1 ); + + set32( CARD_SSTATUS, 0x10 ); +#ifdef CARDDEBUG + dbgprintf(":%d\n", read32( CARD_SRETURN ) ); +#endif + } break; + case 0xCB: + { + char NameSrc[32]; + char NameDst[32]; + + memcpy( NameSrc, (void*)0x17C0, 32 ); + memcpy( NameDst, (void*)0x17E0, 32 ); + +#ifdef CARDDEBUG + dbgprintf("MC:CARDRename( \"%s\", \"%s\" )", NameSrc, NameDst ); +#endif + CardRename( NameSrc, NameDst ); + + while( read32(CARD_SCONTROL) & 1 ) + clear32( CARD_SCONTROL, 1 ); + + set32( CARD_SSTATUS, 0x10 ); +#ifdef CARDDEBUG + dbgprintf(":%d\n", read32( CARD_SRETURN ) ); +#endif + } break; + } +#ifdef ACTIVITYLED + clear32( HW_GPIO_OUT, 1<<5 ); +#endif + } +} diff --git a/Card.h b/Card.h new file mode 100644 index 0000000..8d317a8 --- /dev/null +++ b/Card.h @@ -0,0 +1,114 @@ +#ifndef _CARD_ +#define _CARD_ + +#include "string.h" +#include "global.h" +#include "alloc.h" +#include "ff.h" +#include "vsprintf.h" +#include "HW.h" +#include "vsprintf.h" +#ifdef TRIFORCE +#include "JVSIOMessage.h" +#endif +#define CARD_MAX_FILES 128 + +#define CARD_BASE 0x00002F60 + +#define CARD_CMD (CARD_BASE+0x00) +#define CARD_CMD_1 (CARD_BASE+0x04) +#define CARD_CMD_2 (CARD_BASE+0x08) +#define CARD_CMD_3 (CARD_BASE+0x0C) +#define CARD_CMD_4 (CARD_BASE+0x10) +#define CARD_RETURN (CARD_BASE+0x14) +#define CARD_CONTROL (CARD_BASE+0x18) +#define CARD_STATUS (CARD_BASE+0x1C) + +#define CARD_SHADOW (CARD_BASE + 0x20) + +#define CARD_SCMD (CARD_SHADOW+0x00) +#define CARD_SCMD_1 (CARD_SHADOW+0x04) +#define CARD_SCMD_2 (CARD_SHADOW+0x08) +#define CARD_SCMD_3 (CARD_SHADOW+0x0C) +#define CARD_SCMD_4 (CARD_SHADOW+0x10) +#define CARD_SRETURN (CARD_SHADOW+0x14) +#define CARD_SCONTROL (CARD_SHADOW+0x18) +#define CARD_SSTATUS (CARD_SHADOW+0x1C) + +// internal API command xfer bytes +#define CARD_XFER_CREATE (2 * 8 * 1024) // CARDCreate[Async] +#define CARD_XFER_DELETE (2 * 8 * 1024) // CARD[Fast]Delete[Async] +#define CARD_XFER_MOUNT (5 * 8 * 1024) // CARDMount[Async] +#define CARD_XFER_FORMAT (5 * 8 * 1024) // CARDFormat[Async] +#define CARD_XFER_RENAME (1 * 8 * 1024) // CARDRename[Async] +#define CARD_XFER_SETSTATUS (1 * 8 * 1024) // CARDSetStatus[Async] +#define CARD_XFER_SETATTRIBUTES (1 * 8 * 1024) // CARDSetAttributes[Async] +#define CARD_XFER_WRITE (1 * 8 * 1024) // CARDWrite[Async] + + +#define CARD_FILENAME_MAX 32 + +#define CARD_ICON_MAX 8 +#define CARD_ICON_WIDTH 32 +#define CARD_ICON_HEIGHT 32 +#define CARD_BANNER_WIDTH 96 +#define CARD_BANNER_HEIGHT 32 + +#define CARD_STAT_ICON_NONE 0 +#define CARD_STAT_ICON_C8 1 +#define CARD_STAT_ICON_RGB5A3 2 + +#define CARD_STAT_BANNER_NONE 0 +#define CARD_STAT_BANNER_C8 1 +#define CARD_STAT_BANNER_RGB5A3 2 + +enum CardStatus +{ + CARD_SUCCESS = 0, + CARD_NO_FILE = -4, + CARD_FILE_EXISTS = -7, + CARD_FATAL_ERROR =-128, +}; + +typedef struct CARDFileInfo +{ +/* 0x00 */ s32 chan; +/* 0x04 */ s32 fileNo; + +/* 0x08 */ s32 offset; +/* 0x0C */ s32 length; +/* 0x10 */ u16 iBlock; +} CARDFileInfo; + +typedef struct CARDStat +{ + // read-only (Set by CARDGetStatus) +/* 0x00 */ char fileName[32]; +/* 0x20 */ u32 length; +/* 0x24 */ u32 time; // seconds since 01/01/2000 midnight +/* 0x28 */ u8 gameName[4]; +/* 0x2C */ u8 company[2]; + + // read/write (Set by CARDGetStatus/CARDSetStatus) +/* 0x2E */ u8 bannerFormat; +/* 0x30 */ u32 iconAddr; // offset to the banner, bannerTlut, icon, iconTlut data set. +/* 0x34 */ u16 iconFormat; +/* 0x36 */ u16 iconSpeed; +/* 0x38 */ u32 commentAddr; // offset to the pair of 32 byte character strings. + + // read-only (Set by CARDGetStatus) +/* 0x3C */ u32 offsetBanner; +/* 0x40 */ u32 offsetBannerTlut; +/* 0x44 */ u32 offsetIcon[8]; +/* 0x64 */ u32 offsetIconTlut; +/* 0x68 */ u32 offsetData; +} CARDStat; + + +void CardInit( void ); +void CARDUpdateRegisters( void ); +s32 CardFindFreeEntry( void ); +s32 CardOpenFile( char *Filename, CARDFileInfo *CFInfo ); +void CardCreateFile( char *Filename, u32 Size, CARDFileInfo *CFInfo ); + +#endif diff --git a/CardPatches.c b/CardPatches.c new file mode 100644 index 0000000..b87358c --- /dev/null +++ b/CardPatches.c @@ -0,0 +1,30 @@ +#ifndef __CARDPATCHES__ +#define __CARDPATCHES__ + +#include "asm\__CARDSync.h" +#include "asm\CARDCheck.h" +#include "asm\CARDCheckAsync.h" +#include "asm\CARDCheckEX.h" +#include "asm\CARDClose.h" +#include "asm\CARDCreate.h" +#include "asm\CARDDelete.h" +#include "asm\CARDFastOpen.h" +#include "asm\CARDFreeBlocks.h" +#include "asm\CARDGetEncoding.h" +#include "asm\CARDGetMemSize.h" +#include "asm\CARDGetSerialNo.h" +#include "asm\CARDGetStats.h" +#include "asm\CARDMount.h" +#include "asm\CARDMountAsync.h" +#include "asm\CARDOpen.h" +#include "asm\CARDProbe.h" +#include "asm\CARDProbeEX.h" +#include "asm\CARDRead.h" +#include "asm\CARDSetStats.h" +#include "asm\CARDWrite.h" +#include "asm\CARDGetResultCode.h" +#include "asm\CARDGetXferredBytes.h" +#include "asm\CARDFastDelete.h" +#include "asm\CARDRename.h" + +#endif diff --git a/CheatCode.c b/CheatCode.c new file mode 100644 index 0000000..6942a10 --- /dev/null +++ b/CheatCode.c @@ -0,0 +1,448 @@ +#include "global.h" + +unsigned char kenobigcDBG[4288] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x27, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x21, 0xff, 0x58, 0x90, 0x01, 0x00, 0x08, + 0x7c, 0x08, 0x02, 0xa6, 0x90, 0x01, 0x00, 0xac, 0x7c, 0x00, 0x00, 0x26, 0x90, 0x01, 0x00, 0x0c, + 0x7c, 0x09, 0x02, 0xa6, 0x90, 0x01, 0x00, 0x10, 0x7c, 0x01, 0x02, 0xa6, 0x90, 0x01, 0x00, 0x14, + 0xbc, 0x61, 0x00, 0x18, 0x7f, 0x20, 0x00, 0xa6, 0x63, 0x3a, 0x20, 0x00, 0x73, 0x5a, 0xf9, 0xff, + 0x7f, 0x40, 0x01, 0x24, 0xd8, 0x41, 0x00, 0x98, 0xd8, 0x61, 0x00, 0xa0, 0x3f, 0xe0, 0x80, 0x00, + 0x3e, 0x80, 0xcc, 0x00, 0xa3, 0x94, 0x40, 0x10, 0x63, 0x95, 0x00, 0xff, 0xb2, 0xb4, 0x40, 0x10, + 0x48, 0x00, 0x06, 0x55, 0x3a, 0xa0, 0x00, 0x00, 0x3a, 0xc0, 0x00, 0x19, 0x3a, 0xe0, 0x00, 0xd0, + 0x3f, 0x00, 0xcc, 0x00, 0x63, 0xf2, 0x27, 0x74, 0x80, 0x01, 0x00, 0xac, 0x90, 0x12, 0x00, 0x04, + 0x92, 0xb8, 0x64, 0x3c, 0x48, 0x00, 0x04, 0x2d, 0x41, 0x82, 0x05, 0xa4, 0x2c, 0x1d, 0x00, 0x04, + 0x40, 0x80, 0x00, 0x10, 0x2c, 0x1d, 0x00, 0x01, 0x41, 0x80, 0x05, 0x94, 0x48, 0x00, 0x03, 0x4c, + 0x41, 0x82, 0x04, 0xf0, 0x2c, 0x1d, 0x00, 0x06, 0x41, 0x82, 0x00, 0x8c, 0x2c, 0x1d, 0x00, 0x07, + 0x41, 0x82, 0x03, 0x30, 0x2c, 0x1d, 0x00, 0x08, 0x41, 0x82, 0x05, 0x80, 0x2c, 0x1d, 0x00, 0x09, + 0x41, 0x82, 0x00, 0xa0, 0x2c, 0x1d, 0x00, 0x10, 0x41, 0x82, 0x00, 0x98, 0x2c, 0x1d, 0x00, 0x2f, + 0x41, 0x82, 0x00, 0x70, 0x2c, 0x1d, 0x00, 0x30, 0x41, 0x82, 0x00, 0x78, 0x2c, 0x1d, 0x00, 0x38, + 0x41, 0x82, 0x05, 0x28, 0x2c, 0x1d, 0x00, 0x40, 0x41, 0x82, 0x03, 0x40, 0x2c, 0x1d, 0x00, 0x41, + 0x41, 0x82, 0x03, 0x58, 0x2c, 0x1d, 0x00, 0x44, 0x41, 0x82, 0x00, 0x68, 0x2c, 0x1d, 0x00, 0x50, + 0x41, 0x82, 0x00, 0x20, 0x2c, 0x1d, 0x00, 0x60, 0x41, 0x82, 0x00, 0x24, 0x2c, 0x1d, 0x00, 0x89, + 0x41, 0x82, 0x00, 0x50, 0x2c, 0x1d, 0x00, 0x99, 0x41, 0x82, 0x05, 0x0c, 0x48, 0x00, 0x05, 0x10, + 0x80, 0x72, 0x00, 0x00, 0x48, 0x00, 0x04, 0x29, 0x48, 0x00, 0x05, 0x04, 0x48, 0x00, 0x05, 0x89, + 0x48, 0x00, 0x04, 0xfc, 0x38, 0x80, 0x00, 0x01, 0x90, 0x92, 0x00, 0x00, 0x48, 0x00, 0x04, 0xf0, + 0x48, 0x00, 0x04, 0x09, 0x3a, 0x00, 0x00, 0xa0, 0x63, 0xec, 0x27, 0x98, 0x48, 0x00, 0x03, 0x14, + 0x38, 0x60, 0x01, 0x20, 0x63, 0xec, 0x27, 0x98, 0x48, 0x00, 0x03, 0xc9, 0x48, 0x00, 0x04, 0xd0, + 0x2f, 0x1d, 0x00, 0x10, 0x2e, 0x9d, 0x00, 0x44, 0x63, 0xe4, 0x1a, 0xb4, 0x3c, 0x60, 0x80, 0x00, + 0x60, 0x63, 0x03, 0x00, 0x48, 0x00, 0x05, 0x09, 0x38, 0x63, 0x0a, 0x00, 0x48, 0x00, 0x05, 0x01, + 0x38, 0x63, 0x06, 0x00, 0x48, 0x00, 0x04, 0xf9, 0x63, 0xec, 0x27, 0x88, 0x92, 0xac, 0x00, 0x00, + 0x92, 0xac, 0x00, 0x04, 0x92, 0xac, 0x00, 0x08, 0x63, 0xe4, 0x27, 0x98, 0x81, 0x24, 0x00, 0x18, + 0x80, 0x72, 0x00, 0x00, 0x2c, 0x03, 0x00, 0x02, 0x40, 0x82, 0x00, 0x0c, 0x41, 0x96, 0x00, 0x0c, + 0x48, 0x00, 0x00, 0x20, 0x38, 0x60, 0x00, 0x00, 0x90, 0x6c, 0x00, 0x0c, 0x40, 0x82, 0x00, 0x14, + 0x40, 0x96, 0x00, 0x10, 0x61, 0x29, 0x04, 0x00, 0x91, 0x24, 0x00, 0x18, 0x48, 0x00, 0x02, 0x14, + 0x55, 0x29, 0x05, 0xa8, 0x91, 0x24, 0x00, 0x18, 0x41, 0x96, 0x04, 0x54, 0x41, 0x9a, 0x00, 0x08, + 0x39, 0x8c, 0x00, 0x04, 0x38, 0x60, 0x00, 0x04, 0x48, 0x00, 0x03, 0x09, 0x40, 0x99, 0x00, 0x10, + 0x39, 0x8c, 0x00, 0x04, 0x38, 0x60, 0x00, 0x04, 0x48, 0x00, 0x02, 0xf9, 0x63, 0xe4, 0x27, 0x88, + 0x80, 0x64, 0x00, 0x00, 0x80, 0x84, 0x00, 0x04, 0x7c, 0x72, 0xfb, 0xa6, 0x7c, 0x95, 0xfb, 0xa6, + 0x48, 0x00, 0x04, 0x1c, 0x7c, 0x32, 0x43, 0xa6, 0x7c, 0x3a, 0x02, 0xa6, 0x7c, 0x73, 0x43, 0xa6, + 0x7c, 0x7b, 0x02, 0xa6, 0x54, 0x63, 0x05, 0xa8, 0x90, 0x60, 0x27, 0xb0, 0x54, 0x63, 0x06, 0x1e, + 0x60, 0x63, 0x20, 0x00, 0x7c, 0x7b, 0x03, 0xa6, 0x3c, 0x60, 0x80, 0x00, 0x60, 0x63, 0x1a, 0xe8, + 0x7c, 0x7a, 0x03, 0xa6, 0x4c, 0x00, 0x00, 0x64, 0x3c, 0x60, 0x80, 0x00, 0x60, 0x63, 0x27, 0x98, + 0x90, 0x23, 0x00, 0x14, 0x7c, 0x61, 0x1b, 0x78, 0x7c, 0x73, 0x42, 0xa6, 0xbc, 0x41, 0x00, 0x24, + 0x7c, 0x24, 0x0b, 0x78, 0x7c, 0x32, 0x42, 0xa6, 0x90, 0x04, 0x00, 0x1c, 0x90, 0x24, 0x00, 0x20, + 0x7c, 0x68, 0x02, 0xa6, 0x90, 0x64, 0x00, 0x9c, 0x7c, 0x60, 0x00, 0x26, 0x90, 0x64, 0x00, 0x00, + 0x7c, 0x61, 0x02, 0xa6, 0x90, 0x64, 0x00, 0x04, 0x7c, 0x69, 0x02, 0xa6, 0x90, 0x64, 0x00, 0x08, + 0x7c, 0x72, 0x02, 0xa6, 0x90, 0x64, 0x00, 0x0c, 0x7c, 0x73, 0x02, 0xa6, 0x90, 0x64, 0x00, 0x10, + 0x39, 0x20, 0x00, 0x00, 0x7d, 0x32, 0xfb, 0xa6, 0x7d, 0x35, 0xfb, 0xa6, 0x3c, 0xa0, 0x80, 0x00, + 0x60, 0xa5, 0x1b, 0x70, 0x3f, 0xe0, 0xd0, 0x04, 0x63, 0xff, 0x00, 0xa0, 0x93, 0xe5, 0x00, 0x00, + 0x7c, 0x00, 0x28, 0x6c, 0x7c, 0x00, 0x04, 0xac, 0x7c, 0x00, 0x2f, 0xac, 0x4c, 0x00, 0x01, 0x2c, + 0xd0, 0x04, 0x00, 0xa0, 0x3b, 0xff, 0x00, 0x04, 0x3f, 0xff, 0x00, 0x20, 0x57, 0xf0, 0x01, 0x4b, + 0x41, 0x82, 0xff, 0xdc, 0x3f, 0xe0, 0x80, 0x00, 0x63, 0xe5, 0x27, 0x88, 0x82, 0x05, 0x00, 0x00, + 0x82, 0x25, 0x00, 0x04, 0x82, 0x65, 0x00, 0x0c, 0x2c, 0x13, 0x00, 0x00, 0x41, 0x82, 0x00, 0x74, + 0x2c, 0x13, 0x00, 0x02, 0x40, 0x82, 0x00, 0x18, 0x81, 0x24, 0x00, 0x14, 0x39, 0x33, 0x00, 0x03, + 0x91, 0x25, 0x00, 0x00, 0x91, 0x25, 0x00, 0x0c, 0x48, 0x00, 0x00, 0x6c, 0x7c, 0x10, 0x98, 0x00, + 0x41, 0x82, 0x00, 0x38, 0x7c, 0x11, 0x98, 0x00, 0x41, 0x82, 0x00, 0x30, 0x7d, 0x30, 0x8a, 0x14, + 0x91, 0x25, 0x00, 0x0c, 0x82, 0x05, 0x00, 0x08, 0x2c, 0x10, 0x00, 0x00, 0x41, 0x82, 0x00, 0x48, + 0x80, 0x64, 0x00, 0x10, 0x7c, 0x10, 0x18, 0x00, 0x40, 0x82, 0x00, 0x10, 0x3a, 0x00, 0x00, 0x00, + 0x92, 0x05, 0x00, 0x08, 0x48, 0x00, 0x00, 0x30, 0x3a, 0x20, 0x00, 0x00, 0x92, 0x25, 0x00, 0x0c, + 0x81, 0x24, 0x00, 0x18, 0x61, 0x29, 0x04, 0x00, 0x91, 0x24, 0x00, 0x18, 0x48, 0x00, 0x00, 0x30, + 0x7e, 0x12, 0xfb, 0xa6, 0x7e, 0x35, 0xfb, 0xa6, 0x39, 0x20, 0x00, 0x01, 0x91, 0x25, 0x00, 0x0c, + 0x48, 0x00, 0x00, 0x1c, 0x38, 0xa0, 0x00, 0x02, 0x63, 0xe4, 0x27, 0x74, 0x90, 0xa4, 0x00, 0x00, + 0x38, 0x60, 0x00, 0x11, 0x48, 0x00, 0x01, 0xb9, 0x4b, 0xff, 0xfc, 0x71, 0x7c, 0x20, 0x00, 0xa6, + 0x54, 0x21, 0x07, 0xfa, 0x54, 0x21, 0x04, 0x5e, 0x7c, 0x20, 0x01, 0x24, 0x63, 0xe1, 0x27, 0x98, + 0x80, 0x61, 0x00, 0x00, 0x7c, 0x6f, 0xf1, 0x20, 0x80, 0x61, 0x00, 0x14, 0x7c, 0x7a, 0x03, 0xa6, + 0x80, 0x61, 0x00, 0x18, 0x7c, 0x7b, 0x03, 0xa6, 0x80, 0x61, 0x00, 0x9c, 0x7c, 0x68, 0x03, 0xa6, + 0xb8, 0x41, 0x00, 0x24, 0x80, 0x01, 0x00, 0x1c, 0x80, 0x21, 0x00, 0x20, 0x4c, 0x00, 0x00, 0x64, + 0x92, 0xb2, 0x00, 0x00, 0x48, 0x00, 0x02, 0x54, 0x2e, 0x9d, 0x00, 0x02, 0x38, 0x60, 0x00, 0x08, + 0x63, 0xec, 0x27, 0x7c, 0x48, 0x00, 0x00, 0xfd, 0x80, 0xac, 0x00, 0x00, 0x80, 0x6c, 0x00, 0x04, + 0x98, 0x65, 0x00, 0x00, 0x41, 0x94, 0x00, 0x10, 0xb0, 0x65, 0x00, 0x00, 0x41, 0x96, 0x00, 0x08, + 0x90, 0x65, 0x00, 0x00, 0x7c, 0x00, 0x28, 0xac, 0x7c, 0x00, 0x04, 0xac, 0x7c, 0x00, 0x2f, 0xac, + 0x4c, 0x00, 0x01, 0x2c, 0x48, 0x00, 0x02, 0x08, 0x48, 0x00, 0x01, 0x21, 0x38, 0x60, 0x00, 0x04, + 0x63, 0xec, 0x27, 0x7c, 0x48, 0x00, 0x00, 0xbd, 0x82, 0x0c, 0x00, 0x00, 0x3d, 0x80, 0x80, 0x00, + 0x61, 0x8c, 0x28, 0xb8, 0x48, 0x00, 0x00, 0x1c, 0x48, 0x00, 0x01, 0x01, 0x38, 0x60, 0x00, 0x08, + 0x63, 0xec, 0x27, 0x7c, 0x48, 0x00, 0x00, 0x9d, 0x82, 0x0c, 0x00, 0x04, 0x81, 0x8c, 0x00, 0x00, + 0x63, 0xfb, 0x27, 0x84, 0x3a, 0x20, 0x0f, 0x80, 0x48, 0x00, 0x02, 0x39, 0x41, 0x82, 0x00, 0x20, + 0x7e, 0x23, 0x8b, 0x78, 0x48, 0x00, 0x00, 0x7d, 0x48, 0x00, 0x00, 0xd1, 0x41, 0x82, 0xff, 0xfc, + 0x7d, 0x8c, 0x72, 0x14, 0x35, 0x6b, 0xff, 0xff, 0x41, 0x81, 0xff, 0xe8, 0x80, 0x7b, 0x00, 0x00, + 0x2c, 0x03, 0x00, 0x00, 0x41, 0x82, 0x00, 0x08, 0x48, 0x00, 0x00, 0x59, 0x7c, 0x00, 0x60, 0xac, + 0x7c, 0x00, 0x04, 0xac, 0x7c, 0x00, 0x67, 0xac, 0x4c, 0x00, 0x01, 0x2c, 0x48, 0x00, 0x01, 0x80, + 0x7f, 0xc8, 0x02, 0xa6, 0x3c, 0x60, 0xa0, 0x00, 0x48, 0x00, 0x00, 0x15, 0x76, 0x03, 0x08, 0x00, + 0x56, 0x1d, 0x86, 0x3e, 0x7f, 0xc8, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, 0x92, 0xf8, 0x68, 0x14, + 0x90, 0x78, 0x68, 0x24, 0x92, 0xd8, 0x68, 0x20, 0x80, 0xb8, 0x68, 0x20, 0x70, 0xa5, 0x00, 0x01, + 0x40, 0x82, 0xff, 0xf8, 0x82, 0x18, 0x68, 0x24, 0x90, 0xb8, 0x68, 0x14, 0x4e, 0x80, 0x00, 0x20, + 0x7d, 0x48, 0x02, 0xa6, 0x7c, 0x69, 0x03, 0xa6, 0x39, 0xc0, 0x00, 0x00, 0x48, 0x00, 0x00, 0x79, + 0x48, 0x00, 0x00, 0x75, 0x4b, 0xff, 0xff, 0xad, 0x41, 0x82, 0xff, 0xf4, 0x7f, 0xae, 0x61, 0xae, + 0x39, 0xce, 0x00, 0x01, 0x42, 0x00, 0xff, 0xe8, 0x7d, 0x48, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, + 0x7d, 0x48, 0x02, 0xa6, 0x7c, 0x69, 0x03, 0xa6, 0x39, 0xc0, 0x00, 0x00, 0x7c, 0x6c, 0x70, 0xae, + 0x48, 0x00, 0x00, 0x1d, 0x41, 0x82, 0xff, 0xf8, 0x39, 0xce, 0x00, 0x01, 0x42, 0x00, 0xff, 0xf0, + 0x7d, 0x48, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, 0x38, 0x60, 0x00, 0xaa, 0x7f, 0xc8, 0x02, 0xa6, + 0x54, 0x63, 0xa0, 0x16, 0x64, 0x63, 0xb0, 0x00, 0x3a, 0xc0, 0x00, 0x19, 0x3a, 0xe0, 0x00, 0xd0, + 0x3f, 0x00, 0xcc, 0x00, 0x4b, 0xff, 0xff, 0x69, 0x56, 0x03, 0x37, 0xff, 0x7f, 0xc8, 0x03, 0xa6, + 0x4e, 0x80, 0x00, 0x20, 0x7f, 0xc8, 0x02, 0xa6, 0x3c, 0x60, 0xd0, 0x00, 0x4b, 0xff, 0xff, 0x51, + 0x56, 0x03, 0x37, 0xff, 0x41, 0x82, 0xff, 0xf4, 0x7f, 0xc8, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, + 0x4b, 0xff, 0xff, 0xb9, 0x38, 0x60, 0x00, 0x08, 0x63, 0xec, 0x27, 0x7c, 0x4b, 0xff, 0xff, 0x55, + 0x80, 0xac, 0x00, 0x04, 0x81, 0x8c, 0x00, 0x00, 0x63, 0xfb, 0x27, 0x84, 0x62, 0xb1, 0xf8, 0x00, + 0x7e, 0x0c, 0x28, 0x50, 0x48, 0x00, 0x00, 0xed, 0x41, 0x81, 0x00, 0x10, 0x82, 0x3b, 0x00, 0x00, + 0x2c, 0x11, 0x00, 0x00, 0x41, 0x82, 0x00, 0x68, 0x7e, 0x23, 0x8b, 0x78, 0x4b, 0xff, 0xff, 0x55, + 0x4b, 0xff, 0xff, 0xa5, 0x4b, 0xff, 0xff, 0xa1, 0x4b, 0xff, 0xfe, 0xd9, 0x41, 0x82, 0xff, 0xf4, + 0x2c, 0x1d, 0x00, 0xcc, 0x41, 0x82, 0x00, 0x48, 0x2c, 0x1d, 0x00, 0xbb, 0x41, 0x82, 0xff, 0xdc, + 0x2c, 0x1d, 0x00, 0xaa, 0x40, 0x82, 0xff, 0xdc, 0x7d, 0x8c, 0x72, 0x14, 0x35, 0x6b, 0xff, 0xff, + 0x41, 0x80, 0x00, 0x2c, 0x4b, 0xff, 0xff, 0xb4, 0x7e, 0xb5, 0xfb, 0xa6, 0x7e, 0xb2, 0xfb, 0xa6, + 0x63, 0xe4, 0x27, 0x98, 0x81, 0x24, 0x00, 0x18, 0x55, 0x29, 0x05, 0xa8, 0x91, 0x24, 0x00, 0x18, + 0x48, 0x00, 0x00, 0x0c, 0x38, 0x60, 0x00, 0x80, 0x4b, 0xff, 0xff, 0x25, 0x80, 0x92, 0x00, 0x00, + 0x2c, 0x04, 0x00, 0x00, 0x40, 0x82, 0xfa, 0x50, 0xb3, 0x94, 0x40, 0x10, 0xc8, 0x41, 0x00, 0x98, + 0xc8, 0x61, 0x00, 0xa0, 0x7f, 0x20, 0x00, 0xa6, 0x80, 0x01, 0x00, 0xac, 0x7c, 0x08, 0x03, 0xa6, + 0x80, 0x01, 0x00, 0x0c, 0x7c, 0x0f, 0xf1, 0x20, 0x80, 0x01, 0x00, 0x10, 0x7c, 0x09, 0x03, 0xa6, + 0x80, 0x01, 0x00, 0x14, 0x7c, 0x01, 0x03, 0xa6, 0xb8, 0x61, 0x00, 0x18, 0x80, 0x01, 0x00, 0x08, + 0x38, 0x21, 0x00, 0xa8, 0x4c, 0x00, 0x01, 0x2c, 0x4e, 0x80, 0x00, 0x20, 0x7e, 0x23, 0x20, 0x50, + 0x3c, 0xa0, 0x48, 0x00, 0x52, 0x25, 0x01, 0xba, 0x90, 0xa3, 0x00, 0x00, 0x7c, 0x00, 0x18, 0xac, + 0x7c, 0x00, 0x04, 0xac, 0x7c, 0x00, 0x1f, 0xac, 0x4c, 0x00, 0x01, 0x2c, 0x4e, 0x80, 0x00, 0x20, + 0x7d, 0x70, 0x8b, 0xd7, 0x7d, 0x4b, 0x89, 0xd6, 0x7d, 0x4a, 0x80, 0x50, 0x91, 0x5b, 0x00, 0x00, + 0x4e, 0x80, 0x00, 0x20, 0x7f, 0xa8, 0x02, 0xa6, 0x3d, 0xe0, 0x80, 0x00, 0x61, 0xef, 0x28, 0xb8, + 0x63, 0xe7, 0x18, 0x08, 0x3c, 0xc0, 0x80, 0x00, 0x7c, 0xd0, 0x33, 0x78, 0x39, 0x00, 0x00, 0x00, + 0x3c, 0x60, 0x00, 0xd0, 0x60, 0x63, 0xc0, 0xde, 0x80, 0x8f, 0x00, 0x00, 0x7c, 0x03, 0x20, 0x00, + 0x40, 0x82, 0x00, 0x18, 0x80, 0x8f, 0x00, 0x04, 0x7c, 0x03, 0x20, 0x00, 0x40, 0x82, 0x00, 0x0c, + 0x39, 0xef, 0x00, 0x08, 0x48, 0x00, 0x00, 0x0c, 0x7f, 0xa8, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, + 0x80, 0x6f, 0x00, 0x00, 0x80, 0x8f, 0x00, 0x04, 0x39, 0xef, 0x00, 0x08, 0x71, 0x09, 0x00, 0x01, + 0x2f, 0x89, 0x00, 0x00, 0x39, 0x20, 0x00, 0x00, 0x54, 0x6a, 0x1f, 0x7e, 0x54, 0x65, 0x3f, 0x7e, + 0x74, 0x6b, 0x10, 0x00, 0x54, 0x63, 0x01, 0xfe, 0x40, 0x82, 0x00, 0x0c, 0x54, 0xcc, 0x00, 0x0c, + 0x48, 0x00, 0x00, 0x08, 0x7e, 0x0c, 0x83, 0x78, 0x2e, 0x05, 0x00, 0x00, 0x2c, 0x0a, 0x00, 0x01, + 0x41, 0xa0, 0x00, 0x2c, 0x41, 0xa2, 0x00, 0xe4, 0x2c, 0x0a, 0x00, 0x03, 0x41, 0xa0, 0x01, 0xac, + 0x41, 0x82, 0x02, 0x50, 0x2c, 0x0a, 0x00, 0x05, 0x41, 0x80, 0x02, 0xd4, 0x41, 0xa2, 0x04, 0xe0, + 0x2c, 0x0a, 0x00, 0x07, 0x41, 0xa0, 0x05, 0x0c, 0x48, 0x00, 0x05, 0xf0, 0x7d, 0x8c, 0x1a, 0x14, + 0x2c, 0x05, 0x00, 0x03, 0x41, 0x82, 0x00, 0x48, 0x41, 0x81, 0x00, 0x60, 0x40, 0xbe, 0xff, 0x84, + 0x2e, 0x05, 0x00, 0x01, 0x41, 0x91, 0x00, 0x2c, 0x54, 0x8a, 0x84, 0x3e, 0x41, 0x92, 0x00, 0x10, + 0x7c, 0x89, 0x61, 0xae, 0x39, 0x29, 0x00, 0x01, 0x48, 0x00, 0x00, 0x0c, 0x7c, 0x89, 0x63, 0x2e, + 0x39, 0x29, 0x00, 0x02, 0x35, 0x4a, 0xff, 0xff, 0x40, 0xa0, 0xff, 0xe4, 0x4b, 0xff, 0xff, 0x54, + 0x55, 0x8c, 0x00, 0x3a, 0x90, 0x8c, 0x00, 0x00, 0x4b, 0xff, 0xff, 0x48, 0x7c, 0x89, 0x23, 0x78, + 0x40, 0x9e, 0x04, 0xc8, 0x35, 0x29, 0xff, 0xff, 0x41, 0x80, 0x04, 0xc0, 0x7c, 0xa9, 0x78, 0xae, + 0x7c, 0xa9, 0x61, 0xae, 0x4b, 0xff, 0xff, 0xf0, 0x39, 0xef, 0x00, 0x08, 0x40, 0xbe, 0xff, 0x24, + 0x80, 0xaf, 0xff, 0xf8, 0x81, 0x6f, 0xff, 0xfc, 0x54, 0xb1, 0x04, 0x3e, 0x54, 0xaa, 0x85, 0x3e, + 0x54, 0xa5, 0x27, 0x3e, 0x2e, 0x85, 0x00, 0x01, 0x41, 0x96, 0x00, 0x10, 0x41, 0xb5, 0x00, 0x14, + 0x7c, 0x89, 0x61, 0xae, 0x48, 0x00, 0x00, 0x10, 0x7c, 0x89, 0x63, 0x2e, 0x48, 0x00, 0x00, 0x08, + 0x7c, 0x89, 0x61, 0x2e, 0x7c, 0x84, 0x5a, 0x14, 0x7d, 0x29, 0x8a, 0x14, 0x35, 0x4a, 0xff, 0xff, + 0x40, 0x80, 0xff, 0xd4, 0x4b, 0xff, 0xfe, 0xdc, 0x54, 0x69, 0x07, 0xff, 0x41, 0x82, 0x00, 0x10, + 0x55, 0x08, 0xf8, 0x7e, 0x71, 0x09, 0x00, 0x01, 0x2f, 0x89, 0x00, 0x00, 0x2e, 0x85, 0x00, 0x04, + 0x2d, 0x8a, 0x00, 0x05, 0x51, 0x08, 0x08, 0x3c, 0x40, 0x9e, 0x00, 0x78, 0x41, 0x8d, 0x04, 0xb8, + 0x7d, 0x8c, 0x1a, 0x14, 0x41, 0x8c, 0x00, 0x0c, 0x41, 0x94, 0x00, 0x30, 0x48, 0x00, 0x00, 0x1c, + 0x40, 0x94, 0x00, 0x10, 0x55, 0x8c, 0x00, 0x3a, 0x81, 0x6c, 0x00, 0x00, 0x48, 0x00, 0x00, 0x1c, + 0x55, 0x8c, 0x00, 0x3c, 0xa1, 0x6c, 0x00, 0x00, 0x7c, 0x89, 0x20, 0xf8, 0x55, 0x29, 0x84, 0x3e, + 0x7d, 0x6b, 0x48, 0x38, 0x54, 0x84, 0x04, 0x3e, 0x7f, 0x0b, 0x20, 0x40, 0x70, 0xa9, 0x00, 0x03, + 0x41, 0x82, 0x00, 0x18, 0x2c, 0x09, 0x00, 0x02, 0x41, 0x82, 0x00, 0x18, 0x41, 0x81, 0x00, 0x1c, + 0x40, 0x9a, 0x00, 0x20, 0x48, 0x00, 0x00, 0x18, 0x41, 0x9a, 0x00, 0x18, 0x48, 0x00, 0x00, 0x10, + 0x41, 0x99, 0x00, 0x10, 0x48, 0x00, 0x00, 0x08, 0x41, 0x98, 0x00, 0x08, 0x61, 0x08, 0x00, 0x01, + 0x40, 0x8e, 0xfe, 0x40, 0x41, 0x94, 0xfe, 0x3c, 0x81, 0x6f, 0xff, 0xf8, 0x40, 0x9e, 0x00, 0x20, + 0x70, 0x6c, 0x00, 0x08, 0x41, 0x82, 0x00, 0x0c, 0x71, 0x0c, 0x00, 0x01, 0x41, 0x82, 0x00, 0x10, + 0x39, 0x8b, 0x00, 0x10, 0x51, 0x8b, 0x03, 0x36, 0x48, 0x00, 0x00, 0x08, 0x55, 0x6b, 0x07, 0x16, + 0x91, 0x6f, 0xff, 0xf8, 0x4b, 0xff, 0xfe, 0x0c, 0x40, 0xbe, 0xfe, 0x08, 0x54, 0x69, 0x16, 0xba, + 0x54, 0x6e, 0x87, 0xfe, 0x2d, 0x8e, 0x00, 0x00, 0x2e, 0x05, 0x00, 0x04, 0x70, 0xae, 0x00, 0x03, + 0x2e, 0x8e, 0x00, 0x02, 0x41, 0x94, 0x00, 0x14, 0x41, 0x96, 0x00, 0x50, 0x7c, 0x64, 0x07, 0x34, + 0x7c, 0x84, 0x7a, 0x14, 0x48, 0x00, 0x00, 0x68, 0x54, 0x65, 0xa7, 0xff, 0x41, 0x82, 0x00, 0x0c, + 0x7d, 0x27, 0x48, 0x2e, 0x7c, 0x84, 0x4a, 0x14, 0x41, 0x8e, 0x00, 0x08, 0x7c, 0x8c, 0x22, 0x14, + 0x2e, 0x8e, 0x00, 0x01, 0x41, 0x96, 0x00, 0x08, 0x80, 0x84, 0x00, 0x00, 0x54, 0x63, 0x67, 0xff, + 0x41, 0x82, 0x00, 0x3c, 0x40, 0x90, 0x00, 0x0c, 0x7c, 0x84, 0x32, 0x14, 0x48, 0x00, 0x00, 0x30, + 0x7c, 0x84, 0x82, 0x14, 0x48, 0x00, 0x00, 0x28, 0x54, 0x65, 0xa7, 0xff, 0x41, 0x82, 0x00, 0x0c, + 0x7d, 0x27, 0x48, 0x2e, 0x7c, 0x84, 0x4a, 0x14, 0x40, 0x90, 0x00, 0x0c, 0x7c, 0xcc, 0x21, 0x2e, + 0x4b, 0xff, 0xfd, 0x80, 0x7e, 0x0c, 0x21, 0x2e, 0x4b, 0xff, 0xfd, 0x78, 0x40, 0x90, 0x00, 0x0c, + 0x7c, 0x86, 0x23, 0x78, 0x4b, 0xff, 0xfd, 0x6c, 0x7c, 0x90, 0x23, 0x78, 0x4b, 0xff, 0xfd, 0x64, + 0x54, 0x89, 0x1e, 0x78, 0x39, 0x29, 0x00, 0x40, 0x2c, 0x05, 0x00, 0x02, 0x41, 0x80, 0x00, 0x48, + 0x54, 0x6b, 0x50, 0x03, 0x41, 0x82, 0x00, 0x14, 0x41, 0x81, 0x00, 0x08, 0x48, 0x00, 0x00, 0x10, + 0x41, 0xbe, 0xfd, 0x40, 0x48, 0x00, 0x00, 0x08, 0x40, 0xbe, 0xfd, 0x38, 0x2c, 0x05, 0x00, 0x03, + 0x41, 0x81, 0x00, 0x10, 0x41, 0xa2, 0x00, 0x10, 0x7d, 0xe7, 0x48, 0x2e, 0x4b, 0xff, 0xfd, 0x24, + 0x7d, 0xe7, 0x49, 0x2e, 0x7c, 0x64, 0x07, 0x34, 0x54, 0x84, 0x1a, 0x78, 0x7d, 0xef, 0x22, 0x14, + 0x4b, 0xff, 0xfd, 0x10, 0x40, 0xbe, 0xfd, 0x0c, 0x7c, 0xa7, 0x4a, 0x14, 0x40, 0x92, 0x00, 0x14, + 0x54, 0x64, 0x04, 0x3e, 0x91, 0xe5, 0x00, 0x00, 0x90, 0x85, 0x00, 0x04, 0x4b, 0xff, 0xfc, 0xf4, + 0x81, 0x25, 0x00, 0x04, 0x2c, 0x09, 0x00, 0x00, 0x41, 0xa2, 0xfc, 0xe8, 0x39, 0x29, 0xff, 0xff, + 0x91, 0x25, 0x00, 0x04, 0x81, 0xe5, 0x00, 0x00, 0x4b, 0xff, 0xfc, 0xd8, 0x40, 0xbe, 0xfc, 0xd4, + 0x54, 0x6b, 0x16, 0xba, 0x7f, 0x47, 0x5a, 0x14, 0x81, 0x3a, 0x00, 0x00, 0x54, 0x6e, 0x67, 0xbe, + 0x41, 0x92, 0x00, 0x84, 0x2e, 0x05, 0x00, 0x05, 0x40, 0x90, 0x01, 0x74, 0x2e, 0x05, 0x00, 0x03, + 0x40, 0x90, 0x00, 0x90, 0x2e, 0x05, 0x00, 0x01, 0x54, 0x65, 0x87, 0xff, 0x41, 0x82, 0x00, 0x08, + 0x7c, 0x8c, 0x22, 0x14, 0x2f, 0x0e, 0x00, 0x01, 0x40, 0x92, 0x00, 0x24, 0x41, 0xb9, 0x00, 0x18, + 0x41, 0x9a, 0x00, 0x0c, 0x88, 0x84, 0x00, 0x00, 0x48, 0x00, 0x00, 0xf8, 0xa0, 0x84, 0x00, 0x00, + 0x48, 0x00, 0x00, 0xf0, 0x80, 0x84, 0x00, 0x00, 0x48, 0x00, 0x00, 0xe8, 0x54, 0x73, 0xe5, 0x3e, + 0x41, 0xb9, 0x00, 0x20, 0x41, 0x9a, 0x00, 0x10, 0x99, 0x24, 0x00, 0x00, 0x38, 0x84, 0x00, 0x01, + 0x48, 0x00, 0x00, 0x18, 0xb1, 0x24, 0x00, 0x00, 0x38, 0x84, 0x00, 0x02, 0x48, 0x00, 0x00, 0x0c, + 0x91, 0x24, 0x00, 0x00, 0x38, 0x84, 0x00, 0x04, 0x36, 0x73, 0xff, 0xff, 0x40, 0x80, 0xff, 0xd4, + 0x4b, 0xff, 0xfc, 0x40, 0x54, 0x65, 0x87, 0xff, 0x41, 0x82, 0x00, 0x08, 0x7c, 0x84, 0x62, 0x14, + 0x71, 0xc5, 0x00, 0x01, 0x41, 0x82, 0x00, 0x9c, 0x7c, 0x84, 0x4a, 0x14, 0x48, 0x00, 0x00, 0x94, + 0x54, 0x6a, 0x87, 0xbe, 0x54, 0x8e, 0x16, 0xba, 0x7e, 0x67, 0x72, 0x14, 0x40, 0x92, 0x00, 0x08, + 0x3a, 0x6f, 0xff, 0xfc, 0x80, 0x9a, 0x00, 0x00, 0x81, 0x33, 0x00, 0x00, 0x71, 0x4b, 0x00, 0x01, + 0x41, 0x82, 0x00, 0x08, 0x7c, 0x9a, 0x23, 0x78, 0x71, 0x4b, 0x00, 0x02, 0x41, 0x82, 0x00, 0x10, + 0x7d, 0x33, 0x4b, 0x78, 0x40, 0xb2, 0x00, 0x08, 0x7e, 0x6c, 0x9a, 0x14, 0x54, 0x65, 0x67, 0x3f, + 0x2c, 0x05, 0x00, 0x09, 0x40, 0x80, 0x00, 0x54, 0x48, 0x00, 0x00, 0x79, 0x7c, 0x89, 0x22, 0x14, + 0x48, 0x00, 0x00, 0x40, 0x7c, 0x89, 0x21, 0xd6, 0x48, 0x00, 0x00, 0x38, 0x7d, 0x24, 0x23, 0x78, + 0x48, 0x00, 0x00, 0x30, 0x7d, 0x24, 0x20, 0x38, 0x48, 0x00, 0x00, 0x28, 0x7d, 0x24, 0x22, 0x78, + 0x48, 0x00, 0x00, 0x20, 0x7d, 0x24, 0x20, 0x30, 0x48, 0x00, 0x00, 0x18, 0x7d, 0x24, 0x24, 0x30, + 0x48, 0x00, 0x00, 0x10, 0x5d, 0x24, 0x20, 0x3e, 0x48, 0x00, 0x00, 0x08, 0x7d, 0x24, 0x26, 0x30, + 0x90, 0x9a, 0x00, 0x00, 0x4b, 0xff, 0xfb, 0x8c, 0x2c, 0x05, 0x00, 0x0a, 0x41, 0x81, 0xfb, 0x84, + 0xc0, 0x5a, 0x00, 0x00, 0xc0, 0x73, 0x00, 0x00, 0x41, 0x82, 0x00, 0x0c, 0xec, 0x43, 0x10, 0x2a, + 0x48, 0x00, 0x00, 0x08, 0xec, 0x43, 0x00, 0xb2, 0xd0, 0x5a, 0x00, 0x00, 0x4b, 0xff, 0xfb, 0x64, + 0x7d, 0x48, 0x02, 0xa6, 0x54, 0xa5, 0x1e, 0x78, 0x7d, 0x4a, 0x2a, 0x14, 0x80, 0x9a, 0x00, 0x00, + 0x81, 0x33, 0x00, 0x00, 0x7d, 0x48, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, 0x40, 0xbe, 0xfb, 0x44, + 0x54, 0x69, 0xc0, 0x3e, 0x7d, 0x8e, 0x63, 0x78, 0x48, 0x00, 0x00, 0x35, 0x41, 0x92, 0x00, 0x0c, + 0x7e, 0x31, 0x22, 0x14, 0x48, 0x00, 0x00, 0x08, 0x7d, 0x29, 0x22, 0x14, 0x54, 0x64, 0xc4, 0x3f, + 0x38, 0xa0, 0x00, 0x00, 0x41, 0x82, 0xfb, 0x1c, 0x7d, 0x45, 0x88, 0xae, 0x7d, 0x45, 0x49, 0xae, + 0x38, 0xa5, 0x00, 0x01, 0x7c, 0x05, 0x20, 0x00, 0x4b, 0xff, 0xff, 0xec, 0x2e, 0x8a, 0x00, 0x04, + 0x55, 0x31, 0x36, 0xba, 0x2c, 0x11, 0x00, 0x3c, 0x7e, 0x27, 0x88, 0x2e, 0x40, 0x82, 0x00, 0x08, + 0x7d, 0xd1, 0x73, 0x78, 0x41, 0x96, 0x00, 0x08, 0xa2, 0x31, 0x00, 0x00, 0x55, 0x29, 0x56, 0xba, + 0x2c, 0x09, 0x00, 0x3c, 0x7d, 0x27, 0x48, 0x2e, 0x40, 0x82, 0x00, 0x08, 0x7d, 0xc9, 0x73, 0x78, + 0x41, 0x96, 0x00, 0x08, 0xa1, 0x29, 0x00, 0x00, 0x4e, 0x80, 0x00, 0x20, 0x2c, 0x05, 0x00, 0x04, + 0x40, 0x80, 0x00, 0x28, 0x7c, 0x89, 0x23, 0x78, 0x7d, 0xc3, 0x62, 0x14, 0x55, 0xce, 0x00, 0x3c, + 0x4b, 0xff, 0xff, 0xad, 0x7c, 0x84, 0x20, 0xf8, 0x54, 0x84, 0x04, 0x3e, 0x7d, 0x2b, 0x20, 0x38, + 0x7e, 0x24, 0x20, 0x38, 0x4b, 0xff, 0xfb, 0xc4, 0x54, 0x6b, 0xe4, 0x3e, 0x4b, 0xff, 0xfb, 0xbc, + 0x7c, 0x9a, 0x23, 0x78, 0x54, 0x84, 0x18, 0x38, 0x40, 0x92, 0x00, 0x20, 0x40, 0x9e, 0x00, 0x0c, + 0x7d, 0xe8, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x21, 0x7d, 0xe4, 0x7a, 0x14, 0x39, 0xef, 0x00, 0x07, + 0x55, 0xef, 0x00, 0x38, 0x4b, 0xff, 0xfa, 0x6c, 0x2e, 0x05, 0x00, 0x03, 0x41, 0x91, 0x00, 0x5c, + 0x3c, 0xa0, 0x48, 0x00, 0x7d, 0x83, 0x62, 0x14, 0x55, 0x8c, 0x00, 0x3a, 0x40, 0x92, 0x00, 0x20, + 0x40, 0xbe, 0xfa, 0x50, 0x57, 0x44, 0x00, 0x3a, 0x7c, 0x8c, 0x20, 0x50, 0x50, 0x85, 0x01, 0xba, + 0x50, 0x65, 0x07, 0xfe, 0x90, 0xac, 0x00, 0x00, 0x4b, 0xff, 0xfa, 0x38, 0x40, 0xbe, 0xff, 0xbc, + 0x7d, 0x2c, 0x78, 0x50, 0x51, 0x25, 0x01, 0xba, 0x90, 0xac, 0x00, 0x00, 0x39, 0x8c, 0x00, 0x04, + 0x7d, 0x6f, 0x22, 0x14, 0x39, 0x6b, 0xff, 0xfc, 0x7d, 0x2b, 0x60, 0x50, 0x51, 0x25, 0x01, 0xba, + 0x90, 0xab, 0x00, 0x00, 0x4b, 0xff, 0xff, 0x94, 0x2e, 0x05, 0x00, 0x06, 0x41, 0x92, 0x00, 0x28, + 0x4b, 0xff, 0xfb, 0x28, 0x55, 0x8c, 0x84, 0x3e, 0x57, 0x44, 0x84, 0x3e, 0x57, 0x5a, 0x04, 0x3e, + 0x7c, 0x0c, 0x20, 0x00, 0x41, 0x80, 0xfb, 0xa8, 0x7c, 0x0c, 0xd0, 0x00, 0x40, 0x80, 0xfb, 0xa0, + 0x4b, 0xff, 0xf9, 0xe0, 0x57, 0x45, 0xff, 0xfe, 0x68, 0xa5, 0x00, 0x01, 0x71, 0x03, 0x00, 0x01, + 0x7c, 0x05, 0x18, 0x00, 0x41, 0x82, 0x00, 0x1c, 0x51, 0x1a, 0x0f, 0xbc, 0x6b, 0x5a, 0x00, 0x02, + 0x57, 0x45, 0xff, 0xff, 0x41, 0x82, 0x00, 0x08, 0x6b, 0x5a, 0x00, 0x01, 0x93, 0x4f, 0xff, 0xfc, + 0x53, 0x48, 0x07, 0xfe, 0x4b, 0xff, 0xf9, 0xac, 0x2c, 0x0b, 0x00, 0x00, 0x41, 0x82, 0x01, 0x38, + 0x2c, 0x05, 0x00, 0x01, 0x41, 0x82, 0x00, 0x18, 0x2c, 0x05, 0x00, 0x02, 0x41, 0x82, 0x00, 0x14, + 0x2c, 0x05, 0x00, 0x03, 0x41, 0x82, 0x00, 0x70, 0x4b, 0xff, 0xf9, 0x80, 0x54, 0xcc, 0x00, 0x0c, + 0x54, 0x97, 0x46, 0x3e, 0x54, 0x98, 0xc4, 0x3e, 0x54, 0x84, 0x06, 0x3e, 0x40, 0x9e, 0x00, 0xfc, + 0x56, 0xf9, 0x06, 0x31, 0x7d, 0x9a, 0x63, 0x78, 0x7f, 0x43, 0xd2, 0x14, 0x57, 0x5a, 0x00, 0x3a, + 0x41, 0x82, 0x00, 0x18, 0x7e, 0xf7, 0x07, 0x74, 0x7e, 0xf7, 0x00, 0xd0, 0x1f, 0x37, 0x00, 0x02, + 0x3b, 0x39, 0x00, 0x04, 0x7f, 0x59, 0xd0, 0x50, 0x2c, 0x17, 0x00, 0x00, 0x41, 0x82, 0x00, 0x1c, + 0x3b, 0x20, 0x00, 0x00, 0x7e, 0xe9, 0x03, 0xa6, 0xa3, 0x7a, 0x00, 0x04, 0x7f, 0x79, 0xca, 0x78, + 0x3b, 0x5a, 0x00, 0x02, 0x42, 0x00, 0xff, 0xf4, 0x7c, 0x18, 0xc8, 0x00, 0x40, 0x82, 0x00, 0xac, + 0x4b, 0xff, 0xfe, 0x90, 0x51, 0x08, 0x08, 0x3c, 0x40, 0x9e, 0x00, 0x9c, 0x54, 0x77, 0xb0, 0x03, + 0x41, 0x81, 0x00, 0x88, 0x41, 0x80, 0x00, 0x8c, 0x54, 0x7e, 0x06, 0x3e, 0x1f, 0xde, 0x00, 0x02, + 0x54, 0x97, 0x00, 0x1e, 0x6e, 0xf8, 0x80, 0x00, 0x2c, 0x18, 0x00, 0x00, 0x40, 0x82, 0x00, 0x08, + 0x62, 0xf7, 0x30, 0x00, 0x54, 0x98, 0x80, 0x1e, 0x1f, 0x3e, 0x00, 0x04, 0x7f, 0x19, 0xc0, 0x50, + 0x3b, 0x20, 0x00, 0x00, 0x1f, 0x59, 0x00, 0x04, 0x7f, 0x6f, 0xd0, 0x2e, 0x7f, 0x57, 0xd0, 0x2e, + 0x3b, 0x39, 0x00, 0x01, 0x7c, 0x17, 0xc0, 0x40, 0x41, 0x81, 0x00, 0x34, 0x7c, 0x19, 0xf0, 0x00, + 0x41, 0x81, 0x00, 0x14, 0x7c, 0x1a, 0xd8, 0x00, 0x41, 0x82, 0xff, 0xdc, 0x3a, 0xf7, 0x00, 0x04, + 0x4b, 0xff, 0xff, 0xd0, 0x80, 0x6f, 0xff, 0xf8, 0x60, 0x63, 0x03, 0x00, 0x90, 0x6f, 0xff, 0xf8, + 0x92, 0xef, 0xff, 0xfc, 0x7e, 0xf0, 0xbb, 0x78, 0x48, 0x00, 0x00, 0x1c, 0x80, 0x6f, 0xff, 0xf8, + 0x60, 0x63, 0x01, 0x00, 0x90, 0x6f, 0xff, 0xf8, 0x61, 0x08, 0x00, 0x01, 0x48, 0x00, 0x00, 0x08, + 0x7c, 0x90, 0x23, 0x78, 0x54, 0x64, 0x06, 0x3e, 0x1c, 0x84, 0x00, 0x08, 0x7d, 0xe4, 0x7a, 0x14, + 0x4b, 0xff, 0xf8, 0x70, 0x40, 0x92, 0x00, 0x0c, 0x39, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x14, + 0x54, 0x69, 0x06, 0xff, 0x54, 0x65, 0x67, 0xfe, 0x7d, 0x08, 0x4c, 0x30, 0x55, 0x17, 0xff, 0xff, + 0x40, 0x82, 0x00, 0x08, 0x7d, 0x08, 0x2a, 0x78, 0x54, 0x85, 0x00, 0x1f, 0x41, 0x82, 0x00, 0x08, + 0x7c, 0xa6, 0x2b, 0x78, 0x54, 0x85, 0x80, 0x1f, 0x41, 0x82, 0x00, 0x08, 0x7c, 0xb0, 0x2b, 0x78, + 0x4b, 0xff, 0xf8, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +//unsigned char kenobigc[2736] = +//{ +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x21, 0x60, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x21, 0xff, 0x58, 0x90, 0x01, 0x00, 0x08, +// 0x7c, 0x08, 0x02, 0xa6, 0x90, 0x01, 0x00, 0xac, 0x7c, 0x00, 0x00, 0x26, 0x90, 0x01, 0x00, 0x0c, +// 0x7c, 0x09, 0x02, 0xa6, 0x90, 0x01, 0x00, 0x10, 0x7c, 0x01, 0x02, 0xa6, 0x90, 0x01, 0x00, 0x14, +// 0xbc, 0x61, 0x00, 0x18, 0x7f, 0x20, 0x00, 0xa6, 0x63, 0x3a, 0x20, 0x00, 0x73, 0x5a, 0xf9, 0xff, +// 0x7f, 0x40, 0x01, 0x24, 0xd8, 0x41, 0x00, 0x98, 0xd8, 0x61, 0x00, 0xa0, 0x3f, 0xe0, 0x80, 0x00, +// 0x3e, 0x80, 0xcc, 0x00, 0xa3, 0x94, 0x40, 0x10, 0x63, 0x95, 0x00, 0xff, 0xb2, 0xb4, 0x40, 0x10, +// 0x7f, 0xa8, 0x02, 0xa6, 0x3d, 0xe0, 0x80, 0x00, 0x61, 0xef, 0x22, 0xa8, 0x63, 0xe7, 0x18, 0x08, +// 0x3c, 0xc0, 0x80, 0x00, 0x7c, 0xd0, 0x33, 0x78, 0x39, 0x00, 0x00, 0x00, 0x3c, 0x60, 0x00, 0xd0, +// 0x60, 0x63, 0xc0, 0xde, 0x80, 0x8f, 0x00, 0x00, 0x7c, 0x03, 0x20, 0x00, 0x40, 0x82, 0x00, 0x18, +// 0x80, 0x8f, 0x00, 0x04, 0x7c, 0x03, 0x20, 0x00, 0x40, 0x82, 0x00, 0x0c, 0x39, 0xef, 0x00, 0x08, +// 0x48, 0x00, 0x00, 0x4c, 0x7f, 0xa8, 0x03, 0xa6, 0xb3, 0x94, 0x40, 0x10, 0xc8, 0x41, 0x00, 0x98, +// 0xc8, 0x61, 0x00, 0xa0, 0x7f, 0x20, 0x00, 0xa6, 0x80, 0x01, 0x00, 0xac, 0x7c, 0x08, 0x03, 0xa6, +// 0x80, 0x01, 0x00, 0x0c, 0x7c, 0x0f, 0xf1, 0x20, 0x80, 0x01, 0x00, 0x10, 0x7c, 0x09, 0x03, 0xa6, +// 0x80, 0x01, 0x00, 0x14, 0x7c, 0x01, 0x03, 0xa6, 0xb8, 0x61, 0x00, 0x18, 0x80, 0x01, 0x00, 0x08, +// 0x38, 0x21, 0x00, 0xa8, 0x4c, 0x00, 0x01, 0x2c, 0x4e, 0x80, 0x00, 0x20, 0x80, 0x6f, 0x00, 0x00, +// 0x80, 0x8f, 0x00, 0x04, 0x39, 0xef, 0x00, 0x08, 0x71, 0x09, 0x00, 0x01, 0x2f, 0x89, 0x00, 0x00, +// 0x39, 0x20, 0x00, 0x00, 0x54, 0x6a, 0x1f, 0x7e, 0x54, 0x65, 0x3f, 0x7e, 0x74, 0x6b, 0x10, 0x00, +// 0x54, 0x63, 0x01, 0xfe, 0x40, 0x82, 0x00, 0x0c, 0x54, 0xcc, 0x00, 0x0c, 0x48, 0x00, 0x00, 0x08, +// 0x7e, 0x0c, 0x83, 0x78, 0x2e, 0x05, 0x00, 0x00, 0x2c, 0x0a, 0x00, 0x01, 0x41, 0xa0, 0x00, 0x2c, +// 0x41, 0xa2, 0x00, 0xe4, 0x2c, 0x0a, 0x00, 0x03, 0x41, 0xa0, 0x01, 0xac, 0x41, 0x82, 0x02, 0x50, +// 0x2c, 0x0a, 0x00, 0x05, 0x41, 0x80, 0x02, 0xd4, 0x41, 0xa2, 0x04, 0xe0, 0x2c, 0x0a, 0x00, 0x07, +// 0x41, 0xa0, 0x05, 0x0c, 0x48, 0x00, 0x05, 0xf0, 0x7d, 0x8c, 0x1a, 0x14, 0x2c, 0x05, 0x00, 0x03, +// 0x41, 0x82, 0x00, 0x48, 0x41, 0x81, 0x00, 0x60, 0x40, 0xbe, 0xff, 0x84, 0x2e, 0x05, 0x00, 0x01, +// 0x41, 0x91, 0x00, 0x2c, 0x54, 0x8a, 0x84, 0x3e, 0x41, 0x92, 0x00, 0x10, 0x7c, 0x89, 0x61, 0xae, +// 0x39, 0x29, 0x00, 0x01, 0x48, 0x00, 0x00, 0x0c, 0x7c, 0x89, 0x63, 0x2e, 0x39, 0x29, 0x00, 0x02, +// 0x35, 0x4a, 0xff, 0xff, 0x40, 0xa0, 0xff, 0xe4, 0x4b, 0xff, 0xff, 0x54, 0x55, 0x8c, 0x00, 0x3a, +// 0x90, 0x8c, 0x00, 0x00, 0x4b, 0xff, 0xff, 0x48, 0x7c, 0x89, 0x23, 0x78, 0x40, 0x9e, 0x04, 0xc8, +// 0x35, 0x29, 0xff, 0xff, 0x41, 0x80, 0x04, 0xc0, 0x7c, 0xa9, 0x78, 0xae, 0x7c, 0xa9, 0x61, 0xae, +// 0x4b, 0xff, 0xff, 0xf0, 0x39, 0xef, 0x00, 0x08, 0x40, 0xbe, 0xff, 0x24, 0x80, 0xaf, 0xff, 0xf8, +// 0x81, 0x6f, 0xff, 0xfc, 0x54, 0xb1, 0x04, 0x3e, 0x54, 0xaa, 0x85, 0x3e, 0x54, 0xa5, 0x27, 0x3e, +// 0x2e, 0x85, 0x00, 0x01, 0x41, 0x96, 0x00, 0x10, 0x41, 0xb5, 0x00, 0x14, 0x7c, 0x89, 0x61, 0xae, +// 0x48, 0x00, 0x00, 0x10, 0x7c, 0x89, 0x63, 0x2e, 0x48, 0x00, 0x00, 0x08, 0x7c, 0x89, 0x61, 0x2e, +// 0x7c, 0x84, 0x5a, 0x14, 0x7d, 0x29, 0x8a, 0x14, 0x35, 0x4a, 0xff, 0xff, 0x40, 0x80, 0xff, 0xd4, +// 0x4b, 0xff, 0xfe, 0xdc, 0x54, 0x69, 0x07, 0xff, 0x41, 0x82, 0x00, 0x10, 0x55, 0x08, 0xf8, 0x7e, +// 0x71, 0x09, 0x00, 0x01, 0x2f, 0x89, 0x00, 0x00, 0x2e, 0x85, 0x00, 0x04, 0x2d, 0x8a, 0x00, 0x05, +// 0x51, 0x08, 0x08, 0x3c, 0x40, 0x9e, 0x00, 0x78, 0x41, 0x8d, 0x04, 0xb8, 0x7d, 0x8c, 0x1a, 0x14, +// 0x41, 0x8c, 0x00, 0x0c, 0x41, 0x94, 0x00, 0x30, 0x48, 0x00, 0x00, 0x1c, 0x40, 0x94, 0x00, 0x10, +// 0x55, 0x8c, 0x00, 0x3a, 0x81, 0x6c, 0x00, 0x00, 0x48, 0x00, 0x00, 0x1c, 0x55, 0x8c, 0x00, 0x3c, +// 0xa1, 0x6c, 0x00, 0x00, 0x7c, 0x89, 0x20, 0xf8, 0x55, 0x29, 0x84, 0x3e, 0x7d, 0x6b, 0x48, 0x38, +// 0x54, 0x84, 0x04, 0x3e, 0x7f, 0x0b, 0x20, 0x40, 0x70, 0xa9, 0x00, 0x03, 0x41, 0x82, 0x00, 0x18, +// 0x2c, 0x09, 0x00, 0x02, 0x41, 0x82, 0x00, 0x18, 0x41, 0x81, 0x00, 0x1c, 0x40, 0x9a, 0x00, 0x20, +// 0x48, 0x00, 0x00, 0x18, 0x41, 0x9a, 0x00, 0x18, 0x48, 0x00, 0x00, 0x10, 0x41, 0x99, 0x00, 0x10, +// 0x48, 0x00, 0x00, 0x08, 0x41, 0x98, 0x00, 0x08, 0x61, 0x08, 0x00, 0x01, 0x40, 0x8e, 0xfe, 0x40, +// 0x41, 0x94, 0xfe, 0x3c, 0x81, 0x6f, 0xff, 0xf8, 0x40, 0x9e, 0x00, 0x20, 0x70, 0x6c, 0x00, 0x08, +// 0x41, 0x82, 0x00, 0x0c, 0x71, 0x0c, 0x00, 0x01, 0x41, 0x82, 0x00, 0x10, 0x39, 0x8b, 0x00, 0x10, +// 0x51, 0x8b, 0x03, 0x36, 0x48, 0x00, 0x00, 0x08, 0x55, 0x6b, 0x07, 0x16, 0x91, 0x6f, 0xff, 0xf8, +// 0x4b, 0xff, 0xfe, 0x0c, 0x40, 0xbe, 0xfe, 0x08, 0x54, 0x69, 0x16, 0xba, 0x54, 0x6e, 0x87, 0xfe, +// 0x2d, 0x8e, 0x00, 0x00, 0x2e, 0x05, 0x00, 0x04, 0x70, 0xae, 0x00, 0x03, 0x2e, 0x8e, 0x00, 0x02, +// 0x41, 0x94, 0x00, 0x14, 0x41, 0x96, 0x00, 0x50, 0x7c, 0x64, 0x07, 0x34, 0x7c, 0x84, 0x7a, 0x14, +// 0x48, 0x00, 0x00, 0x68, 0x54, 0x65, 0xa7, 0xff, 0x41, 0x82, 0x00, 0x0c, 0x7d, 0x27, 0x48, 0x2e, +// 0x7c, 0x84, 0x4a, 0x14, 0x41, 0x8e, 0x00, 0x08, 0x7c, 0x8c, 0x22, 0x14, 0x2e, 0x8e, 0x00, 0x01, +// 0x41, 0x96, 0x00, 0x08, 0x80, 0x84, 0x00, 0x00, 0x54, 0x63, 0x67, 0xff, 0x41, 0x82, 0x00, 0x3c, +// 0x40, 0x90, 0x00, 0x0c, 0x7c, 0x84, 0x32, 0x14, 0x48, 0x00, 0x00, 0x30, 0x7c, 0x84, 0x82, 0x14, +// 0x48, 0x00, 0x00, 0x28, 0x54, 0x65, 0xa7, 0xff, 0x41, 0x82, 0x00, 0x0c, 0x7d, 0x27, 0x48, 0x2e, +// 0x7c, 0x84, 0x4a, 0x14, 0x40, 0x90, 0x00, 0x0c, 0x7c, 0xcc, 0x21, 0x2e, 0x4b, 0xff, 0xfd, 0x80, +// 0x7e, 0x0c, 0x21, 0x2e, 0x4b, 0xff, 0xfd, 0x78, 0x40, 0x90, 0x00, 0x0c, 0x7c, 0x86, 0x23, 0x78, +// 0x4b, 0xff, 0xfd, 0x6c, 0x7c, 0x90, 0x23, 0x78, 0x4b, 0xff, 0xfd, 0x64, 0x54, 0x89, 0x1e, 0x78, +// 0x39, 0x29, 0x00, 0x40, 0x2c, 0x05, 0x00, 0x02, 0x41, 0x80, 0x00, 0x48, 0x54, 0x6b, 0x50, 0x03, +// 0x41, 0x82, 0x00, 0x14, 0x41, 0x81, 0x00, 0x08, 0x48, 0x00, 0x00, 0x10, 0x41, 0xbe, 0xfd, 0x40, +// 0x48, 0x00, 0x00, 0x08, 0x40, 0xbe, 0xfd, 0x38, 0x2c, 0x05, 0x00, 0x03, 0x41, 0x81, 0x00, 0x10, +// 0x41, 0xa2, 0x00, 0x10, 0x7d, 0xe7, 0x48, 0x2e, 0x4b, 0xff, 0xfd, 0x24, 0x7d, 0xe7, 0x49, 0x2e, +// 0x7c, 0x64, 0x07, 0x34, 0x54, 0x84, 0x1a, 0x78, 0x7d, 0xef, 0x22, 0x14, 0x4b, 0xff, 0xfd, 0x10, +// 0x40, 0xbe, 0xfd, 0x0c, 0x7c, 0xa7, 0x4a, 0x14, 0x40, 0x92, 0x00, 0x14, 0x54, 0x64, 0x04, 0x3e, +// 0x91, 0xe5, 0x00, 0x00, 0x90, 0x85, 0x00, 0x04, 0x4b, 0xff, 0xfc, 0xf4, 0x81, 0x25, 0x00, 0x04, +// 0x2c, 0x09, 0x00, 0x00, 0x41, 0xa2, 0xfc, 0xe8, 0x39, 0x29, 0xff, 0xff, 0x91, 0x25, 0x00, 0x04, +// 0x81, 0xe5, 0x00, 0x00, 0x4b, 0xff, 0xfc, 0xd8, 0x40, 0xbe, 0xfc, 0xd4, 0x54, 0x6b, 0x16, 0xba, +// 0x7f, 0x47, 0x5a, 0x14, 0x81, 0x3a, 0x00, 0x00, 0x54, 0x6e, 0x67, 0xbe, 0x41, 0x92, 0x00, 0x84, +// 0x2e, 0x05, 0x00, 0x05, 0x40, 0x90, 0x01, 0x74, 0x2e, 0x05, 0x00, 0x03, 0x40, 0x90, 0x00, 0x90, +// 0x2e, 0x05, 0x00, 0x01, 0x54, 0x65, 0x87, 0xff, 0x41, 0x82, 0x00, 0x08, 0x7c, 0x8c, 0x22, 0x14, +// 0x2f, 0x0e, 0x00, 0x01, 0x40, 0x92, 0x00, 0x24, 0x41, 0xb9, 0x00, 0x18, 0x41, 0x9a, 0x00, 0x0c, +// 0x88, 0x84, 0x00, 0x00, 0x48, 0x00, 0x00, 0xf8, 0xa0, 0x84, 0x00, 0x00, 0x48, 0x00, 0x00, 0xf0, +// 0x80, 0x84, 0x00, 0x00, 0x48, 0x00, 0x00, 0xe8, 0x54, 0x73, 0xe5, 0x3e, 0x41, 0xb9, 0x00, 0x20, +// 0x41, 0x9a, 0x00, 0x10, 0x99, 0x24, 0x00, 0x00, 0x38, 0x84, 0x00, 0x01, 0x48, 0x00, 0x00, 0x18, +// 0xb1, 0x24, 0x00, 0x00, 0x38, 0x84, 0x00, 0x02, 0x48, 0x00, 0x00, 0x0c, 0x91, 0x24, 0x00, 0x00, +// 0x38, 0x84, 0x00, 0x04, 0x36, 0x73, 0xff, 0xff, 0x40, 0x80, 0xff, 0xd4, 0x4b, 0xff, 0xfc, 0x40, +// 0x54, 0x65, 0x87, 0xff, 0x41, 0x82, 0x00, 0x08, 0x7c, 0x84, 0x62, 0x14, 0x71, 0xc5, 0x00, 0x01, +// 0x41, 0x82, 0x00, 0x9c, 0x7c, 0x84, 0x4a, 0x14, 0x48, 0x00, 0x00, 0x94, 0x54, 0x6a, 0x87, 0xbe, +// 0x54, 0x8e, 0x16, 0xba, 0x7e, 0x67, 0x72, 0x14, 0x40, 0x92, 0x00, 0x08, 0x3a, 0x6f, 0xff, 0xfc, +// 0x80, 0x9a, 0x00, 0x00, 0x81, 0x33, 0x00, 0x00, 0x71, 0x4b, 0x00, 0x01, 0x41, 0x82, 0x00, 0x08, +// 0x7c, 0x9a, 0x23, 0x78, 0x71, 0x4b, 0x00, 0x02, 0x41, 0x82, 0x00, 0x10, 0x7d, 0x33, 0x4b, 0x78, +// 0x40, 0xb2, 0x00, 0x08, 0x7e, 0x6c, 0x9a, 0x14, 0x54, 0x65, 0x67, 0x3f, 0x2c, 0x05, 0x00, 0x09, +// 0x40, 0x80, 0x00, 0x54, 0x48, 0x00, 0x00, 0x79, 0x7c, 0x89, 0x22, 0x14, 0x48, 0x00, 0x00, 0x40, +// 0x7c, 0x89, 0x21, 0xd6, 0x48, 0x00, 0x00, 0x38, 0x7d, 0x24, 0x23, 0x78, 0x48, 0x00, 0x00, 0x30, +// 0x7d, 0x24, 0x20, 0x38, 0x48, 0x00, 0x00, 0x28, 0x7d, 0x24, 0x22, 0x78, 0x48, 0x00, 0x00, 0x20, +// 0x7d, 0x24, 0x20, 0x30, 0x48, 0x00, 0x00, 0x18, 0x7d, 0x24, 0x24, 0x30, 0x48, 0x00, 0x00, 0x10, +// 0x5d, 0x24, 0x20, 0x3e, 0x48, 0x00, 0x00, 0x08, 0x7d, 0x24, 0x26, 0x30, 0x90, 0x9a, 0x00, 0x00, +// 0x4b, 0xff, 0xfb, 0x8c, 0x2c, 0x05, 0x00, 0x0a, 0x41, 0x81, 0xfb, 0x84, 0xc0, 0x5a, 0x00, 0x00, +// 0xc0, 0x73, 0x00, 0x00, 0x41, 0x82, 0x00, 0x0c, 0xec, 0x43, 0x10, 0x2a, 0x48, 0x00, 0x00, 0x08, +// 0xec, 0x43, 0x00, 0xb2, 0xd0, 0x5a, 0x00, 0x00, 0x4b, 0xff, 0xfb, 0x64, 0x7d, 0x48, 0x02, 0xa6, +// 0x54, 0xa5, 0x1e, 0x78, 0x7d, 0x4a, 0x2a, 0x14, 0x80, 0x9a, 0x00, 0x00, 0x81, 0x33, 0x00, 0x00, +// 0x7d, 0x48, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, 0x40, 0xbe, 0xfb, 0x44, 0x54, 0x69, 0xc0, 0x3e, +// 0x7d, 0x8e, 0x63, 0x78, 0x48, 0x00, 0x00, 0x35, 0x41, 0x92, 0x00, 0x0c, 0x7e, 0x31, 0x22, 0x14, +// 0x48, 0x00, 0x00, 0x08, 0x7d, 0x29, 0x22, 0x14, 0x54, 0x64, 0xc4, 0x3f, 0x38, 0xa0, 0x00, 0x00, +// 0x41, 0x82, 0xfb, 0x1c, 0x7d, 0x45, 0x88, 0xae, 0x7d, 0x45, 0x49, 0xae, 0x38, 0xa5, 0x00, 0x01, +// 0x7c, 0x05, 0x20, 0x00, 0x4b, 0xff, 0xff, 0xec, 0x2e, 0x8a, 0x00, 0x04, 0x55, 0x31, 0x36, 0xba, +// 0x2c, 0x11, 0x00, 0x3c, 0x7e, 0x27, 0x88, 0x2e, 0x40, 0x82, 0x00, 0x08, 0x7d, 0xd1, 0x73, 0x78, +// 0x41, 0x96, 0x00, 0x08, 0xa2, 0x31, 0x00, 0x00, 0x55, 0x29, 0x56, 0xba, 0x2c, 0x09, 0x00, 0x3c, +// 0x7d, 0x27, 0x48, 0x2e, 0x40, 0x82, 0x00, 0x08, 0x7d, 0xc9, 0x73, 0x78, 0x41, 0x96, 0x00, 0x08, +// 0xa1, 0x29, 0x00, 0x00, 0x4e, 0x80, 0x00, 0x20, 0x2c, 0x05, 0x00, 0x04, 0x40, 0x80, 0x00, 0x28, +// 0x7c, 0x89, 0x23, 0x78, 0x7d, 0xc3, 0x62, 0x14, 0x55, 0xce, 0x00, 0x3c, 0x4b, 0xff, 0xff, 0xad, +// 0x7c, 0x84, 0x20, 0xf8, 0x54, 0x84, 0x04, 0x3e, 0x7d, 0x2b, 0x20, 0x38, 0x7e, 0x24, 0x20, 0x38, +// 0x4b, 0xff, 0xfb, 0xc4, 0x54, 0x6b, 0xe4, 0x3e, 0x4b, 0xff, 0xfb, 0xbc, 0x7c, 0x9a, 0x23, 0x78, +// 0x54, 0x84, 0x18, 0x38, 0x40, 0x92, 0x00, 0x20, 0x40, 0x9e, 0x00, 0x0c, 0x7d, 0xe8, 0x03, 0xa6, +// 0x4e, 0x80, 0x00, 0x21, 0x7d, 0xe4, 0x7a, 0x14, 0x39, 0xef, 0x00, 0x07, 0x55, 0xef, 0x00, 0x38, +// 0x4b, 0xff, 0xfa, 0x6c, 0x2e, 0x05, 0x00, 0x03, 0x41, 0x91, 0x00, 0x5c, 0x3c, 0xa0, 0x48, 0x00, +// 0x7d, 0x83, 0x62, 0x14, 0x55, 0x8c, 0x00, 0x3a, 0x40, 0x92, 0x00, 0x20, 0x40, 0xbe, 0xfa, 0x50, +// 0x57, 0x44, 0x00, 0x3a, 0x7c, 0x8c, 0x20, 0x50, 0x50, 0x85, 0x01, 0xba, 0x50, 0x65, 0x07, 0xfe, +// 0x90, 0xac, 0x00, 0x00, 0x4b, 0xff, 0xfa, 0x38, 0x40, 0xbe, 0xff, 0xbc, 0x7d, 0x2c, 0x78, 0x50, +// 0x51, 0x25, 0x01, 0xba, 0x90, 0xac, 0x00, 0x00, 0x39, 0x8c, 0x00, 0x04, 0x7d, 0x6f, 0x22, 0x14, +// 0x39, 0x6b, 0xff, 0xfc, 0x7d, 0x2b, 0x60, 0x50, 0x51, 0x25, 0x01, 0xba, 0x90, 0xab, 0x00, 0x00, +// 0x4b, 0xff, 0xff, 0x94, 0x2e, 0x05, 0x00, 0x06, 0x41, 0x92, 0x00, 0x28, 0x4b, 0xff, 0xfb, 0x28, +// 0x55, 0x8c, 0x84, 0x3e, 0x57, 0x44, 0x84, 0x3e, 0x57, 0x5a, 0x04, 0x3e, 0x7c, 0x0c, 0x20, 0x00, +// 0x41, 0x80, 0xfb, 0xa8, 0x7c, 0x0c, 0xd0, 0x00, 0x40, 0x80, 0xfb, 0xa0, 0x4b, 0xff, 0xf9, 0xe0, +// 0x57, 0x45, 0xff, 0xfe, 0x68, 0xa5, 0x00, 0x01, 0x71, 0x03, 0x00, 0x01, 0x7c, 0x05, 0x18, 0x00, +// 0x41, 0x82, 0x00, 0x1c, 0x51, 0x1a, 0x0f, 0xbc, 0x6b, 0x5a, 0x00, 0x02, 0x57, 0x45, 0xff, 0xff, +// 0x41, 0x82, 0x00, 0x08, 0x6b, 0x5a, 0x00, 0x01, 0x93, 0x4f, 0xff, 0xfc, 0x53, 0x48, 0x07, 0xfe, +// 0x4b, 0xff, 0xf9, 0xac, 0x2c, 0x0b, 0x00, 0x00, 0x41, 0x82, 0x01, 0x38, 0x2c, 0x05, 0x00, 0x01, +// 0x41, 0x82, 0x00, 0x18, 0x2c, 0x05, 0x00, 0x02, 0x41, 0x82, 0x00, 0x14, 0x2c, 0x05, 0x00, 0x03, +// 0x41, 0x82, 0x00, 0x70, 0x4b, 0xff, 0xf9, 0x40, 0x54, 0xcc, 0x00, 0x0c, 0x54, 0x97, 0x46, 0x3e, +// 0x54, 0x98, 0xc4, 0x3e, 0x54, 0x84, 0x06, 0x3e, 0x40, 0x9e, 0x00, 0xfc, 0x56, 0xf9, 0x06, 0x31, +// 0x7d, 0x9a, 0x63, 0x78, 0x7f, 0x43, 0xd2, 0x14, 0x57, 0x5a, 0x00, 0x3a, 0x41, 0x82, 0x00, 0x18, +// 0x7e, 0xf7, 0x07, 0x74, 0x7e, 0xf7, 0x00, 0xd0, 0x1f, 0x37, 0x00, 0x02, 0x3b, 0x39, 0x00, 0x04, +// 0x7f, 0x59, 0xd0, 0x50, 0x2c, 0x17, 0x00, 0x00, 0x41, 0x82, 0x00, 0x1c, 0x3b, 0x20, 0x00, 0x00, +// 0x7e, 0xe9, 0x03, 0xa6, 0xa3, 0x7a, 0x00, 0x04, 0x7f, 0x79, 0xca, 0x78, 0x3b, 0x5a, 0x00, 0x02, +// 0x42, 0x00, 0xff, 0xf4, 0x7c, 0x18, 0xc8, 0x00, 0x40, 0x82, 0x00, 0xac, 0x4b, 0xff, 0xfe, 0x90, +// 0x51, 0x08, 0x08, 0x3c, 0x40, 0x9e, 0x00, 0x9c, 0x54, 0x77, 0xb0, 0x03, 0x41, 0x81, 0x00, 0x88, +// 0x41, 0x80, 0x00, 0x8c, 0x54, 0x7e, 0x06, 0x3e, 0x1f, 0xde, 0x00, 0x02, 0x54, 0x97, 0x00, 0x1e, +// 0x6e, 0xf8, 0x80, 0x00, 0x2c, 0x18, 0x00, 0x00, 0x40, 0x82, 0x00, 0x08, 0x62, 0xf7, 0x30, 0x00, +// 0x54, 0x98, 0x80, 0x1e, 0x1f, 0x3e, 0x00, 0x04, 0x7f, 0x19, 0xc0, 0x50, 0x3b, 0x20, 0x00, 0x00, +// 0x1f, 0x59, 0x00, 0x04, 0x7f, 0x6f, 0xd0, 0x2e, 0x7f, 0x57, 0xd0, 0x2e, 0x3b, 0x39, 0x00, 0x01, +// 0x7c, 0x17, 0xc0, 0x40, 0x41, 0x81, 0x00, 0x34, 0x7c, 0x19, 0xf0, 0x00, 0x41, 0x81, 0x00, 0x14, +// 0x7c, 0x1a, 0xd8, 0x00, 0x41, 0x82, 0xff, 0xdc, 0x3a, 0xf7, 0x00, 0x04, 0x4b, 0xff, 0xff, 0xd0, +// 0x80, 0x6f, 0xff, 0xf8, 0x60, 0x63, 0x03, 0x00, 0x90, 0x6f, 0xff, 0xf8, 0x92, 0xef, 0xff, 0xfc, +// 0x7e, 0xf0, 0xbb, 0x78, 0x48, 0x00, 0x00, 0x1c, 0x80, 0x6f, 0xff, 0xf8, 0x60, 0x63, 0x01, 0x00, +// 0x90, 0x6f, 0xff, 0xf8, 0x61, 0x08, 0x00, 0x01, 0x48, 0x00, 0x00, 0x08, 0x7c, 0x90, 0x23, 0x78, +// 0x54, 0x64, 0x06, 0x3e, 0x1c, 0x84, 0x00, 0x08, 0x7d, 0xe4, 0x7a, 0x14, 0x4b, 0xff, 0xf8, 0x70, +// 0x40, 0x92, 0x00, 0x0c, 0x39, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x14, 0x54, 0x69, 0x06, 0xff, +// 0x54, 0x65, 0x67, 0xfe, 0x7d, 0x08, 0x4c, 0x30, 0x55, 0x17, 0xff, 0xff, 0x40, 0x82, 0x00, 0x08, +// 0x7d, 0x08, 0x2a, 0x78, 0x54, 0x85, 0x00, 0x1f, 0x41, 0x82, 0x00, 0x08, 0x7c, 0xa6, 0x2b, 0x78, +// 0x54, 0x85, 0x80, 0x1f, 0x41, 0x82, 0x00, 0x08, 0x7c, 0xb0, 0x2b, 0x78, 0x4b, 0xff, 0xf8, 0x30, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +//}; diff --git a/Config.c b/Config.c new file mode 100644 index 0000000..9f7032c --- /dev/null +++ b/Config.c @@ -0,0 +1,74 @@ +#include "Config.h" + +DML_CFG *DMLCfg; + +void ConfigInit( DML_CFG *Cfg ) +{ + DMLCfg = (DML_CFG*)0xFFFE4200; + + memset32( DMLCfg, 0, sizeof(DML_CFG) ); + + //If a loader supplied any options we use them otherwise use the code defines + if( Cfg->Magicbytes == 0xD1050CF6 && Cfg->Version == CONFIG_VERSION ) + { + memcpy( DMLCfg, Cfg, sizeof( DML_CFG ) ); + + } else { + + dbgprintf("No config found in RAM\n"); + dbgprintf("Version:%08X\n", DMLCfg->Version ); + dbgprintf("Config:%08X\n", DMLCfg->Config ); + + DMLCfg->Config = 0; +#ifdef CHEATHOOK + DMLCfg->Config |= DML_CFG_CHEATS; +#endif +#ifdef DEBUGGER + DMLCfg->Config |= DML_CFG_DEBUGGER; +#endif +#ifdef DEBUGGERWAIT + DMLCfg->Config |= DML_CFG_DEBUGWAIT; +#endif +#ifdef CARDMODE + DMLCfg->Config |= DML_CFG_NMM; +#endif +#ifdef CARDDEBUG + DMLCfg->Config |= DML_CFG_NMM_DEBUG; +#endif +#ifdef ACTIVITYLED + DMLCfg->Config |= DML_CFG_ACTIVITY_LED; +#endif +#ifdef PADHOOK + DMLCfg->Config |= DML_CFG_PADHOOK; +#endif + DMLCfg->VideoMode = DML_VID_DML_AUTO; + DMLCfg->Version = CONFIG_VERSION; + DMLCfg->Magicbytes = 0xD1050CF6; + } + + //Check if a memcard is inserted in Slot A + if( (read32(0xD806800) & 0x1000) == 0x1000 ) + { + DMLCfg->Config &= ~DML_CFG_NMM; // disable NMM + } + + dbgprintf("Config:%08X\n", DMLCfg->Config ); +} + +inline u32 ConfigGetConfig( u32 Config ) +{ + return !!(DMLCfg->Config & Config); +} +u32 ConfigGetVideMode( void ) +{ + return DMLCfg->VideoMode; +} + +char *ConfigGetGamePath( void ) +{ + return DMLCfg->GamePath; +} +char *ConfigGetCheatPath( void ) +{ + return DMLCfg->CheatPath; +} diff --git a/Config.h b/Config.h new file mode 100644 index 0000000..a374642 --- /dev/null +++ b/Config.h @@ -0,0 +1,97 @@ +#ifndef _CFG_ +#define _CFG_ + +#include "string.h" +#include "global.h" +#include "ipc.h" +#include "alloc.h" +#include "vsprintf.h" +#include "HW.h" + +#define EXI_BASE 0x0D806800 + +typedef struct GC_SRAM +{ +/* 0x00 */ u16 CheckSum1; +/* 0x02 */ u16 CheckSum2; +/* 0x04 */ u32 ead0; +/* 0x08 */ u32 ead1; +/* 0x0C */ u32 CounterBias; +/* 0x10 */ u8 DisplayOffsetH; +/* 0x11 */ u8 BootMode; // Bit 6 PAL60 flag +/* 0x12 */ u8 Language; +/* 0x13 */ u8 Flags; + /* + bit desc 0 1 + 0 -\_ Video mode + 1 -/ + 2 Sound mode Mono Stereo + 3 always 1 + 4 always 0 + 5 always 1 + 6 ? + 7 Prog mode off on + */ +/* 0x14 */ u8 FlashID[2*12]; +/* 0x2C */ u32 WirelessKBID; +/* 0x30 */ u16 WirlessPADID[4]; +/* 0x38 */ u8 LastDVDError; +/* 0x39 */ u8 Reserved; +/* 0x3A */ u16 FlashIDChecksum[2]; +/* 0x3C */ u16 Unused; +} GC_SRAM; + +typedef struct DML_CFG +{ + u32 Magicbytes; // 0xD1050CF6 + u32 Version; // 0x00000001 + u32 VideoMode; + u32 Config; + char GamePath[255]; + char CheatPath[255]; +} DML_CFG; + +enum dmlconfig +{ + DML_CFG_CHEATS = (1<<0), + DML_CFG_DEBUGGER = (1<<1), + DML_CFG_DEBUGWAIT = (1<<2), + DML_CFG_NMM = (1<<3), + DML_CFG_NMM_DEBUG = (1<<4), + DML_CFG_GAME_PATH = (1<<5), + DML_CFG_CHEAT_PATH = (1<<6), + DML_CFG_ACTIVITY_LED= (1<<7), + DML_CFG_PADHOOK = (1<<8), + DML_CFG_NODISC = (1<<9), + DML_CFG_BOOT_DISC = (1<<10), + DML_CFG_BOOT_DOL = (1<<11), +}; + +enum dmlvideomode +{ + DML_VID_DML_AUTO = (0<<16), + DML_VID_FORCE = (1<<16), + DML_VID_NONE = (2<<16), + + DML_VID_FORCE_PAL50 = (1<<0), + DML_VID_FORCE_PAL60 = (1<<1), + DML_VID_FORCE_NTSC = (1<<2), + DML_VID_FORCE_PROG = (1<<3), +}; + +enum VideoModes +{ + GCVideoModeNone = 0, + GCVideoModePAL60 = 1, + GCVideoModeNTSC = 2, + GCVideoModePROG = 3, +}; + +void ConfigInit( DML_CFG *Cfg ); +u32 ConfigGetConfig( u32 Config ); +u32 ConfigGetVideMode( void ); + +char *ConfigGetGamePath( void ); +char *ConfigGetCheatPath( void ); + +#endif \ No newline at end of file diff --git a/DVD.c b/DVD.c new file mode 100644 index 0000000..cfa66a4 --- /dev/null +++ b/DVD.c @@ -0,0 +1,398 @@ +#include "DVD.h" + +DVDConfig *DICfg = (DVDConfig *)NULL; +u32 read; +static char GamePath[256]; + +extern FIL GameFile; +extern u32 FSTMode; +extern u32 DOLMaxOff; +extern u32 DOLOffset; + +u8 HardDriveConnected; +FATFS fatfs; + +static u8 *FSTable ALIGNED(32); +u32 ApploaderSize=0; +u32 dolOffset=0; +u32 FSTableSize=0; +u32 FSTableOffset=0; + +u32 FCEntry=0; +FileCache FC[FILECACHE_MAX]; +u32 FCState[FILECACHE_MAX]; + +void DVDInit( void ) +{ + int i=0; + s32 fres = FR_DISK_ERR; + int MountFail=0; + HardDriveConnected = 0; + + while(!HardDriveConnected) + { + while(1) + { + fres = f_mount(0, &fatfs ); + dbgprintf( "DIP:f_mount():%d\n", fres ); + if( fres == FR_OK ) + break; + else + MountFail++; + + if( MountFail == 10 ) + { + dbgprintf( "DIP:too much fail! looping now!\n"); + while(1); + } + + udelay(500000); + } + + //try to open a file, it doesn't have to exist, just testing if FS works + FIL f; + fres = f_open( &f, "/randmb.in", FA_READ|FA_OPEN_EXISTING ); + switch(fres) + { + case FR_OK: + f_close( &f ); + case FR_NO_PATH: + case FR_NO_FILE: + { + HardDriveConnected = 1; + fres = FR_OK; + } break; + default: + case FR_DISK_ERR: + { + dbgprintf( "DIP: Disk error\n", fres ); + while(1); + } break; + } + } + + if( fres != FR_OK ) + { + dbgprintf( "Could not find any USB device!"); + } + + return; +} +s32 DVDSelectGame( void ) +{ + char *str = (char*)malloca( 256, 32 ); + + if( ConfigGetConfig(DML_CFG_GAME_PATH) ) + { + sprintf( str, "%s", ConfigGetGamePath() ); + + } else { + dbgprintf("No game path was supplied!\n"); + free(str); + return -1; + } + + s32 fres = f_open( &GameFile, str, FA_READ ); + if( fres != FR_OK ) + { + sprintf( GamePath, "%s", str ); + + //Try to switch to FST mode + if( !FSTInit() ) + { + dbgprintf("Failed to open:\"%s\" fres:%d\n", str, fres ); + free(str); + return -2; + } + + } else { + + f_lseek( &GameFile, 0 ); + f_read( &GameFile, (void*)0, 0x20, &read ); + + f_lseek( &GameFile, 0 ); + f_read( &GameFile, str, 0x400, &read ); + + dbgprintf("DIP:Loading game %.6s: %s\n", str, (char *)(str+0x20)); + + f_lseek( &GameFile, 0x420 ); + f_read( &GameFile, str, 0x40, &read ); + } + + free( str ); + + return DI_SUCCESS; +} +u32 FSTInit( void ) +{ + char Path[256]; + FIL fd; + u32 read; + + sprintf( Path, "%ssys/boot.bin", GamePath ); + if( f_open( &fd, Path, FA_READ ) != FR_OK ) + { + dbgprintf( "DIP:[%s] Failed to open!\n", Path ); + return 0; + + } else { + + u8 *rbuf = (u8*)malloc( 0x100 ); + + f_lseek( &fd, 0 ); + f_read( &fd, rbuf, 0x100, &read ); + + dbgprintf("DIP:Loading game %.6s: %s\n", rbuf, (char *)(rbuf+0x20)); + + //Read DOL/FST offset/sizes for later usage + f_lseek( &fd, 0x0420 ); + f_read( &fd, rbuf, 0x20, &read ); + + dolOffset = *(u32*)(rbuf); + FSTableOffset = *(u32*)(rbuf+4); + FSTableSize = *(u32*)(rbuf+8); + + free( rbuf ); + + dbgprintf( "DIP:FSTableOffset:%08X\n", FSTableOffset ); + dbgprintf( "DIP:FSTableSize: %08X\n", FSTableSize ); + dbgprintf( "DIP:DolOffset: %08X\n", dolOffset ); + + FSTMode = 1; + + f_close( &fd ); + } + + //Init cache + u32 count = 0; + for( count=0; count < FILECACHE_MAX; ++count ) + { + FCState[count] = 0xdeadbeef; + } + + return 1; +} + +void Asciify( char *str ) +{ + int i=0; + for( i=0; i < strlen(str); i++ ) + if( str[i] < 0x20 || str[i] > 0x7F ) + str[i] = '_'; +} +void FSTRead( char *Buffer, u32 Length, u32 Offset ) +{ + char Path[256]; + FIL fd; + u32 read; + int i,j; + + if( Offset >= FSTableOffset+FSTableSize ) { + + //Get FSTTable offset from low memory, must be set by apploader + if( FSTable == NULL ) + { + FSTable = (u8*)((*(vu32*)0x38) & 0x7FFFFFFF); + //dbgprintf("DIP:FSTOffset: %08X\n", (u32)FSTable ); + } + + //try cache first! + for( i=0; i < FILECACHE_MAX; ++i ) + { + if( FCState[i] == 0xdeadbeef ) + continue; + + if( Offset >= FC[i].Offset ) + { + u64 nOffset = Offset - FC[i].Offset; + if( nOffset < FC[i].Size ) + { + //dbgprintf("DIP:[Cache:%02d][%08X:%05X]\n", i, (u32)(nOffset>>2), Length ); + f_lseek( &(FC[i].File), nOffset ); + f_read( &(FC[i].File), Buffer, ((Length)+31)&(~31), &read ); + return; + } + } + } + + //The fun part! + + u32 Entries = *(u32*)(FSTable+0x08); + char *NameOff = (char*)(FSTable + Entries * 0x0C); + FEntry *fe = (FEntry*)(FSTable); + + u32 Entry[16]; + u32 LEntry[16]; + u32 level=0; + + for( i=1; i < Entries; ++i ) + { + if( level ) + { + while( LEntry[level-1] == i ) + { + //printf("[%03X]leaving :\"%s\" Level:%d\n", i, buffer + NameOff + swap24( fe[Entry[level-1]].NameOffset ), level ); + level--; + } + } + + if( fe[i].Type ) + { + //Skip empty folders + if( fe[i].NextOffset == i+1 ) + continue; + + //printf("[%03X]Entering:\"%s\" Level:%d leave:%04X\n", i, buffer + NameOff + swap24( fe[i].NameOffset ), level, swap32( fe[i].NextOffset ) ); + Entry[level] = i; + LEntry[level++] = fe[i].NextOffset; + if( level > 15 ) // something is wrong! + break; + } else { + + if( Offset >= fe[i].FileOffset ) + { + u32 nOffset = (Offset - fe[i].FileOffset); + if( nOffset < fe[i].FileLength ) + { + // dbgprintf("DIP:Offset:%08X FOffset:%08X Dif:%08X Flen:%08X nOffset:%08X\n", Offset, fe[i].FileOffset, Offset-fe[i].FileOffset, fe[i].FileLength, nOffset ); + + //Do not remove! + memset( Path, 0, 256 ); + sprintf( Path, "%sroot/", GamePath ); + + for( j=0; j= FILECACHE_MAX ) + FCEntry = 0; + + if( FCState[FCEntry] != 0xdeadbeef ) + { + f_close( &(FC[FCEntry].File) ); + FCState[FCEntry] = 0xdeadbeef; + } + + Asciify( Path ); + + f_open( &(FC[FCEntry].File), Path, FA_READ ); + + FC[FCEntry].Size = fe[i].FileLength; + FC[FCEntry].Offset = fe[i].FileOffset; + FCState[FCEntry] = 0x23; + + f_lseek( &(FC[FCEntry].File), nOffset ); + f_read( &(FC[FCEntry].File), Buffer, Length, &read ); + + FCEntry++; + } + } + } + } + + } else if ( Offset >= FSTableOffset ) { + + Offset -= FSTableOffset; + + sprintf( Path, "%ssys/fst.bin", GamePath ); + if( f_open( &fd, Path, FA_READ ) != FR_OK ) + { + dbgprintf( "DIP:[%s] Failed to open!\n", Path ); + return; + } else { + //dbgprintf( "DIP:[fst.bin] Offset:%08X Size:%08X\n", Offset, Length ); + + f_lseek( &fd, Offset ); + f_read( &fd, Buffer, Length, &read ); + f_close( &fd ); + + if( FSTable == NULL ) + { + FSTable = (u8*)Buffer; + } + + return; + } + + } else if ( Offset >= dolOffset ) { + + Offset -= dolOffset; + + sprintf( Path, "%ssys/main.dol", GamePath ); + if( f_open( &fd, Path, FA_READ ) != FR_OK ) + { + dbgprintf( "DIP:[%s] Failed to open!\n", Path ); + return; + } else { + //dbgprintf( "DIP:[main.dol] Offset:%08X Size:%08X\n", Offset, Length ); + + f_lseek( &fd, Offset ); + f_read( &fd, Buffer, Length, &read ); + f_close( &fd ); + + return; + } + + } else if ( Offset >= 0x2440 ) { + + Offset -= 0x2440; + + sprintf( Path, "%ssys/apploader.img", GamePath ); + if( f_open( &fd, Path, FA_READ ) != FR_OK ) + { + dbgprintf( "DIP:[%s] Failed to open!\n", Path ); + return; + } else { + //dbgprintf( "DIP:[apploader.img] Offset:%08X Size:%08X\n", Offset, Length ); + + f_lseek( &fd, Offset ); + f_read( &fd, Buffer, Length, &read ); + f_close( &fd ); + + return; + } + + } else if ( Offset >= 0x440 ) { + + Offset -= 0x440; + + sprintf( Path, "%ssys/bi2.bin", GamePath ); + if( f_open( &fd, Path, FA_READ ) != FR_OK ) + { + dbgprintf( "DIP:[%s] Failed to open!\n", Path ); + return; + } else { + //dbgprintf( "DIP:[bi2.bin] Offset:%08X Size:%08X\n", Offset, Length ); + + f_lseek( &fd, Offset ); + f_read( &fd, Buffer, Length, &read ); + + f_close( &fd ); + + return; + } + + } else { + sprintf( Path, "%ssys/boot.bin", GamePath ); + if( f_open( &fd, Path, FA_READ ) != FR_OK ) + { + dbgprintf( "DIP:[%s] Failed to open!\n", Path ); + return; + } else { + //dbgprintf( "DIP:[boot.bin] Offset:%08X Size:%08X\n", Offset, Length ); + + f_lseek( &fd, Offset ); + f_read( &fd, Buffer, Length, &read ); + + f_close( &fd ); + + return; + } + } +} diff --git a/DVD.h b/DVD.h new file mode 100644 index 0000000..7d15799 --- /dev/null +++ b/DVD.h @@ -0,0 +1,29 @@ +#ifndef _DVD_ +#define _DVD_ + +#include "global.h" +#include "HW.h" +#include "Config.h" +#include "ff.h" +#include "dol.h" +#include "Drive.h" + +typedef struct +{ + u32 SlotID; + u32 Region; + u32 Gamecount; + u32 Config; + u8 GameInfo[][0x80]; +} DVDConfig; + +void DVDInit( void ); +void DVDReadConfig( void ); +s32 DVDSelectGame( void ); +void DVDUpdateFSTRAM( void ); +void DiscBackup( u32 FrameBuffer ); + +u32 FSTInit( void ); +void FSTRead( char *Buffer, u32 Length, u32 Offset ); + +#endif diff --git a/DVDPatches.c b/DVDPatches.c new file mode 100644 index 0000000..3bff736 --- /dev/null +++ b/DVDPatches.c @@ -0,0 +1,10 @@ +#ifndef __DVDPATCHES__ +#define __DVDPATCHES__ + +#include "global.h" + +#include "asm\DVDInquiryAsync.h" +#include "asm\DVDSeekAbsAsyncPrio.h" +#include "asm\padipc.h" + +#endif diff --git a/Drive.c b/Drive.c new file mode 100644 index 0000000..e4002f5 --- /dev/null +++ b/Drive.c @@ -0,0 +1,124 @@ +#include "Drive.h" + +void DVDLowReset( void ) +{ + clear32( HW_RESETS, (1<<10) | (1<<17) ); + set32( HW_RESETS, (1<<10) | (1<<17) ); +} +u32 DVDLowGetError( void ) +{ + write32( 0x0D806000, 0x2E ); + write32( 0x0D806008, 0xE0000000 ); + write32( 0x0D806020, 0 ); + write32( 0x0D80601c, 1 ); + + while( read32(0x0D80601c) & 1 ); + + set32( 0x0D806000, (1<<4) ); + + return read32( 0x0D806020 ); +} +u32 DVDLowSeek( void ) +{ + write32( 0x0D806000, 0x2E ); + write32( 0x0D806008, 0xAB000000 ); + write32( 0x0D806020, 0 ); + write32( 0x0D80601c, 1 ); + + while( read32(0x0D80601c) & 1 ); + + set32( 0x0D806000, (1<<4) ); + + return read32( 0x0D806020 ); +} +u32 DVDLowStopMotor( void ) +{ + write32( 0x0D806000, 0x2E ); + write32( 0x0D806008, 0xE3000000 ); + write32( 0x0D806020, 0 ); + write32( 0x0D80601c, 1 ); + + while( read32(0x0D80601c) & 1 ); + + set32( 0x0D806000, (1<<4) ); + + return read32( 0x0D806020 ); +} +u32 LowReadDiscID( void *data ) +{ + write32( 0x0D806008, 0xA8000040 ); + write32( 0x0D80600C, 0 ); + write32( 0x0D806010, 0x20 ); + write32( 0x0D806018, 0x20 ); + + write32( 0x0D806014, (u32)data ); + + write32( 0x0D806000, 0x3A ); + + write32( 0x0D80601C, 3 ); + + while (1) + { + if( read32( 0x0D806000 ) & (1<<2) ) + { + set32( 0x0D806000, (1<<2) ); + return 1; + } + if( read32( 0x0D806000 ) & (1<<4) ) + { + set32( 0x0D806000, (1<<4) ); + return 0; + } + } + + return 0; +} +u32 LowRead( void *data, u32 Offset, u32 Length ) +{ + write32( 0x0D806008, 0xA8000000 ); + write32( 0x0D80600C, Offset>>2 ); + write32( 0x0D806010, Length ); + write32( 0x0D806018, Length ); + + write32( 0x0D806014, (u32)data ); + + write32( 0x0D806000, 0x3A ); + + write32( 0x0D80601C, 3 ); + + while (1) + { + if( read32( 0x0D806000 ) & (1<<2) ) + { + set32( 0x0D806000, (1<<2) ); + return 1; + } + if( read32( 0x0D806000 ) & (1<<4) ) + { + set32( 0x0D806000, (1<<4) ); + return 0; + } + } + + return 0; +} +u32 DVDEnableAudioStreaming( u32 Enable ) +{ + write32( 0x0D806004, read32( 0x0D806004 ) ); + + write32( 0x0D806008, 0xE4000000 | (Enable<<16) | 0x0A ); + + write32( 0x0D80601C, 1 ); + + while( read32(0x0D80601C) & 1 ); + + while(1) + { + if( read32( 0x0D806000 ) & 4 ) + return DI_ERROR; + if(!read32(0x0D806018)) + return DI_SUCCESS; + } + + return DI_FATAL; +} diff --git a/Drive.h b/Drive.h new file mode 100644 index 0000000..0dc9fcb --- /dev/null +++ b/Drive.h @@ -0,0 +1,16 @@ +#ifndef _DRIVE_ +#define _DRIVE_ + +#include "global.h" +#include "HW.h" +#include "memory.h" + +void DVDLowReset( void ); +u32 DVDEnableAudioStreaming( u32 Enable ); +u32 DVDLowGetError( void ); +u32 DVDLowStopMotor( void ); +u32 LowReadDiscID( void *data ); +u32 LowRead( void *data, u32 Offset, u32 Length ); +u32 DVDLowSeek( void ); + +#endif diff --git a/FwritePatches.c b/FwritePatches.c new file mode 100644 index 0000000..b24db39 --- /dev/null +++ b/FwritePatches.c @@ -0,0 +1,42 @@ + +unsigned char VIFlush[8] = +{ + 0x7C, 0x80, 0x03, 0x78, + 0x7C, 0xA3, 0x1B, 0x78, +} ; + +//unsigned char __fwrite[] = +//{ +// 0x7C, 0x08, 0x02, 0xA6, 0x90, 0x01, 0x00, 0x04, 0x94, 0x21, 0xFF, 0x88, 0x40, 0x86, 0x00, 0x24, +//} ; +// +//unsigned char __fwriteGC[] = +//{ +// 0x94, 0x21, 0xFF, 0xD0, 0x7C, 0x08, 0x02, 0xA6, 0x90, 0x01, 0x00, 0x34, 0xBF, 0x21, 0x00, 0x14, +// 0x7C, 0x99, 0x23, 0x78, 0x7C, 0xDA, 0x33, 0x78, 0x7C, 0x7B, 0x1B, 0x78, 0x7C, 0xBC, 0x2B, 0x78, +// 0x38, 0x80, 0x00, 0x00, 0x7F, 0x43, 0xD3, 0x78, +//} ; +//unsigned char __fwriteGCB[] = +//{ +// 0x7C, 0x08, 0x02, 0xA6, 0x90, 0x01, 0x00, 0x04, 0x94, 0x21, 0xFF, 0xB8, 0xBF, 0x21, 0x00, 0x2C, +// 0x3B, 0x44, 0x00, 0x00, 0x3B, 0x66, 0x00, 0x00, 0x3B, 0x83, 0x00, 0x00, 0x3B, 0x25, 0x00, 0x00, +// 0x38, 0x7B, 0x00, 0x00, 0x38, 0x80, 0x00, 0x00, +//} ; +//unsigned char __fwriteGCC[32] = +//{ +// 0x94, 0x21, 0xFF, 0xB0, 0x7C, 0x08, 0x02, 0xA6, 0x90, 0x01, 0x00, 0x54, 0xBE, 0x81, 0x00, 0x20, +// 0x7C, 0x97, 0x23, 0x78, 0x7C, 0xDF, 0x33, 0x78, 0x38, 0x80, 0x00, 0x00, 0x90, 0x61, 0x00, 0x08, +//} ; + +unsigned char patch_fwrite_GC[144] = +{ + 0x7C, 0x85, 0x21, 0xD7, 0x40, 0x81, 0x00, 0x84, 0x3C, 0xE0, 0xCC, 0x00, 0x3D, 0x40, 0xCC, 0x00, + 0x3D, 0x60, 0xCC, 0x00, 0x60, 0xE7, 0x68, 0x14, 0x61, 0x4A, 0x68, 0x24, 0x61, 0x6B, 0x68, 0x20, + 0x38, 0xC0, 0x00, 0x00, 0x7C, 0x06, 0x18, 0xAE, 0x54, 0x00, 0xA0, 0x16, 0x64, 0x08, 0xB0, 0x00, + 0x38, 0x00, 0x00, 0xD0, 0x90, 0x07, 0x00, 0x00, 0x7C, 0x00, 0x06, 0xAC, 0x91, 0x0A, 0x00, 0x00, + 0x7C, 0x00, 0x06, 0xAC, 0x38, 0x00, 0x00, 0x19, 0x90, 0x0B, 0x00, 0x00, 0x7C, 0x00, 0x06, 0xAC, + 0x80, 0x0B, 0x00, 0x00, 0x7C, 0x00, 0x04, 0xAC, 0x70, 0x09, 0x00, 0x01, 0x40, 0x82, 0xFF, 0xF4, + 0x80, 0x0A, 0x00, 0x00, 0x7C, 0x00, 0x04, 0xAC, 0x39, 0x20, 0x00, 0x00, 0x91, 0x27, 0x00, 0x00, + 0x7C, 0x00, 0x06, 0xAC, 0x74, 0x09, 0x04, 0x00, 0x41, 0x82, 0xFF, 0xB8, 0x38, 0xC6, 0x00, 0x01, + 0x7F, 0x86, 0x20, 0x00, 0x40, 0x9E, 0xFF, 0xA0, 0x7C, 0xA3, 0x2B, 0x78, 0x4E, 0x80, 0x00, 0x20, +}; diff --git a/GCPad.h b/GCPad.h new file mode 100644 index 0000000..1950928 --- /dev/null +++ b/GCPad.h @@ -0,0 +1,47 @@ +#ifndef __GCPAD__ +#define __GCPAD__ +typedef struct +{ + union + { + struct + { + bool ErrorStatus :1; + bool ErrorLatch :1; + u32 Reserved :1; + bool Start :1; + + bool Y :1; + bool X :1; + bool B :1; + bool A :1; + + u32 AlwaysSet :1; + bool R :1; + bool L :1; + bool Z :1; + + bool Up :1; + bool Down :1; + bool Right :1; + bool Left :1; + + s16 StickX :8; + s16 StickY :8; + }; + u32 Buttons; + }; + union + { + struct + { + s16 CStickX; + s16 CStickY; + s16 LShoulder; + s16 RShoulder; + }; + u32 Sticks; + }; +} GCPadStatus; +#endif + diff --git a/HW.c b/HW.c new file mode 100644 index 0000000..38a9417 --- /dev/null +++ b/HW.c @@ -0,0 +1,1045 @@ +#include "HW.h" + +void EXIControl( u32 value ) +{ + if( value == 0 ) + { + *(vu32*)0x3160 = 0xDEADBEEF; + *(vu32*)0xD800070 &= ~1; + } else { + *(vu32*)0x3160 = 0; + *(vu32*)0xD800070 |= 1; + } +} +void HWRegWriteBatch( u32 A, u32 B, u32 C, u32 D, u32 delay ) +{ + DRAMWrite( 0x160, A ); + DRAMWrite( 0x161, B ); + DRAMWrite( 0x161, C ); + DRAMWrite( 0x161, D ); + udelay(delay); +} +//inline void RegisterWrite( u32 Offset, u32 Value ) +//{ +// *(vu16*)(0x0D8B4000+Offset) = Value; +//} +//inline volatile u32 RegisterRead( u32 Offset ) +//{ +// return *(vu16*)(0x0D8B4000+Offset); +//} +//void DRAMWrite( u32 Register, u32 Value ) +//{ +// RegisterWrite( 0x74, Register ); +// RegisterRead( 0x74 ); +// RegisterWrite( 0x76, Value ); +//} +//u32 DRAMRead( u32 Register ) +//{ +// RegisterWrite( 0x74, Register ); +// RegisterRead( 0x74 ); +// return RegisterRead( 0x76 ); +//} +//void DRAMCTRLWrite( u32 ValueA, u32 ValueB ) +//{ +// DRAMWrite( 0x0163, ValueA ); +// DRAMRead( 0x0163 ); +// DRAMWrite( 0x0162, ValueB ); +//} +//u32 DRAMCTRLRead( u32 ValueA ) +//{ +// DRAMWrite( 0x0163, ValueA ); +// DRAMRead( 0x0163 ); +// return DRAMRead( 0x0162 ); +//} +void SomeFuncA( void ) +{ + set32( 0xD80018C, 0x400 ); + set32( 0xD80018C, 0x800 ); +} +void SomeFuncD( void ) +{ + u32 cookie = *(vu32*)(0xD8001D8) & 0x7FFFFFFF; + write32( 0xD8001D8, cookie ); + udelay(2); + + write32( 0xD8001D8, cookie & 0xBFFFFFFF ); + udelay(10); + + write32( 0xD8001D8, (read32(0xD8001D8) & 0xBFFFFFFF) | 0x40000000 ); + udelay(50); + + write32( 0xD8001D8, (read32(0xD8001D8) & 0x7FFFFFFF) | 0x80000000 ); + udelay(2); + +} +void EHCIInit( void ) +{ + SomeFuncA(); + + u32 HWVerFlag = ((*(vu32*)0xD800214) << 0x18) >> 0x1C; + + write32( 0xD800088, 0xFE ); + udelay(2); + + SomeFuncD(); + + write32( 0xD800088, 0xF6 ); + udelay(50); + + write32( 0xD800088, 0xF4 ); + udelay(1); + + write32( 0xD800088, 0xF0 ); + udelay(1); + + write32( 0xD800088, 0x70 ); + udelay(1); + + write32( 0xD800088, 0x60 ); + udelay(1); + + write32( 0xD800088, 0x40 ); + udelay(1); + + write32( 0xD800088, 0x00 ); + udelay(1); + + write32( 0xD0400B4, 0x2214 ); + + if( HWVerFlag == 0 ) + { + write32( 0xD0400B0, 0x20400 ); + } else { + write32( 0xD0400B0, 0x20600 ); + } + + write32( 0xD0400A4, 0x26 ); + udelay(1); + + write32( 0xD0400A4, 0x2026 ); + udelay(1); + + write32( 0xD0400A4, 0x4026 ); + udelay(20); + + write32( 0xD0400CC, 0x111 ); + udelay(1); + + //set32( 0xD800194, 0x7FDFBCF ); + + write32( 0xD8001E0, 0x65244A ); + write32( 0xD8001E4, 0x46A024 ); + + return; +} +void Shutdown( void ) +{ + udelay(200); + set32( 0x0d8000E0, 1<<1); + while(1); +} + +void GetRevision( u32 *Version, u32 *Revision ) +{ + u32 val = read32(HW_VERSION); + + *Version = (val << 24 ) >> 28; + *Revision = val & 0x0F; +} +void PPCReset( void ) +{ + clear32( HW_RESETS, 0x30 ); + udelay(15); + set32( HW_RESETS, 0x20 ); +} + +void HWResetDisable( void ) +{ + clear32( HW_RESETS, 0x4800 ); +} +void HWResetEnable( void ) +{ + set32( HW_RESETS, 0x4800 ); +} +void HW_184( void ) +{ + clear32( 0xD800184, 0x438E ); +} +void HW_184_2( void ) +{ + set32( 0xD800184, 0x438E ); +} +void ChangeClock( void ) +{ + write32( HW_CLOCKS, 1 ); + udelay(1); + write32( HW_RESETS, 0x7FFFFFF7 ); + udelay(1); + + write32( HW_CLOCKS, 3 ); + write32( HW_RESETS, 0x7FFFFFFF ); + udelay(4); + + write32( HW_CLOCKS, 2 ); + udelay(1); +} + +#define RAMTEST {write32( 0x13020000, 0xdeadbeaf); if( read32( 0x13020000 ) != 0xdeadbeaf ){ dbgprintf("Fail at:%d\n", __LINE__ );/*Shutdown();*/}} + +u32 SP[3]; +void DRAMInit( u32 A, u32 B ) +{ + SP[0] = 0; + + HWMAgic( 162, 999, A, B ); +} +void HWMAgic( u32 R0, u32 R1, u32 R2, u32 R3 ) +{ + u32 R10,R11,R7,R8,R9,value; + + R3 = R3 << 24; + SP[1] = R3 >> 24; + SP[2] = R0; + + R10 = (R1<<16)>>16; + + //if( read32( HW_RESETS ) & 0x800 ) + //{ + // DRAMCTRLWrite( 0x18, 0 ); + // DRAMCTRLWrite( 0x19, 0 ); + // DRAMCTRLWrite( 0x17, 0 ); + // udelay(10); + //} + + //clear32( HW_RESETS, 0x800 ); + //udelay(100); + + clear32( HW_DIFLAGS, 0x20 ); + clear32( HW_RESETS, 0x100 ); + udelay(10); + + write32( HW_RESETS, (read32( HW_RESETS ) & 0xFFFFF7FF) | 0x800 ); + udelay(5); + + write32( HW_RESETS, (read32( HW_RESETS ) & 0xFFFFFEFF) | 0x100 ); + udelay(5); + + value = read32( HW_RESETS ) & 0xFFFFFEFF; + write32( HW_RESETS, value ); + udelay(100); + + value |= 0x100; + write32( HW_RESETS, value ); + udelay(5); + +//SubD + clear32( 0xD8001C0, 0xC0000000 ); + udelay(100); + + value = read32( 0xD8001BC ); + + value&= ~0x3F; + + value|= 0; + value&=0xFFFC003F; + + value|= 0x00001240; + value&=0xF803FFFF; + + value|= 0x00100000; + value&=0xEFFFFFFF; + + value|= 0; + + write32( 0xD8001BC, value ); + udelay(100); + + value = read32( 0xD8001C0 ); + + value&=0x7FFFFFFF; + value&=0xBFFFFFFF; + value|=0x40000000; + + value&=0xEFFFFFFF; + value|=0x10000000; + + value&=0xF7FFFFFF; + value|=0x08000000; + + write32( 0xD8001C0, value ); + udelay(1000); + + value = read32( 0xD8001C0 ); + + value&=0x7FFFFFFF; + value|=0x80000000; + + write32( 0xD8001C0, value ); + udelay(1000); + + //DRAMWrite( 0x100, 0x24 ); + //udelay(5); + //DRAMWrite( 0x100, 0x20 ); + + DRAMCTRLWrite( 0x4B, 0 ); + + if( SP[0] == 1 ) + { + DRAMCTRLWrite( 0x0048, 0xD09 ); + udelay(50); + if( SP[0] == 1 ) + { + DRAMCTRLWrite( 0x0048, 0x509 ); + } else { + DRAMCTRLWrite( 0x0048, 0x50B ); + } + } else { + DRAMCTRLWrite( 0x0048, 0xD0B ); + udelay(50); + if( SP[0] != 1 ) + { + DRAMCTRLWrite( 0x0048, 0x50B ); + } else { + DRAMCTRLWrite( 0x0048, 0x509 ); + } + } + udelay(50); + +//SubJ + DRAMCTRLWrite( 0x003E, 0xF0F0 ); + DRAMCTRLWrite( 0x003F, 0xF0F0 ); + DRAMCTRLWrite( 0x0040, 0x1616 ); + DRAMCTRLWrite( 0x0041, 0x1616 ); + DRAMCTRLWrite( 0x0042, 0x1616 ); + DRAMCTRLWrite( 0x0043, 0x1616 ); + udelay(50); + +//SubO + if( SP[0] == 1 ) + { + DRAMCTRLWrite( 0x0048, 0x0109 ); + } else { + DRAMCTRLWrite( 0x0048, 0x010B ); + } + udelay(10); + + DRAMCTRLWrite( 0x0047, 0x8000 ); + DRAMCTRLWrite( 0x0027, 0x0000 ); + + DRAMWrite( 0x010C, 0x01FF ); + DRAMWrite( 0x010D, 0x0FFF ); + DRAMWrite( 0x010E, 0x0007 ); + DRAMWrite( 0x010B, 0x0001 ); + DRAMWrite( 0x0109, 0x0004 ); + DRAMWrite( 0x0108, 0x0006 ); + DRAMWrite( 0x010A, 0x0002 ); + DRAMWrite( 0x015B, 0x0EFF ); + DRAMWrite( 0x0134, 0x0008 ); + DRAMWrite( 0x0135, 0x000C ); + DRAMWrite( 0x0136, 0x0018 ); + DRAMWrite( 0x0140, 0x0006 ); + DRAMWrite( 0x015A, 0x0005 ); + DRAMWrite( 0x0137, 0x0005 ); + DRAMWrite( 0x0138, 0x0005 ); + DRAMWrite( 0x0139, 0x0005 ); + DRAMWrite( 0x013A, 0x0005 ); + DRAMWrite( 0x013B, 0x0005 ); + DRAMWrite( 0x013C, 0x0005 ); + DRAMWrite( 0x013D, 0x0005 ); + DRAMWrite( 0x013E, 0x0005 ); + DRAMWrite( 0x013F, 0x0005 ); + + DRAMCTRLWrite( 0x001C, 0x0000 ); + DRAMCTRLWrite( 0x001B, 0x0000 ); + DRAMCTRLWrite( 0x0000, 0x0000 ); + DRAMCTRLWrite( 0x0015, 0x0001 ); + DRAMCTRLWrite( 0x0016, 0x0000 ); + DRAMCTRLWrite( 0x0025, 0x0001 ); + DRAMCTRLWrite( 0x0010, 0x0000 ); + DRAMCTRLWrite( 0x0023, 0x0008 ); + DRAMCTRLWrite( 0x0001, 0x0007 ); + DRAMCTRLWrite( 0x0002, 0x0004 ); + DRAMCTRLWrite( 0x0005, 0x0007 ); + DRAMCTRLWrite( 0x0008, 0x0004 ); + DRAMCTRLWrite( 0x0009, 0x0018 ); + DRAMCTRLWrite( 0x000A, 0x001B ); + DRAMCTRLWrite( 0x0004, 0x0017 ); + DRAMCTRLWrite( 0x0021, 0x000B ); + DRAMCTRLWrite( 0x000B, 0x0009 ); + DRAMCTRLWrite( 0x000C, 0x000B ); + DRAMCTRLWrite( 0x000D, 0x0006 ); + DRAMCTRLWrite( 0x000E, 0x000C ); + DRAMCTRLWrite( 0x000F, 0x0017 ); + DRAMCTRLWrite( 0x0011, 0xFC00 ); + DRAMCTRLWrite( 0x0012, 0x001F ); + DRAMCTRLWrite( 0x0013, 0x0000 ); + DRAMCTRLWrite( 0x0014, 0x0000 ); + DRAMCTRLWrite( 0x0006, 0x0002 ); + DRAMCTRLWrite( 0x0007, 0x000A ); + DRAMCTRLWrite( 0x0022, 0x0008 ); + DRAMCTRLWrite( 0x001F, 0x1FE0 ); + DRAMCTRLWrite( 0x0020, 0x0000 ); + DRAMCTRLWrite( 0x002C, 0x7252 ); + DRAMCTRLWrite( 0x002D, 0x4A5E ); + DRAMCTRLWrite( 0x002E, 0x7BDE ); + DRAMCTRLWrite( 0x002F, 0x00DE ); + DRAMCTRLWrite( 0x0030, 0x00CC ); + DRAMCTRLWrite( 0x0031, 0x0000 ); + DRAMCTRLWrite( 0x0032, 0x00CC ); + DRAMCTRLWrite( 0x0033, 0x0000 ); + DRAMCTRLWrite( 0x0034, 0x00CC ); + DRAMCTRLWrite( 0x0035, 0x0000 ); + DRAMCTRLWrite( 0x0036, 0x08EC ); + DRAMCTRLWrite( 0x0037, 0x0000 ); + DRAMCTRLWrite( 0x0038, 0x0476 ); + DRAMCTRLWrite( 0x0039, 0x0000 ); + + if( SP[0] == 1 ) + { + DRAMCTRLWrite( 0x3A, 0x800F ); + DRAMCTRLWrite( 0x3B, 7 ); + DRAMCTRLWrite( 0x3C, 0x800F ); + DRAMCTRLWrite( 0x3D, 7 ); + + } else { + + DRAMCTRLWrite( 0x3A, 0 ); + DRAMCTRLWrite( 0x3B, 0 ); + DRAMCTRLWrite( 0x3C, 0 ); + DRAMCTRLWrite( 0x3D, 0 ); + } + + DRAMCTRLWrite( 0x45, 0 ); + + DRAMCTRLWrite( 0x100, 0 ); + udelay(5); + + DRAMCTRLWrite( 0x18, 1 ); + udelay(5); + + DRAMCTRLWrite( 0x17, 1 ); + udelay(200); + + DRAMCTRLWrite( 0x4B, 1 ); + DRAMCTRLWrite( 0x4C, 1 ); + + HWRegWriteBatch( 0xFFFF, 0x20, 0x21, 0x20, 1 ); + HWRegWriteBatch( 0x2882, 0x22, 0x23, 0x22, 5 ); + HWRegWriteBatch( 0x2882, 0x24, 0x25, 0x24, 5 ); + HWRegWriteBatch( 0x2C82, 0x22, 0x23, 0x22, 5 ); + + u32 r5 = (DRAMCTRLRead(0x29) << 0x10) >> 0x18; + + HWRegWriteBatch( 0x2882, 0x22, 0x23, 0x22, 5 ); + HWRegWriteBatch( 0x2C82, 0x24, 0x25, 0x24, 5 ); + + u32 r4 = (DRAMCTRLRead(0x29) << 0x10) >> 0x18; + + HWRegWriteBatch( 0x2882, 0x24, 0x25, 0x24, 5 ); + HWRegWriteBatch( 0x0903, 0x22, 0x23, 0x22, 1 ); + HWRegWriteBatch( 0x0903, 0x24, 0x25, 0x24, 1 ); + + DRAMCTRLWrite( 0x4C, 0 ); + DRAMCTRLWrite( 0x18, 0 ); + DRAMCTRLWrite( 0x17, 0 ); + + udelay(200); + + if( r4 == r5 ) + { + DRAMCTRLWrite( 0x18, 1 ); + udelay(5); + DRAMCTRLWrite( 0x17, 1 ); + + } else { + + DRAMCTRLWrite( 0x17, 1 ); + udelay(5); + DRAMCTRLWrite( 0x18, 1 ); + + } + udelay(200); + + DRAMCTRLWrite( 0x4B, 0 ); + + if( r4 != r5 ) + { + DRAMWrite( 0x10B, 7 ); + DRAMCTRLWrite( 0x15, 0 ); + } + + HWRegWriteBatch( 0xFFFF, 0x20, 0x21, 0x20, 2 ); + + if( r4 == r5 ) + { + HWRegWriteBatch( 0x288E, 0x22, 0x23, 0x22, 1 ); + } else { + HWRegWriteBatch( 0x288A, 0x22, 0x23, 0x22, 1 ); + } + + if( r4 == r5 ) + { + HWRegWriteBatch( 0x288E, 0x24, 0x25, 0x24, 1 ); + } + + HWRegWriteBatch( 0x903, 0x22, 0x23, 0x22, 1 ); + + if( r4 == r5 ) + { + HWRegWriteBatch( 0x903, 0x24, 0x25, 0x24, 1 ); + } + + udelay(70); + +//SubEND + HWRegWriteBatch( 0xFFFF, 0x20, 0x21, 0x20, 2 ); + HWRegWriteBatch( 0xFFFF, 2, 3, 2, 5 ); + HWRegWriteBatch( 0xFFFF, 2, 3, 2, 5 ); + + int i; + for( i=0; i < 0x10; i+=2 ) + write16( 0xD8B4000 + i, 0 ); + + write16( 0xD8B4026, 65 ); + +//GC-MODE + DRAMCTRLWrite( 0x18, 0 ); + DRAMCTRLWrite( 0x19, 1 ); + + DRAMWrite( 0x113, 631 ); + +//END + DRAMWrite( 0x165, 0x29 ); + DRAMWrite( 0x164, r5 ); + DRAMWrite( 0x165, 0x2B ); + DRAMWrite( 0x164, r4 ); +} +void MIOSInit( void ) +{ + ahb_flush_from(1); + ahb_flush_to(1); + + MIOSHWInit( 1, 1 ); +} +void MIOSDoStuff( u32 R0, u32 R1 ) +{ + if( R0 == 0 ) + { + write32( 0xD8B0010, R0 ); + } + + write32( 0xD8B0010, 0 ); + + if( R0 == 1 ) + { + if( R1 == 0 ) + { + u32 value = read32( 0xD800140 ); + value |= R0; + value &= 0xFFFF000F; + write32( 0xD800140, value ); + } + } +} +void MIOSUnkInit( void ) +{ + u32 value = read32( HW_DIFLAGS ); + + value &= 0xFFFFFEFF; + value &= ~0x80; + + write32( HW_DIFLAGS, value ); + + clear32( 0xD8001D0, 0x80000000 ); + udelay(2); + + clear32( 0xD8001D0, 0x40000000 ); + + if( SP[1] <= 1 ) + { + value = read32( 0xD8001CC ); + + value&= 0xFFFC003F; + value|= 0x00000FC0; + value&=~0x0000003F; + value&= 0xF803FFFF; + value|= 0x04640000; + + write32( 0xD8001CC, value ); + + } else { + + value = read32( 0xD8001A8 ); + + value&= 0xFFFC003F; + value|= 0x00000FC0; + value&=~0x0000003F; + value&= 0xF803FFFF; + value|= 0x04640000; + + write32( 0xD8001A8, value ); + } + + udelay(10); + + value = read32( 0xD8001D0 ); + value&= 0xBFFFFFFF; + value|= 0x40000000; + write32( 0xD8001D0, value ); + udelay(500); + + value = read32( 0xD8001D0 ); + value&= 0x7FFFFFFF; + value|= 0x80000000; + write32( 0xD8001D0, value ); + udelay(2); + +} +void MIOSEHCISub( void ) +{ + u32 v = read32( 0xD8001D8 ) & 0x7FFFFFFF; + + write32( 0xD8001D8, v ); + udelay(2); + + write32( 0xD8001D8, v & 0xBFFFFFFF ); + udelay(10); + + write32( 0xD8001D8, ( read32( 0xD8001D8 ) & 0xBFFFFFFF ) | 0x40000000 ); + udelay(50); + + write32( 0xD8001D8, ( read32( 0xD8001D8 ) & 0x7FFFFFFF ) | 0x80000000 ); + udelay(2); +} +char EHCIData[] = { + 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, + 0x05, 0x0F, 0x00, 0x00, +}; +void MIOSEHCISub2( u32 A ) +{ + if( A > 2 ) + A = 2; + + A <<= 2; + + u32 R1 = *(u8*)(EHCIData+A); + u32 R3 = *(u8*)(EHCIData+A+1); + + R1 &= 0xF; + R3 &= 0xF; + +u32 R4 = R1 << 8; + R3 = R3 << 0x17; + + R4 = R4 | R3; +u32 R2 = R1 << 2; + R2 = R2 + R1; + R4 = R4 | 0x2014; + + R2 = R2 - 4; + R2 = R2 & 0xFF; + + R2 = R2 << 8; + R2 = R2 | 0x20000; + + write32( 0xD0400B4, R4 ); + write32( 0xD0400B0, R2 ); +} +void MIOSEHCIInit( u32 A ) +{ + write32( 0xD800088, 0xFE ); + udelay(2); + + MIOSEHCISub(); + + write32( 0xD800088, 0xF6 ); + udelay(50); + + write32( 0xD800088, 0xF4 ); + udelay(1); + + write32( 0xD800088, 0xF0 ); + udelay(1); + + write32( 0xD800088, 0x70 ); + udelay(1); + + write32( 0xD800088, 0x60 ); + udelay(1); + + write32( 0xD800088, 0x40 ); + udelay(1); + + write32( 0xD800088, 0x00 ); + udelay(1); + + MIOSEHCISub2( A ); + + if( A > 1 ) + { + write32( 0xD0400A4, 0x23 ); + udelay(1); + write32( 0xD0400A4, 0x2023 ); + udelay(1); + write32( 0xD0400A4, 0x4023 ); + udelay(20); + } else { + write32( 0xD0400A4, 0x26 ); + udelay(1); + write32( 0xD0400A4, 0x2026 ); + udelay(1); + write32( 0xD0400A4, 0x4026 ); + udelay(20); + } + + write32( 0xD0400CC, 0x111 ); + +} +void MIOSEHCIInit2( void ) +{ + GetRevision( SP+1, SP ); + + if( SP[1] <= 1 ) + { + write32( 0xD8001E0, 0x65244A ); + write32( 0xD8001E4, 0x46A024 ); + return; + } + +// dbgprintf("Unsupported CPU version!\n"); + +} +void MIOSHWInit( u32 A, u32 B ) +{ + GetRevision( SP+1, SP ); + + set32( HW_EXICTRL, 1 ); + + MIOSDoStuff( SP[1], SP[0] ); + + MIOSUnkInit(); + + MIOSEHCIInit( SP[1] ); + + set32( HW_RESETS, 0x7FDFBCF ); + + MIOSEHCIInit2(); +} +void UNKInit( u32 A, u32 B ) +{ + u32 FlagA = (A << 24) >> 24; + u32 FlagB = B << 24; + + if( FlagB == 0 ) + { + if( read32( 0xD8001D0 ) & (1<31) ) + { + if( read32( 0xD8001D0 ) & (1<30) ) + return; + } + } + + u32 value = read32( HW_DIFLAGS ); + if( FlagA ) + { + value &= 0xFFFFFEFF; + } else { + value &= 0xFFFFFEFF; + value |= 0x100; + } + + value &= ~0x80; + + write32( HW_DIFLAGS, value ); + + + clear32( 0xD8001D0, 0x80000000 ); + udelay(2); + + clear32( 0xD8001D0, 0x40000000 ); + + if( FlagA ) + { + value = read32( 0xD8001CC ); + + value&= 0xFFFC003F; + value|= 0x00000FC0; + value&=~0x0000003F; + value&= 0xF803FFFF; + value|= 0x04640000; + + write32( 0xD8001CC, value ); + + } else { + + clear32( 0xD8001D0, 0x10000000 ); + + value = read32( 0xD8001CC ); + + value&= 0xFFFC003F; + value|= 0x0000FFC0; + value&=~0x0000003F; + value|= 0x0000000E; + value&= 0xF803FFFF; + value|= 0x04B00000; + + write32( 0xD8001CC, value ); + } + + udelay(10); + + value = read32( 0xD8001D0 ); + value&= 0xBFFFFFFF; + value|= 0x40000000; + write32( 0xD8001D0, value ); + udelay(500); + + value = read32( 0xD8001D0 ); + value&= 0x7FFFFFFF; + value|= 0x80000000; + write32( 0xD8001D0, value ); + udelay(2); + +} +void BootPPC( void ) +{ + u32 BootCode[] = // 16 * 4 + { + 0x3C600000, + 0x60633400, + 0x7C7A03A6, + 0x38600000, + 0x7C7B03A6, + 0x4C000064, + + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + } ; + + u32 cookie = read32(HW_EXICTRL); + + //Enable EXI + set32( HW_EXICTRL, 1 ); + + //Upload BootCode + int i; + for( i=0; i < 16; ++i ) + write32( 0xd806840 + i * 4, BootCode[i] ); + + u32 val = read32( 0xd806840 + 4 * 16 - 4 ); + + write32( HW_EXICTRL, cookie ); + val = read32(HW_EXICTRL); +} +extern u8 *RAM; +//void MEM2Switch( u32 A ) +//{ +// clear32( HW_RESETS, 0x48000 ); +// clear32( 0xD800184, 0x428E ); +// +// if( A ) +// { +// +// DRAMWrite( 0x10B, 7 ); +// DRAMCTRLWrite( 0x15, 0 ); +// DRAMCTRLWrite( 0x18, 1 ); +// DRAMCTRLWrite( 0x19, 0 ); +// DRAMCTRLWrite( 0x4A, 0x0E ); +// DRAMCTRLWrite( 0x0F, 0x08 ); +// DRAMCTRLWrite( 0x03, 0x0E ); +// +// DRAMCTRLWrite( 0x49, 1 ); +// udelay(2); +// +// DRAMWrite( 0x113, 631 ); +// +// } else { +// DRAMWrite( 0x10B, 7 ); +// DRAMCTRLWrite( 0x15, 0x00 ); +// DRAMCTRLWrite( 0x18, 0x00 ); +// DRAMCTRLWrite( 0x19, 0x01 ); +// DRAMCTRLWrite( 0x4A, 0x00 ); +// DRAMCTRLWrite( 0x0F, 0x17 ); +// DRAMCTRLWrite( 0x03, 0x00 ); +// +// DRAMCTRLWrite( 0x49, 0x00 ); +// udelay(2); +// +// DRAMWrite( 0x113, 947 ); +// } +// set32( HW_RESETS, 0x48000 ); +// set32( 0xD800184, 0x428E ); +// +// udelay(5000); +// +// +//} +void MEMInitLow( void ) +{ + write32( 0x3118, 0x04000000 ); + write32( 0x311C, 0x04000000 ); + + write32( 0x3124, 0x90000800 ); + write32( 0x3128, 0x935E0000 ); + write32( 0x3130, 0x935E0000 ); + + write32( 0x3138, 0x00000101 ); + write32( 0x3140, 0x00000707 ); + + write32( 0x3144, 0x00082209 ); + + write32( 0x3158, 0xCAFEBABE ); + + write32( 0x3114, 0xDEADBEEF ); + + write32( 0x312C, 0xDEADBEEF ); + write32( 0x313C, 0xDEADBEEF ); + + write32( 0x3148, 0xDEADBEEF ); + write32( 0x314C, 0xDEADBEEF ); + + write32( 0x3154, 0xDEADBEEF ); + write32( 0x3150, 0xDEADBEEF ); + + write32( 0x315C, 0xDEADBEEF ); + write32( 0x3160, 0xDEADBEEF ); + + write32( 0x3100, 0x01800000 ); + write32( 0x3104, 0x01800000 ); + write32( 0x3108, 0x81800000 ); + write32( 0x310C, 0x00000000 ); + write32( 0x3110, 0x81800000 ); + + write32( 0x3120, 0x93600000 ); + write32( 0x3134, 0x93600000 ); + + write32( 0x0028, 0x01800000 ); + write32( 0x00F0, 0x01800000 ); + + write32( 0x0030, 0x00000000 ); + write32( 0x0034, 0x81800000 ); + + write32( 0x00D0, 16*1024*1024 ); //Set ARAM size + + write16( 0x0D8B4200, 0x00000000 ); + udelay(1); + + write32( 0x00000080, 0x09142001 ); //some date? 14th SEP 2001 + + clear32( HW_RESETS, 0x00048000 ); + clear32( 0xD800184, 0x0000438E ); + + DRAMWrite( 0x10B, 7 ); + DRAMCTRLWrite( 0x15, 0 ); + DRAMCTRLWrite( 0x18, 1 ); + DRAMCTRLWrite( 0x19, 0 ); + DRAMCTRLWrite( 0x4A, 0x0E ); + DRAMCTRLWrite( 0x0F, 0x08 ); + DRAMCTRLWrite( 0x03, 0x0E ); + //DRAMCTRLWrite( 0x49, 0x0E ); + //udelay(2); + + //DRAMCTRLWrite( 0x49, 0x0F ); + //udelay(2); + + DRAMWrite( 0x113, 631 ); + + set32( HW_RESETS, 0x00048000 ); + set32( 0xD800184, 0x0000438E ); + + write32( 0x30F8, 0 ); + ahb_flush_to( 1 ); + +//RegisterSet + clear32( HW_RESETS, 0x48F200 ); + udelay(1); + + u32 val = read32( HW_DIFLAGS ); + + val &= ~0x01; + //val &= ~0x02; // also required to be changed + //val &= ~0x04; //this disables bit 14 of the IRQ Flags + val &= ~0x08; + + val |= 0x10; + val |= 0x20; + + val &= 0xFFFFFEFF; + val &= ~0x80; + + val &= 0xFFFFFDFF; + val &= 0xFFFFFBFF; + val &= 0xFFFFF7FF; + + val &= 0xFFFFDFFF; + val &= 0xFFFFBFFF; + val &= 0xFFFF7FFF; + + val &= 0xFFFEFFFF; + val &= 0xFFFDFFFF; + val &= 0xFFFBFFFF; + + write32( HW_DIFLAGS, val ); + udelay(1); + + set32( HW_RESETS, 0x48F200 ); + udelay(1); + +//SetUP GPIOs + clear32( 0xD8000E0, 0xC120 ); + set32( 0xD8000E0, read32( 0xD8000C0 ) ); + write32( 0xD8000FC, 0 ); + set32( 0xD8000E4, 0xFFDF3E ); + + EXIControl(1); + + BootPPC(); + + write32( HW_DIFLAGS, (read32(HW_DIFLAGS) & 0xFFEFFFFF) | 0x100000 ); + + EXIControl(0); + + write32( 0xD800034, 0x40000000 ); + + clear32( HW_RESETS, 0x10 | 0x20 ); + udelay(15); + + set32( HW_RESETS, 0x20 ); + udelay(150); + + set32( HW_RESETS, 0x10 ); + udelay(200); + + set32( 0xD8000E0, 0xCE0000 ); + + udelay(200); + + while( read32( 0x30F8 ) == 0 ) + ahb_flush_from(0); + + val = read32(HW_DIFLAGS); + + val |= 0x00000040; + val &= ~0x00200000; + val |= 0x00200000; + val &= ~0x00400000; + val |= 0x00400000; + val &= ~0x00001000; + + write32( HW_DIFLAGS, val ); + udelay(1); + + //write32( 0x30F8, 0 ); + //ahb_flush_to(1); + + clear32( HW_DIFLAGS, 0x180000 ); +} diff --git a/HW.h b/HW.h new file mode 100644 index 0000000..8b6a47f --- /dev/null +++ b/HW.h @@ -0,0 +1,89 @@ +#ifndef _HW_ +#define _HW_ + +#include "string.h" +#include "global.h" +#include "memory.h" +#include "ipc.h" + +#include "alloc.h" +#include "dip.h" +#include "GCPad.h" + +#define P2C(x) ((x)&0x7FFFFFFF) + +#define HW_BASE 0x0d800000 +#define HW_MEMIRR (HW_BASE+0x60) +#define HW_AHBPROT (HW_BASE+0x64) + +#define HW_DDRCTRL_ADDR (HW_BASE+0x74) +#define HW_DDRCTRL_VAL (HW_BASE+0x76) + +#define HW_GPIO_ENABLE (HW_BASE+0xDC) +#define HW_GPIO_OUT (HW_BASE+0xE0) +#define HW_GPIO_DIR (HW_BASE+0xE4) +#define HW_GPIO_IN (HW_BASE+0xE8) +#define HW_GPIO_INTLVL (HW_BASE+0xEC) +#define HW_GPIO_INTFLAG (HW_BASE+0xF0) +#define HW_GPIO_INTMASK (HW_BASE+0xF4) +#define HW_GPIO_INMIR (HW_BASE+0xF8) +#define HW_GPIO_OWNER (HW_BASE+0xFC) + +#define HW_ACRPLLSYS (HW_BASE+0x1B0) +#define HW_ACRPLLSYSEXT (HW_BASE+0x1B4) + +#define DIFLAGS_PPCBOOT (1<<20) + + +#define IRQ_TIMER (1<<0) +#define IRQ_NAND (1<<1) +#define IRQ_AES (1<<2) +#define IRQ_SHA1 (1<<3) +#define IRQ_EHCI (1<<4) +#define IRQ_OHCI0 (1<<5) +#define IRQ_OHCI1 (1<<6) +#define IRQ_SDHC (1<<7) +#define IRQ_WIFI (1<<8) +#define IRQ_GPIO1B (1<<10) +#define IRQ_GPIO1 (1<<11) +#define IRQ_RESET (1<<17) +#define IRQ_PPCIPC (1<<30) +#define IRQ_IPC (1<<31) + +#define GPIO_POWER (1<<1) +#define GPIO_EJECT (1<<9) + +extern void DRAMCTRLWrite( u32 Register, u32 Value ); +extern u32 DRAMCTRLRead( u32 Register ); + +void EHCIInit( void ); + +void EXIControl( u32 value ); + +void MIOSHWInit( u32 A, u32 B ); +void MIOSInit( void ); +void MEMInitLow( void ); +void BootPPC( void ); +void UNKInit( u32 A, u32 B ); +void DRAMInit( u32 A, u32 B ); +void ChangeClock( void ); +void PPCReset( void ); +void HWResetDisable( void ); +void HWResetEnable( void ); +void HW_184( void ); +void HW_184_2( void ); + +void GetRevision( u32 *Version, u32 *Revision ); + +void HWMAgic( u32 R0, u32 R1, u32 R2, u32 R3 ); + +u32 DRAMRead( u32 ValueA ); +void DRAMWrite( u32 ValueA, u32 ValueB ); + +u32 RegRead( u32 Register ); +void RegWrite( u32 Register, u32 Value ); +void HWRegWriteBatch( u32 A, u32 B, u32 C, u32 D, u32 delay ); + +void Shutdown( void ); + +#endif diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ce9d262 --- /dev/null +++ b/Makefile @@ -0,0 +1,53 @@ +PREFIX = $(DEVKITARM)/bin/arm-eabi- +CC = $(PREFIX)gcc +AS = $(PREFIX)as +LD = $(PREFIX)gcc +SSTRIP = $(DEVKITARM)/bin/arm-eabi-strip + +CFLAGS = -mbig-endian -fomit-frame-pointer -O2 -Wall -I. -mcpu=arm926ej-s -mthumb +CFLAGS += -fno-builtin-memcpy -fno-builtin-memset -fno-builtin-toupper -fno-builtin-memcmp -fno-builtin-malloc -fno-builtin-free + +ASFLAGS = -mbig-endian -mcpu=arm926ej-s + +LDFLAGS = -nostartfiles -nodefaultlibs -mbig-endian -Wl,-T,iosmodule.ld,-Map,iosmodule.map -n + +LIBS = -lgcc + +TARGET = iosmodule.elf +OBJECTS = start.o utils_asm.o HW.o Card.o memory.o memory_asm.o Config.o common.o ff.o diskio.o alloc.o Drive.o DVD.o dip.o Patches.o main.o vsprintf.o string.o tiny_ehci_glue.o usb_os.o +.PHONY: FORCE + +all: $(TARGET) + +$(TARGET) : iosmodule.ld $(OBJECTS) + @echo "LD $@" + @$(LD) $(LDFLAGS) $(OBJECTS) $(LIBS) -o $@ + @echo $(SSTRIP) -s $@ + + +%.o : %.s + @echo "AS $@" + @$(CC) $(CFLAGS) -D_LANGUAGE_ASSEMBLY -c -x assembler-with-cpp -o $@ $< + +%.o : %.S + @echo "AS $@" + @$(CC) $(CFLAGS) -D_LANGUAGE_ASSEMBLY -c -x assembler-with-cpp -o $@ $< + +%.o : %.c + @echo "CC $@" + @$(CC) $(CFLAGS) -c -o $@ $< + +%.d: %.c + @echo "DEP $@" + @set -e; $(CC) -M $(CFLAGS) $< \ + | sed 's?\($*\)\.o[ :]*?\1.o $@ : ?g' > $@; \ + [ -s $@ ] || rm -f $@ + +%.d: %.S + @echo "DEP $@" + @touch $@ + +-include $(OBJECTS:.o=.d) + +clean: + -rm -f *.elf *.o *.bin *.d *.map diff --git a/Patches.c b/Patches.c new file mode 100644 index 0000000..df9abce --- /dev/null +++ b/Patches.c @@ -0,0 +1,874 @@ +#include "Patches.h" + +#include "CardPatches.c" +#include "DVDPatches.c" +#include "FwritePatches.c" +#include "CheatCode.c" + +extern u32 DOLSize; + +unsigned char OSReportDM[] = +{ + 0x7C, 0x08, 0x02, 0xA6, 0x90, 0x01, 0x00, 0x04, 0x90, 0xE1, 0x00, 0x08, 0x3C, 0xE0, 0xC0, 0x00, + 0x90, 0x67, 0x18, 0x60, 0x90, 0x87, 0x18, 0x64, 0x90, 0xA7, 0x18, 0x68, 0x90, 0xC7, 0x18, 0x6C, + 0x90, 0xE7, 0x18, 0x70, 0x91, 0x07, 0x18, 0x74, 0x80, 0x07, 0x18, 0x60, 0x7C, 0x00, 0x18, 0x00, + 0x41, 0x82, 0xFF, 0xF8, 0x80, 0xE1, 0x00, 0x08, 0x80, 0x01, 0x00, 0x04, 0x7C, 0x08, 0x03, 0xA6, + 0x4E, 0x80, 0x00, 0x20, +} ; +// Audio streaming replacement functions copied from Swiss r92 +u32 __dvdLowAudioStatusNULL[17] = { + // execute function(1); passed in on r4 + 0x9421FFC0, // stwu sp, -0x0040 (sp) + 0x7C0802A6, // mflr r0 + 0x90010000, // stw r0, 0 (sp) + 0x7C8903A6, // mtctr r4 + 0x3C80CC00, // lis r4, 0xCC00 + 0x2E830000, // cmpwi cr5, r3, 0 + 0x4196000C, // beq- cr5, +0xC ? + 0x38600001, // li r3, 1 + 0x48000008, // b +0x8 ? + 0x38600000, // li r3, 0 + 0x90646020, // stw r3, 0x6020 (r4) + 0x38600001, // li r3, 1 + 0x4E800421, // bctrl + 0x80010000, // lwz r0, 0 (sp) + 0x7C0803A6, // mtlr r0 + 0x38210040, // addi sp, sp, 64 + 0x4E800020 // blr +}; + +u32 __dvdLowAudioConfigNULL[10] = { + // execute callback(1); passed in on r5 without actually touching the drive! + 0x9421FFC0, // stwu sp, -0x0040 (sp) + 0x7C0802A6, // mflr r0 + 0x90010000, // stw r0, 0 (sp) + 0x7CA903A6, // mtctr r5 + 0x38600001, // li r3, 1 + 0x4E800421, // bctrl + 0x80010000, // lwz r0, 0 (sp) + 0x7C0803A6, // mtlr r0 + 0x38210040, // addi sp, sp, 64 + 0x4E800020 // blr +}; + +u32 __dvdLowReadAudioNULL[] = { + // execute callback(1); passed in on r6 without actually touching the drive! + 0x9421FFC0, // stwu sp, -0x0040 (sp) + 0x7C0802A6, // mflr r0 + 0x90010000, // stw r0, 0 (sp) + 0x7CC903A6, // mtctr r6 + 0x38600001, // li r3, 1 + 0x4E800421, // bctr; + 0x80010000, // lwz r0, 0 (sp) + 0x7C0803A6, // mtlr r0 + 0x38210040, // addi sp, sp, 64 + 0x4E800020 +}; + +u32 __GXSetVAT_patch[31] = { + /*0x8122ce00,*/ 0x39400000, 0x896904f2, 0x7d284b78, + 0x556007ff, 0x41820050, 0x38e00008, 0x3cc0cc01, + 0x98e68000, 0x61400070, 0x61440080, 0x61430090, + 0x98068000, 0x38000000, 0x80a8001c, 0x90a68000, + 0x98e68000, 0x98868000, 0x8088003c, 0x90868000, + 0x98e68000, 0x98668000, 0x8068005c, 0x90668000, + 0x98068000, 0x556bf87f, 0x394a0001, 0x39080004, + 0x4082ffa0, 0x38000000, 0x980904f2, 0x4e800020 +}; + + +FuncPattern FPatterns[] = +{ + { 0xCC, 17, 10, 5, 3, 2, DVDInquiryAsync, sizeof(DVDInquiryAsync), "DVDInquiryAsync", 0, 0 }, + { 0xC8, 16, 9, 5, 3, 3, DVDSeekAbsAsyncPrio, sizeof(DVDSeekAbsAsyncPrio), "DVDSeekAbsAsyncPrio", 0, 0 }, + { 0xD4, 13, 8, 11, 2, 7, (u8*)NULL, 0xdead0004, "AIResetStreamSampleCount", 0, 0 }, + + { 0x94, 18, 10, 2, 0, 2, (u8*)__dvdLowReadAudioNULL, sizeof(__dvdLowReadAudioNULL), "DVDLowReadAudio", 0, 0 }, + { 0x88, 18, 8, 2, 0, 2, (u8*)__dvdLowAudioStatusNULL, sizeof(__dvdLowAudioStatusNULL), "DVDLowAudioStatus", 0, 0 }, + { 0x98, 19, 8, 2, 1, 3, (u8*)__dvdLowAudioConfigNULL, sizeof(__dvdLowAudioConfigNULL), "DVDLowAudioConfig", 0, 0 }, + + { 0x308, 40, 18, 10, 23, 17, patch_fwrite_GC, sizeof(patch_fwrite_GC), "__fwrite A", 1, 0 }, + { 0x338, 48, 20, 10, 24, 16, patch_fwrite_GC, sizeof(patch_fwrite_GC), "__fwrite B", 1, 0 }, + { 0x2D8, 41, 17, 8, 21, 13, patch_fwrite_GC, sizeof(patch_fwrite_GC), "__fwrite C", 1, 0 }, + + { 0x98, 8, 3, 0, 3, 5, (u8*)NULL, 0xdead0001, "__GXSetVAT", 0, 0 }, + + { 0x3A8, 86, 13, 27, 17, 24, (u8*)NULL, 0xdead000B, "PADRead A", 2, 0 }, + { 0x2FC, 73, 8, 23, 16, 15, (u8*)NULL, 0xdead000B, "PADRead B", 2, 0 }, + { 0x3B0, 87, 13, 27, 17, 25, (u8*)NULL, 0xdead000B, "PADRead C", 2, 0 }, + { 0x334, 78, 7, 20, 17, 19, (u8*)NULL, 0xdead000B, "PADRead D", 2, 0 }, + +}; + +FuncPattern CPatterns[] = +{ + { 0x14C, 28, 12, 7, 12, 4, CARDFreeBlocks, sizeof(CARDFreeBlocks), "CARDFreeBlocks A", 1, 0 }, + { 0x11C, 24, 10, 7, 10, 4, CARDFreeBlocks, sizeof(CARDFreeBlocks), "CARDFreeBlocks B", 1, 0 }, + { 0xC0, 22, 5, 2, 5, 10, CARDGetSerialNo, sizeof(CARDGetSerialNo),"CARDGetSerialNo", 0, 0 }, + { 0x84, 12, 5, 3, 4, 2, CARDGetEncoding, sizeof(CARDGetEncoding),"CARDGetEncoding", 0, 0 }, + { 0x80, 11, 5, 3, 4, 2, CARDGetMemSize, sizeof(CARDGetMemSize), "CARDGetMemSize", 0, 0 }, + + { 0x94, 11, 6, 3, 5, 4, __CARDSync, sizeof(__CARDSync), "__CARDSync", 0, 0 }, + { 0x50, 6, 3, 2, 2, 2, CARDCheck, sizeof(CARDCheck), "CARDCheck", 0, 0 }, + //{ 0x24, 4, 2, 1, 0, 2, CARDCheckAsync, sizeof(CARDCheckAsync), "CARDCheckAsync", 0, 0 }, + { 0x58C, 82, 11, 18, 41, 57, CARDCheckEX, sizeof(CARDCheckEX), "CARDCheckExAsync", 0, 0 }, + { 0x34, 4, 2, 1, 2, 2, CARDProbe, sizeof(CARDProbe), "CARDProbe", 2, 0 }, + //{ 0x1C, 2, 2, 1, 0, 2, CARDProbe, sizeof(CARDProbe), "CARDProbe B", 2, 0 }, //This is causing more trouble than a hack... + { 0x178, 20, 6, 6, 20, 4, CARDProbeEX, sizeof(CARDProbeEX), "CARDProbeEx A", 3, 0 }, + { 0x198, 22, 6, 5, 19, 4, CARDProbeEX, sizeof(CARDProbeEX), "CARDProbeEx B", 3, 0 }, + { 0x160, 17, 6, 5, 18, 4, CARDProbeEX, sizeof(CARDProbeEX), "CARDProbeEx C", 3, 0 }, + { 0x19C, 32, 14, 11, 12, 3, CARDMountAsync, sizeof(CARDMountAsync), "CARDMountAsync A", 4, 0 }, + { 0x184, 30, 14, 11, 10, 3, CARDMountAsync, sizeof(CARDMountAsync), "CARDMountAsync B", 4, 0 }, + { 0xA8, 15, 8, 6, 3, 2, CARDCheck, sizeof(CARDCheck), "CARDUnMount", 0, 0 }, + + { 0x174, 23, 6, 7, 14, 5, CARDOpen, sizeof(CARDOpen), "CARDOpen A", 5, 0 }, + { 0x118, 14, 6, 6, 11, 4, CARDOpen, sizeof(CARDOpen), "CARDOpen B", 5, 0 }, + { 0x170, 23, 6, 7, 14, 5, CARDOpen, sizeof(CARDOpen), "CARDOpen C", 5, 0 }, + { 0x15C, 27, 6, 5, 15, 6, CARDFastOpen, sizeof(CARDFastOpen), "CARDFastOpen A", 11, 0 }, + { 0x100, 20, 10, 4, 10, 4, CARDFastOpen, sizeof(CARDFastOpen), "CARDFastOpen B", 11, 0 }, + { 0x50, 8, 4, 2, 2, 3, CARDClose, sizeof(CARDClose), "CARDClose", 0, 0 }, + { 0x21C, 44, 6, 13, 19, 12, CARDCreate, sizeof(CARDCreate), "CARDCreateAsync A", 6, 0 }, + { 0x214, 42, 6, 13, 19, 12, CARDCreate, sizeof(CARDCreate), "CARDCreateAsync B", 6, 0 }, + { 0x10C, 25, 6, 9, 9, 5, CARDDelete, sizeof(CARDDelete), "CARDDeleteAsync A", 12, 0 }, + { 0x10C, 25, 6, 9, 9, 5, CARDDelete, sizeof(CARDDelete), "CARDDeleteAsync C", 12, 0 }, + { 0x128, 24, 7, 9, 12, 5, CARDFastDelete, sizeof(CARDFastDelete), "CARDFastDelete", 0, 0 }, + { 0x144, 27, 3, 8, 10, 9, CARDRead, sizeof(CARDRead), "CARDReadAsync A", 7, 0 }, + { 0x140, 30, 7, 7, 10, 10, CARDRead, sizeof(CARDRead), "CARDReadAsync B", 7, 0 }, + { 0x140, 27, 3, 8, 10, 9, CARDRead, sizeof(CARDRead), "CARDReadAsync C", 7, 0 }, + { 0x110, 24, 4, 8, 9, 6, CARDWrite, sizeof(CARDWrite), "CARDWriteAsync A", 8, 0 }, + { 0x10C, 23, 4, 8, 9, 6, CARDWrite, sizeof(CARDWrite), "CARDWriteAsync B", 8, 0 }, + { 0x1F8, 37, 3, 17, 18, 9, CARDRename, sizeof(CARDRename), "CARDRenameAsync", 0, 0 }, + { 0x128, 25, 9, 9, 6, 5, CARDGetStats, sizeof(CARDGetStats), "CARDGetStatus A", 9, 0 }, + { 0x110, 25, 9, 8, 6, 5, CARDGetStats, sizeof(CARDGetStats), "CARDGetStatus B", 9, 0 }, + { 0x124, 25, 9, 9, 6, 5, CARDGetStats, sizeof(CARDGetStats), "CARDGetStatus C", 9, 0 }, + { 0x170, 29, 9, 9, 12, 5, CARDSetStats, sizeof(CARDSetStats), "CARDSetStatusAsync A", 10, 0 }, + { 0x16C, 29, 9, 9, 12, 5, CARDSetStats, sizeof(CARDSetStats), "CARDSetStatusAsync B", 10, 0 }, +}; + +u32 CardLowestOff = 0; + + +void PatchB( u32 dst, u32 src ) +{ + u32 newval = (dst - src); + newval&= 0x03FFFFFC; + newval|= 0x48000000; + write32( src, newval ); +} +void PatchBL( u32 dst, u32 src ) +{ + u32 newval = (dst - src); + newval&= 0x03FFFFFC; + newval|= 0x48000001; + write32( src, newval ); +} +void MPattern( u8 *Data, u32 Length, FuncPattern *FunctionPattern ) +{ + u32 i; + + memset( FunctionPattern, 0, sizeof(FuncPattern) ); + + for( i = 0; i < Length; i+=4 ) + { + u32 word = read32( (u32)Data + i ); + + if( (word & 0xFC000003) == 0x48000001 ) + FunctionPattern->FCalls++; + + if( (word & 0xFC000003) == 0x48000000 ) + FunctionPattern->Branch++; + if( (word & 0xFFFF0000) == 0x40800000 ) + FunctionPattern->Branch++; + if( (word & 0xFFFF0000) == 0x41800000 ) + FunctionPattern->Branch++; + if( (word & 0xFFFF0000) == 0x40810000 ) + FunctionPattern->Branch++; + if( (word & 0xFFFF0000) == 0x41820000 ) + FunctionPattern->Branch++; + + if( (word & 0xFC000000) == 0x80000000 ) + FunctionPattern->Loads++; + if( (word & 0xFF000000) == 0x38000000 ) + FunctionPattern->Loads++; + if( (word & 0xFF000000) == 0x3C000000 ) + FunctionPattern->Loads++; + + if( (word & 0xFC000000) == 0x90000000 ) + FunctionPattern->Stores++; + if( (word & 0xFC000000) == 0x94000000 ) + FunctionPattern->Stores++; + + if( (word & 0xFF000000) == 0x7C000000 ) + FunctionPattern->Moves++; + + if( word == 0x4E800020 ) + break; + } + + FunctionPattern->Length = i; +} +bool CPattern( FuncPattern *FPatA, FuncPattern *FPatB ) +{ + if( memcmp( FPatA, FPatB, sizeof(u32) * 6 ) == 0 ) + return true; + else + return false; +} +void DoCardPatches( char *ptr, u32 size, u32 SectionOffset ) +{ + u32 i,j,k,offset,fail,FoundCardFuncStart=0; + + dbgprintf("DoCardPatches( 0x%p, %d, 0x%X)\n", ptr, size, SectionOffset ); + + for( i=0; i < size; i+=4 ) + { + if( read32( (u32)ptr + i) == 0x7C630214 && read32( (u32)ptr + i + 4) == 0x806300B8 ) + { + dbgprintf("Found [CARDGetXferredBytes] @ 0x%08X\n", (u32)ptr + i - 12 ); + memcpy( ptr + i - 12, CARDGetXferredBytes, sizeof(CARDGetXferredBytes) ); + } + + if( read32( (u32)ptr + i ) != 0x7C0802A6 ) // MFLR + continue; + + FuncPattern fp; + MPattern( (u8*)(ptr+i), size, &fp ); + + for( j=0; j < sizeof(CPatterns)/sizeof(FuncPattern); ++j ) + { + if( CPatterns[j].PatchLength == 0 ) + continue; + + if( CPatterns[j].Found ) // Skip already found patches + continue; + + if( CPattern( &fp, &(CPatterns[j]) ) ) + { + if( CPatterns[j].Patch == CARDFreeBlocks ) + { + if( CardLowestOff == 0 ) + { + dbgprintf("CardLowestOff:0x%08X\n", i ); + CardLowestOff = i; + } + + //Check for CARDGetResultCode which is always (when used) above CARDFreeBlocks + if( read32( (u32)ptr + i - 0x30 ) == 0x2C030000 ) + { + dbgprintf("Found [CARDGetResultCode] @ 0x%08X\n", (u32)ptr + i - 0x30 + SectionOffset ); + memcpy( ptr + i - 0x30, CARDGetResultCode, sizeof(CARDGetResultCode) ); + } + + FoundCardFuncStart = 1; + } + + if( FoundCardFuncStart == 0 ) + continue; + + dbgprintf("Found [%s] @ 0x%08X\n", CPatterns[j].Name, (u32)ptr + i + SectionOffset ); + CPatterns[j].Found = (u32)ptr + i; + + // If this is a patch group set all others of this group as found aswell + if( CPatterns[j].Group ) + { + for( k=0; k < sizeof(CPatterns)/sizeof(FuncPattern); ++k ) + { + if( CPatterns[k].Group == CPatterns[j].Group ) + { + if( !CPatterns[k].Found ) //Don't overwrite the offset! + CPatterns[k].Found = -1; // Usually this holds the offset, to determinate it from a REALLY found pattern we set it -1 which still counts a logical TRUE + + //dbgprintf("Setting [%s] to found!\n", CPatterns[k].Name ); + } + } + } + + //If by now no CARDProbe is found it won't be so set it to found to prevent CARDProbe B false hits + if( CPatterns[j].Patch == CARDRead ) + { + for( k=0; k < sizeof(CPatterns)/sizeof(FuncPattern); ++k ) + { + if( CPatterns[k].Patch == CARDProbe ) + { + if( !CPatterns[k].Found ) //Don't overwrite the offset! + CPatterns[k].Found = -1; + } + } + } + + + if( strstr( CPatterns[j].Name, "Async" ) != NULL ) + { + //dbgprintf("Async!\n"); + + //Most games only use the normal functions so we patch a branch over to async and clear the CB + //Find function call to our function + + offset = (u32)ptr + i; + fail = 0; + + while(fail < 3) + { + //dbgprintf("[%08X] %08X %08X(%08X)\n", offset, read32( offset ) & 0xFC000003,read32( offset ) & 0x03FFFFFC ,(read32( offset ) & 0x03FFFFFC ) + offset); + if( (read32( offset ) & 0xFC000003 ) == 0x48000001 ) + { + if( (((read32( offset ) & 0x03FFFFFC ) + offset) & 0x03FFFFFC) == (u32)ptr+i ) + break; + } + + if( read32( offset ) == 0x4E800020 ) + fail++; + + offset+=4; + } + + if( fail < 3 ) + { + dbgprintf("Found function call to [%s] @ 0x%08X\n", CPatterns[j].Name, offset + SectionOffset ); + + //Now find function start + offset -= 4; + while(1) + { + if( read32( offset ) == 0x7C0802A6 ) + break; + + offset-=4; + } + + dbgprintf("Found function start of [%s(Sync)] @ 0x%08X\n", CPatterns[j].Name, offset + SectionOffset ); + + //This patches a li rX, 0 before the Async function call for the Sync call + //Since this register of the cb is different per function we do this haxx + if( (read32( offset + 0x04 ) & 0x0000F000 ) == 0x00008000 ) // lis + { + write32( offset, read32( offset + 0x0C ) & 0xFBE00000 ); + + //Forge a branch to the async function + + offset += 4; + + u32 newval = ((u32)ptr + i) - offset; + newval&= 0x03FFFFFC; + newval|= 0x48000000; + write32( offset, newval ); + } else { + dbgprintf("Unhandled Async cb case!\n"); + } + + } else { + dbgprintf("No sync function found!\n"); + } + + memcpy( ptr + i, CPatterns[j].Patch, CPatterns[j].PatchLength ); + + } else { + memcpy( ptr + i, CPatterns[j].Patch, CPatterns[j].PatchLength ); + } + } + } + } + + for( j=0; j < sizeof(CPatterns)/sizeof(FuncPattern); ++j ) + { + if( CPatterns[j].Found == 0 ) + dbgprintf("Pattern %s not found!\n", CPatterns[j].Name ); + } + + return; +} +void DoPatches( char *ptr, u32 size, u32 SectionOffset ) +{ + u32 i=0,j=0,k=0,value; + u32 PatchCount = 0; + + dbgprintf("DoPatches( 0x%p, %d, 0x%X)\n", ptr, size, SectionOffset ); + + // HACK: PokemonXD and Pokemon Colosseum low memory clear patch + if(( (read32(0)>>8) == 0x475858 ) || ( (read32(0)>>8) == 0x474336 )) + { + // patch out initial memset(0x1800, 0, 0x1800) + if( (read32(0) & 0xFF) == 0x4A ) // JAP + write32( 0x560C, 0x60000000 ); + else // EUR/USA + write32( 0x5614, 0x60000000 ); + + // patch memset to jump to test function + write32(0x00005498, 0x4BFFABF0); + + // patch in test < 0x3000 function + write32(0x00000088, 0x3D008000); + write32(0x0000008C, 0x61083000); + write32(0x00000090, 0x7C044000); + write32(0x00000094, 0x4180542C); + write32(0x00000098, 0x90E40004); + write32(0x0000009C, 0x48005400); + + // skips __start init of debugger mem + write32(0x00003194, 0x48000028); + } + + + // Reset Found + for( k=0; k < sizeof(FPatterns)/sizeof(FuncPattern); ++k ) + FPatterns[k].Found = 0; + + + if( ConfigGetConfig(DML_CFG_NMM) ) + DoCardPatches( ptr, size, SectionOffset ); + +//Note: ORing the values prevents an early break out when a single patterns has multiple hits + PatchCount=0; + + for( i=0; i < size; i+=4 ) + { + if( (PatchCount & 1) == 0 ) + { + if( read32( (u32)ptr + i ) == 0x3C00A800 ) // Loader + { + int j=0; + while( read32( (u32)ptr + i - j ) != 0x7C0802A6 ) // Seek to start of function + j+=4; + + //Check if there is a lis %rX, 0xCC00 in this function + //At least Sunshine has one false hit on lis r3,0xA800 + int k=0; + while( 1 ) + { + if( read32( (u32)ptr + i + k - j ) == 0x4E800020 ) + break; + + if( (read32( (u32)ptr + i + k - j ) & 0xF81FFFFF) == 0x3800CC00 ) + break; + + k += 4; + } + + if( read32( (u32)ptr + i + k - j ) == 0x4E800020 ) + { + //dbgprintf("Patch:No 0xCC00 found around:%08X\n", (u32)ptr+i); + continue; + } + + k = 0; + while(1) + { + u32 val = read32( (u32)ptr + i + k ); + + if( (val & 0xFFFF ) == 0xCC00 ) + { + write32( (u32)ptr + i + k, (val&0xFFFF0000) | 0xC000 ); + //dbgprintf("Patch:lis 0x%08X\n", (u32)ptr + i + k + SectionOffset ); + continue; + } + + if( (val & 0xFC00FF00) == 0x90006000 ) + { + write32( (u32)ptr + i + k, (val&0xFFFF00FF) | 0x2F00 ); + //dbgprintf("Patch:stw 0x%08X\n", (u32)ptr + i + k + SectionOffset ); + continue; + } + + if( read32( (u32)ptr + i + k ) == 0x4E800020 ) + break; + + k+=4; + } + + write32( (u32)ptr + i, 0x3C00A700 ); + + dbgprintf("Patch:Found [DVDLowRead]: 0x%08X\n", (u32)ptr + i + SectionOffset ); + + PatchCount |= 1; + + } else if( read32( (u32)ptr + i ) == 0x3C60A800 ) // Games + { + int j=0; + while( read32( (u32)ptr + i - j ) != 0x7C0802A6 ) // Seek to start of function + j+=4; + + //Check if there is a lis %rX, 0xCC00 in this function + //At least Sunshine has one false hit on lis r3,0xA800 + int k=0; + while( 1 ) + { + if( read32( (u32)ptr + i + k - j ) == 0x4E800020 ) + break; + if( (read32( (u32)ptr + i + k - j ) & 0xF81FFFFF) == 0x3800CC00 ) + { + write32( (u32)ptr + i + k - j, (read32((u32)ptr + i + k - j) & 0xFFFF0000) | 0xC000 ); + break; + } + + k += 4; + } + + if( read32( (u32)ptr + i + k - j ) == 0x4E800020 ) + { + //dbgprintf("Patch:No 0xCC00 found around:%08X\n", (u32)ptr+i); + continue; + } + + //Search addi 0x6000 + while( 1 ) + { + if( read32( (u32)ptr + i + k - j ) == 0x4E800020 ) + break; + if( (read32( (u32)ptr + i + k - j ) & 0xFFFF) == 0x6000 ) + { + write32( (u32)ptr + i + k - j, (read32((u32)ptr + i + k - j) & 0xFFFF0000) | 0x2F00 ); + break; + } + + k += 4; + + } + + if( read32( (u32)ptr + i + k - j ) == 0x4E800020 ) + { + //dbgprintf("Patch:No 0xCC00 found around:%08X\n", (u32)ptr+i); + continue; + } + + write32( (u32)ptr + i, 0x3C60A700 ); + + dbgprintf("Patch:Found [DVDLowRead]: 0x%08X\n", (u32)ptr + i + SectionOffset ); + + PatchCount |= 1; + } + } + + if( (PatchCount & 2) == 0 ) + if( (read32( (u32)ptr + i )&0xFC00FFFF) == 0x5400077A && + (read32( (u32)ptr + i + 4 )&0xFC00FFFF) == 0x28000000 && + read32( (u32)ptr + i + 8 ) == 0x41820008 && + (read32( (u32)ptr + i +12 )&0xFC00FFFF) == 0x64002000 + ) + { + dbgprintf("Patch:Found [__OSDispatchInterrupt]: 0x%08X 0x%08X\n", (u32)ptr + i + 0 + SectionOffset, (u32)ptr + i + 0x1A8 + SectionOffset ); + + write32( (u32)ptr + i + 0, (read32( (u32)ptr + i + 0 ) & 0xFFFF0000) | 0x0463 ); + write32( (u32)ptr + i + 0x1A8, (read32( (u32)ptr + i + 0x1A8 ) & 0xFFFF0000) | 0x0463 ); + + PatchCount |= 2; + } + + if( (PatchCount & 4) == 0 ) + if( read32( (u32)ptr + i ) == 0x5480056A && + read32( (u32)ptr + i + 4 ) == 0x28000000 && + read32( (u32)ptr + i + 8 ) == 0x40820008 && + (read32( (u32)ptr + i +12 )&0xFC00FFFF) == 0x60000004 + ) + { + dbgprintf("Patch:Found [SetInterruptMask]: 0x%08X\n", (u32)ptr + i + 12 + SectionOffset ); + + write32( (u32)ptr + i + 12, (read32( (u32)ptr + i + 12 ) & 0xFFFF0000) | 0x4000 ); + + PatchCount |= 4; + } + + if( (PatchCount & 8) == 0 ) + if( (read32( (u32)ptr + i + 0 ) & 0xFFFF) == 0x6000 && + (read32( (u32)ptr + i + 4 ) & 0xFFFF) == 0x002A && + (read32( (u32)ptr + i + 8 ) & 0xFFFF) == 0x0054 + ) + { + u32 Offset = (u32)ptr + i - 8; + + dbgprintf("Patch:Found [__DVDIntrruptHandler]: 0x%08X\n", Offset + SectionOffset ); + + if( (read32(Offset+4) & 0xFFFF) == 0xCC00 ) // Loader + { + value = *(vu32*)(Offset+4); + value&= 0xFFFF0000; + value|= 0x0000C000; + *(vu32*)(Offset+4) = value; + + } else { + + value = *(vu32*)Offset; + value&= 0xFFFF0000; + value|= 0x0000C000; + *(vu32*)Offset = value; + } + + Offset += 8; + + value = *(vu32*)Offset; + value&= 0xFFFF0000; + value|= 0x00002F30; + *(vu32*)Offset = value; + + Offset += 20; + + dbgprintf("Patch:[__DVDInterruptHandler] 0x%08X\n", Offset + SectionOffset ); + *(vu32*)Offset = 0x3D00CD00; Offset += 4; + *(vu32*)Offset = 0x38000034; Offset += 4; + *(vu32*)Offset = 0x90080004; Offset +=16; + + dbgprintf("Patch:[__DVDInterruptHandler] 0x%08X\n", Offset + SectionOffset ); + *(vu32*)Offset = 0x3D00CD00; Offset += 4; + *(vu32*)Offset = 0x3C004000; Offset += 4; + *(vu32*)Offset = 0x90080030; Offset +=32; + + if( (read32(Offset-8) & 0xFFFF) == 0xCC00 ) // Loader + { + Offset -= 8; + } + + dbgprintf("Patch:[__DVDInterruptHandler] 0x%08X\n", Offset + SectionOffset ); + + value = *(vu32*)Offset; + value&= 0xFFFF0000; + value|= 0x0000C000; + *(vu32*)Offset = value; + + Offset += 4; + + value = *(vu32*)Offset; + value&= 0xFFFF0000; + value|= 0x00002F08; + *(vu32*)Offset = value; + + PatchCount |= 8; + } + + if( (PatchCount & 32) == 0 ) + { + if( (read32( (u32)ptr + i + 0 ) & 0xFFFF) == 0xCC00 && // Game + (read32( (u32)ptr + i + 4 ) & 0xFFFF) == 0x6000 && + (read32( (u32)ptr + i +12 ) & 0xFFFF) == 0x001C + ) + { + u32 Offset = (u32)ptr + i; + + dbgprintf("Patch:[cbForStateBusy] 0x%08X\n", Offset + SectionOffset ); + + write32( Offset, 0x3C80C000 ); + write32( Offset+4, 0x38842F30 ); + + PatchCount |= 32; + + } else if( (read32( (u32)ptr + i + 0 ) & 0xFFFF) == 0xCC00 && // Loader + (read32( (u32)ptr + i + 4 ) & 0xFFFF) == 0x6018 && + (read32( (u32)ptr + i +12 ) & 0xFFFF) == 0x001C + ) + { + u32 Offset = (u32)ptr + i; + + dbgprintf("Patch:[cbForStateBusy] 0x%08X\n", Offset + SectionOffset ); + + write32( Offset, 0x3C60C000 ); + write32( Offset+4, 0x80832F48 ); + + PatchCount |= 32; + + } + } + + if( ConfigGetConfig(DML_CFG_CHEATS) || ConfigGetConfig( DML_CFG_DEBUGGER ) ) + { + // OSSleepThread(Pattern 1) + if( (PatchCount & 16) == 0 ) + if( read32((u32)ptr + i + 0 ) == 0x3C808000 && + ( read32((u32)ptr + i + 4 ) == 0x38000004 || read32((u32)ptr + i + 4 ) == 0x808400E4 ) && + ( read32((u32)ptr + i + 8 ) == 0x38000004 || read32((u32)ptr + i + 8 ) == 0x808400E4 ) + ) + { + int j = 12; + + while( read32((u32)ptr + i + j ) != 0x4E800020 ) + j+=4; + + dbgprintf("Patch:[Hook:OSSleepThread] at 0x%08X\n", ((u32)ptr + i + j) | 0x80000000 ); + + u32 DBGSize; + + //if( ConfigGetConfig( DML_CFG_DEBUGGER ) ) + //{ + memcpy( (void*)0x1800, kenobigcDBG, sizeof(kenobigcDBG) ); + DBGSize = sizeof(kenobigcDBG); + //} else { + // memcpy( (void*)0x1800, kenobigc, sizeof(kenobigc) ); + // DBGSize = sizeof(kenobigc); + //} + + if( ConfigGetConfig(DML_CFG_DEBUGWAIT) ) + write32( P2C(read32(0x1808)), 1 ); + else + write32( P2C(read32(0x1808)), 0 ); + + u32 newval = 0x18A8 - ((u32)ptr + i + j); + newval&= 0x03FFFFFC; + newval|= 0x48000000; + write32( (u32)ptr + i + j, newval ); + + memcpy( (void*)0x1800, (void*)0, 6 ); + + char *path = (char*)malloc( 128 ); + + if( ConfigGetConfig(DML_CFG_CHEAT_PATH) ) + { + sprintf( path, "%s", ConfigGetCheatPath() ); + } else { + sprintf( path, "/games/%.6s/%.6s.gct", (char*)0x1800, (char*)0x1800 ); + } + + FIL CodeFD; + u32 read; + + if( f_open( &CodeFD, path, FA_OPEN_EXISTING|FA_READ ) == FR_OK ) + { + if( CodeFD.fsize >= 0x2E80 - (0x1800+DBGSize-8) ) + { + dbgprintf("Patch:Cheatfile is too large, it must not be large than %d bytes!\n", + 0x2E80 - (0x1800+DBGSize-8)); + } else { + if( f_read( &CodeFD, (void*)(0x1800+DBGSize-8), CodeFD.fsize, &read ) == FR_OK ) + { + dbgprintf("Patch:Copied cheat file to memory\n"); + write32( 0x1804, 1 ); + } else + dbgprintf("Patch:Failed to read cheat file:\"%s\"\n", path ); + } + + f_close( &CodeFD ); + + } else { + dbgprintf("Patch:Failed to open/find cheat file:\"%s\"\n", path ); + } + + free(path); + + PatchCount |= 16; + } + } + + if( ConfigGetConfig(DML_CFG_CHEATS) ) + { + if( PatchCount == 63 ) + break; + } else { + if( PatchCount == 47 ) + break; + } + } + + for( i=0; i < size; i+=4 ) + { + if( read32( (u32)ptr + i ) != 0x4E800020 ) + continue; + + i+=4; + + FuncPattern fp; + MPattern( (u8*)(ptr+i), size, &fp ); + + for( j=0; j < sizeof(FPatterns)/sizeof(FuncPattern); ++j ) + { + if( FPatterns[j].Found ) //Skip already found patches + continue; + + if( CPattern( &fp, &(FPatterns[j]) ) ) + { + u32 FOffset = (u32)ptr + i; + + dbgprintf("Patch:Found [%s]: 0x%08X\n", FPatterns[j].Name, FOffset + SectionOffset ); + + switch( FPatterns[j].PatchLength ) + { + case 0xdead0004: // Audiostreaming hack + { + switch( read32(0) >> 8 ) + { + case 0x474544: // Eternal Darkness + break; + default: + { + write32( FOffset + 0xB4, 0x60000000 ); + write32( FOffset + 0xC0, 0x60000000 ); + } break; + } + } break; + case 0xdead0001: // Patch for __GXSetVAT, fixes the dungeon map freeze in Wind Waker + { + switch( read32(0) >> 8 ) + { + case 0x505A4C: // The Legend of Zelda: Collector's Edition + if( !(DOLSize == 3847012 || DOLSize == 3803812) ) // only patch the main.dol of the Zelda:ww game + break; + case 0x475A4C: // The Legend of Zelda: The Wind Waker + { + write32(FOffset, (read32(FOffset) & 0xff00ffff) | 0x220000); + memcpy((void *)(FOffset + 4), __GXSetVAT_patch, sizeof(__GXSetVAT_patch)); + + dbgprintf("Patch:Applied __GXSetVAT patch\n"); + + } break; + default: + break; + } + } break; + case 0xdead000B: // PADRead hook + { + //Find blr + + j=0; + while(1) + { + if( read32( FOffset + j ) == 0x4E800020 ) + break; + j+=4; + } + + dbgprintf("Patch:[PADRead hook] %08X\n", FOffset + j ); + + memcpy( (void*)0x2EE0, padipc, sizeof(padipc) ); + PatchB( 0x2EE0, FOffset + j ); + write32( 0x12FC, 0 ); + + } break; + default: + { + if( ConfigGetConfig( DML_CFG_CHEATS ) ) + { + if( FPatterns[j].Patch == patch_fwrite_GC ) + break; + } + + if( (FPatterns[j].Length >> 16) == 0xdead ) + { + dbgprintf("DIP:Unhandled dead case:%08X\n", FPatterns[j].Length ); + } else + { + memcpy( (void*)(FOffset), FPatterns[j].Patch, FPatterns[j].PatchLength ); + + if ((FPatterns[j].Patch == (u8 *)__dvdLowAudioStatusNULL) && ((read32(0) >> 8) == 0x47494B)) + { + // Ikaruga resets to the main menu, if the returned status is 0(finished playing the stream), but it works if 1(still playing) is returned + write32(FOffset + 36, 0x38600001); + dbgprintf("Patch:LowAudioStatus patched for Ikaruga\n"); + } + } + + } break; + } + + // If this is a patch group set all others of this group as found aswell + if( FPatterns[j].Group ) + { + for( k=0; k < sizeof(FPatterns)/sizeof(FuncPattern); ++k ) + { + if( FPatterns[k].Group == FPatterns[j].Group ) + { + if( !FPatterns[k].Found ) // Don't overwrite the offset! + FPatterns[k].Found = -1; // Usually this holds the offset, to determinate it from a REALLY found pattern we set it -1 which still counts a logical TRUE + + //dbgprintf("Setting [%s] to found!\n", FPatterns[k].Name ); + } + } + } + } + } + } +} diff --git a/Patches.h b/Patches.h new file mode 100644 index 0000000..4a306bb --- /dev/null +++ b/Patches.h @@ -0,0 +1,52 @@ +#ifndef _PATCHES_ +#define _PATCHES_ + +#include "string.h" +#include "global.h" +#include "ipc.h" +#include "alloc.h" +#include "ff.h" +#include "vsprintf.h" +#include "HW.h" +#include "dol.h" + + +typedef struct PatchInfo +{ + u8 *Signature; + u8 *Mask; + u32 Length; + u32 FunctionLength; + u8 *Patch; + u32 PatchLength; + char *Name; +} PatchInfo; + +typedef struct FuncPattern +{ + u32 Length; + u32 Loads; + u32 Stores; + u32 FCalls; + u32 Branch; + u32 Moves; + u8 *Patch; + u32 PatchLength; + char *Name; + u32 Group; + u32 Found; +} FuncPattern; + +typedef struct PatchCache +{ + u32 Offset; + u32 PatchID; + +} PatchCache; + +void PatchGCIPL( void ); +void DoPatches( char *ptr, u32 size, u32 SectionOffset ); +void DoCardPatches( char *ptr, u32 size, u32 SectionOffset ); +void DoPatchesLoader( char *ptr, u32 size ); + +#endif \ No newline at end of file diff --git a/alloc.c b/alloc.c new file mode 100644 index 0000000..c2e1c45 --- /dev/null +++ b/alloc.c @@ -0,0 +1,205 @@ +#include "alloc.h" +#include "vsprintf.h" + +u8 *RAM;//[_AHEAP_SIZE_TOTAL]; +HeapInfoEntry *HeapInfoEntries=(HeapInfoEntry *)NULL; + +extern u32 DRAMRead( u32 a ); +extern void DRAMWrite( u32 a, u32 b ); + +void HeapInit( u8 *Offset ) +{ + //RAM = (u8*)0xFFFE4000; + //RAM = (u8*)0x13600000; + //RAM = (u8*)0x00600000; + + RAM = Offset; + + HeapInfoEntries = (HeapInfoEntry*)(RAM+_AHEAP_SIZE); + memset32( HeapInfoEntries, 0, _AHEAP_INFO_SIZE ); + + while( HeapInfoEntries[0].Offset != 0 ) + { + EXIControl(1); + dbgprintf("Failed to clear memory!:%08X", HeapInfoEntries[0].Offset ); + Shutdown(); + } + +// dbgprintf("Cleared 0x%04X bytes Space for %d allocs\n", _AHEAP_INFO_SIZE, _AHEAP_INFO_SIZE / 8 ); + +} +void *malloc( u32 _size ) +{ + if( _size == 0 ) + return NULL; + + if( _size > _AHEAP_SIZE ) + return NULL; + + //align size to 32, easy cheat toallow all allocs to be aligned easily + u32 size = (_size+0x1F) & (~0x1F); + + //find a free entry to be used + u32 entry = 0xdeadbeef; + u32 i; + + for( i=0; i < _AHEAP_INFO_SIZE / sizeof(HeapInfoEntry); ++i ) + { + if( HeapInfoEntries[i].Offset == 0 ) + { + entry = i; + break; + } + } + + if( entry == 0xdeadbeef ) + { + EXIControl(1); + dbgprintf("Alloc: run out of entries!\n"); + while(1); + } + + dbgprintf("Using entry:%d to alloc %u(%u) bytes...\n", entry, size, _size ); + + //Now we search a used entry + u32 used_entry = 0xdeadbeef; + + for( i=0; i < _AHEAP_INFO_SIZE / sizeof(HeapInfoEntry); ++i ) + { + if( HeapInfoEntries[i].Offset == 0 ) + continue; + + used_entry = i; + break; + } + + if( used_entry == 0xdeadbeef ) + { + //dbgprintf("There are no other entries used atm\n"); + HeapInfoEntries[entry].Offset = RAM; + HeapInfoEntries[entry].Size = size; + //dbgprintf("alloc1: ptr:%p size:%08X Entry:%d\n", HeapInfoEntries[entry].Offset, HeapInfoEntries[entry].Size, entry ); + return HeapInfoEntries[entry].Offset; + } + +find_space: +; + //dbgprintf("[%02d]Offset:%08X Size:%08X\n", used_entry, HeapInfoEntries[used_entry].Offset, HeapInfoEntries[used_entry].Size ); + + //now we search for the next closest and the previous closest entry + u32 next = 0xdeadbeef; + u32 prev = 0xdeadbeef; + + for( i=0; i < _AHEAP_INFO_SIZE / sizeof(HeapInfoEntry); ++i ) + { + if( HeapInfoEntries[i].Offset == 0 ) + continue; + if( used_entry == i ) + continue; + + if( next == 0xdeadbeef ) + { + if( HeapInfoEntries[i].Offset > HeapInfoEntries[used_entry].Offset ) + next = i; + } else { + if( HeapInfoEntries[i].Offset < HeapInfoEntries[next].Offset && HeapInfoEntries[i].Offset > HeapInfoEntries[used_entry].Offset ) + next = i; + } + + if( prev == 0xdeadbeef ) + { + if( HeapInfoEntries[i].Offset < HeapInfoEntries[used_entry].Offset ) + prev = i; + } else { + if( HeapInfoEntries[i].Offset > HeapInfoEntries[prev].Offset && HeapInfoEntries[i].Offset < HeapInfoEntries[used_entry].Offset ) + prev = i; + } + } + + if( next == 0xdeadbeef ) + { + //dbgprintf("This is the last entry\n"); + + //check if there is engough space left for our alloc + + if( (u32)(HeapInfoEntries[used_entry].Offset-RAM) + HeapInfoEntries[used_entry].Size + size <= _AHEAP_SIZE ) + { + HeapInfoEntries[entry].Offset = HeapInfoEntries[used_entry].Offset + HeapInfoEntries[used_entry].Size; + HeapInfoEntries[entry].Size = size; + //dbgprintf("alloc2: ptr:%p size:%08X Entry:%d\n", HeapInfoEntries[entry].Offset, HeapInfoEntries[entry].Size, entry ); + return HeapInfoEntries[entry].Offset; + } + ;//dbgprintf("2Not enough space left only had:%d\n", HEAP_SIZE - ((u32)(HeapInfoEntries[used_entry].Offset-RAM) + HeapInfoEntries[used_entry].Size) ); + } else if( (u32)(HeapInfoEntries[used_entry].Offset) + HeapInfoEntries[used_entry].Size + size < (u32)(HeapInfoEntries[next].Offset) ) + { + HeapInfoEntries[entry].Offset = HeapInfoEntries[used_entry].Offset + HeapInfoEntries[used_entry].Size; + HeapInfoEntries[entry].Size = size; + //dbgprintf("alloc4: ptr:%p size:%08X Entry:%d\n", HeapInfoEntries[entry].Offset, HeapInfoEntries[entry].Size, entry ); + return HeapInfoEntries[entry].Offset; + } else { + ;//dbgprintf("4Not enough space left only had:%d %d:%d\n", (u32)( HeapInfoEntries[next].Offset - HeapInfoEntries[used_entry].Offset ) - HeapInfoEntries[used_entry].Size, next, used_entry ); + } + + if( prev == 0xdeadbeef ) + { + //dbgprintf("This is the first entry\n"); + if( (u32)(HeapInfoEntries[used_entry].Offset-RAM) >= size ) + { + HeapInfoEntries[entry].Offset = HeapInfoEntries[used_entry].Offset - size; + HeapInfoEntries[entry].Size = size; + //dbgprintf("alloc3: ptr:%p size:%08X Entry:%d\n", HeapInfoEntries[entry].Offset, HeapInfoEntries[entry].Size, entry ); + return HeapInfoEntries[entry].Offset; + } + ;//dbgprintf("3Not enough space left only had:%d\n", (u32)(HeapInfoEntries[used_entry].Offset-RAM) ); + } else if( (u32)(HeapInfoEntries[prev].Offset) + HeapInfoEntries[prev].Size + size < (u32)(HeapInfoEntries[used_entry].Offset) ) + { + HeapInfoEntries[entry].Offset = HeapInfoEntries[prev].Offset + HeapInfoEntries[prev].Size; + HeapInfoEntries[entry].Size = size; + //dbgprintf("alloc5: ptr:%p size:%08X Entry:%d\n", HeapInfoEntries[entry].Offset, HeapInfoEntries[entry].Size, entry ); + return HeapInfoEntries[entry].Offset; + } else { + ;//dbgprintf("5Not enough space left only had:%d\n", (u32)(HeapInfoEntries[used_entry].Offset-HeapInfoEntries[prev].Offset) - HeapInfoEntries[prev].Size ); + } + + //if we land here we have to go to the next entry + u32 temp = used_entry + 1; + used_entry = 0xdeadbeef; + + for( i=temp; i < _AHEAP_INFO_SIZE / sizeof(HeapInfoEntry); ++i ) + { + if( HeapInfoEntries[i].Offset == 0 ) + continue; + + used_entry = i; + break; + } + + if( used_entry != 0xdeadbeef ) + goto find_space; + + dbgprintf("failed to alloc %d bytes\n", size ); + + return NULL; +} +void *malloca( u32 size, u32 align ) +{ + return malloc( size ); +} +void free( void *ptr ) +{ + if( ptr == NULL ) + return; + + u32 i; + for( i=0; i < _AHEAP_INFO_SIZE / sizeof(HeapInfoEntry); ++i ) + { + if( HeapInfoEntries[i].Offset == ptr ) + { + //dbgprintf("free: ptr:%p size:%08X Entry:%d\n", HeapInfoEntries[i].Offset, HeapInfoEntries[i].Size, i ); + HeapInfoEntries[i].Offset = NULL; + HeapInfoEntries[i].Size = 0; + ptr = NULL; + return; + } + } +} \ No newline at end of file diff --git a/alloc.h b/alloc.h new file mode 100644 index 0000000..d0dec11 --- /dev/null +++ b/alloc.h @@ -0,0 +1,23 @@ +#include "utils.h" +#include "memory.h" +#include "HW.h" + +#ifndef _ALLOC_ +#define _ALLOC_ + +#define _AHEAP_SIZE_TOTAL 0x3000 +#define _AHEAP_INFO_SIZE 0x0100 +#define _AHEAP_SIZE _AHEAP_SIZE_TOTAL-_AHEAP_INFO_SIZE + +typedef struct +{ + u8 *Offset; + u32 Size; +} HeapInfoEntry; + +void HeapInit( u8 *Offset ); +void *malloc( u32 size ); +void *malloca( u32 size, u32 align ); +void free( void *ptr ); + +#endif diff --git a/asm/CARDCheck.S b/asm/CARDCheck.S new file mode 100644 index 0000000..ebb0e86 --- /dev/null +++ b/asm/CARDCheck.S @@ -0,0 +1,23 @@ +#include + +# +# r3 Channel + +CARDCheck: + + mflr %r0 + cmpwi %r3, 0 + beq CARDPresent + + li %r3, -3 + b end + +CARDPresent: + li %r3, 0 +end: + + lis %r4, 0xC000 + stw %r3, 0x2F94(%r4) + + mtlr %r0 + blr diff --git a/asm/CARDCheckAsync.S b/asm/CARDCheckAsync.S new file mode 100644 index 0000000..5c04cc5 --- /dev/null +++ b/asm/CARDCheckAsync.S @@ -0,0 +1,43 @@ +#include + +# +# r3 Channel +# r4 cb( chan, res ) + +CARDCheck: + + mflr %r0 + stw %r0, 4(%sp) + stwu %sp, -0x10(%sp) + + cmpwi %r3, 0 + beq CARDPresent + + cmpwi %r4, 0 + beq NoCardNoCB + + mtctr %r4 + li %r4, -3 + bctrl + +NoCardNoCB: + li %r3, -3 + b end + +CARDPresent: + + cmpwi %r4, 0 + beq CardNoCB + + mtctr %r4 + li %r4, 0 + bctrl + +CardNoCB: + li %r3, 0 +end: + lwz %r0, 0x14(%sp) + addi %sp, %sp, 0x10 + mtlr %r0 + blr + diff --git a/asm/CARDCheckEX.S b/asm/CARDCheckEX.S new file mode 100644 index 0000000..03365f2 --- /dev/null +++ b/asm/CARDCheckEX.S @@ -0,0 +1,60 @@ +#include + +# +# r3 Channel +# r4 *xfer bytes used to repair the FS +# r5 cb + +CARDCheckEx: + + mflr %r0 + stw %r0, 4(%sp) + stwu %sp, -0x10(%sp) + + cmpwi %r3, 0 + beq CARDPresent + + cmpwi %r4, 0 + beq NoCardZeroPtr + + li %r6, 0 + stw %r6, 0(%r4) + +NoCardZeroPtr: + cmpwi %r5, 0 + beq NoCardNoCB + + mtctr %r5 + li %r4, -3 + bctrl + +NoCardNoCB: + li %r3, -3 + b end + +CARDPresent: + + cmpwi %r4, 0 + beq CardZeroPtr + + li %r6, 0 + stw %r6, 0(%r4) + +CardZeroPtr: + cmpwi %r5, 0 + beq CardNoCB + + mtctr %r5 + li %r4, 0 + bctrl + +CardNoCB: + li %r3, 0 +end: + lis %r4, 0xC000 + stw %r3, 0x2F94(%r4) + + lwz %r0, 0x14(%sp) + addi %sp, %sp, 0x10 + mtlr %r0 + blr diff --git a/asm/CARDClose.S b/asm/CARDClose.S new file mode 100644 index 0000000..072e4d3 --- /dev/null +++ b/asm/CARDClose.S @@ -0,0 +1,47 @@ +#include + +# +# r3 FileInfo + +CARDClose: + + mflr %r0 + stw %r0, 4(%sp) + stwu %sp, -0x10(%sp) + +#send cmd to DM + + li %r0, 0 + lis %r7, 0xC000 + addi %r7, %r7, 0x2F60 +#IPC area + dcbi %r0, %r7 + + lis %r7, 0xC000 + lis %r0, 0xC100 + + stw %r0, 0x2F60(%r7) + lwz %r0, 0x04(%r3) + stw %r0, 0x2F64(%r7) + + li %r0, 3 + stw %r0, 0x2F78(%r7) + +ready_loop: + lwz %r0, 0x2F78(%r7) + cmpwi %r0, 3 + beq ready_loop + +wait_loop: + lwz %r0, 0x2F9C(%r7) + andi. %r0, %r0, 0x14 + cmpwi %r0, 0 + beq wait_loop + + lwz %r3, 0x2F94(%r7) + mr %r4, %r3 + + lwz %r0, 0x14(%sp) + addi %sp, %sp, 0x10 + mtlr %r0 + blr diff --git a/asm/CARDCreate.S b/asm/CARDCreate.S new file mode 100644 index 0000000..e0dedab --- /dev/null +++ b/asm/CARDCreate.S @@ -0,0 +1,88 @@ +#include + +# +# r3 chan +# r4 fileName +# r5 size +# r6 fileInfo +# r7 cb + +CARDCreate: + + cmpwi %r3, 0 + beq DoCode + li %r3, -3 + li %r4, -3 + blr + +DoCode: + + mflr %r0 + stw %r0, 4(%sp) + stwu %sp, -0x10(%sp) + +#send cmd to DM + + mr %r12, %r7 + + li %r0, 0 + lis %r7, 0xC000 + addi %r7, %r7, 0x2F60 +#IPC area + dcbi %r0, %r7 + addi %r7, %r7, 0x20 + dcbi %r0, %r7 + + lis %r7, 0xC000 + lis %r0, 0xC200 + + stw %r0, 0x2F60(%r7) + stw %r4, 0x2F64(%r7) + stw %r5, 0x2F68(%r7) + stw %r6, 0x2F6C(%r7) + +#cache workaround for the filename + li %r5, 8 + mtctr %r5 + lis %r5, 0xC000 +invalidloop: + lwz %r0, 0(%r4) + stw %r0, 0x17E0(%r5) + addi %r4, %r4, 4 + addi %r5, %r5, 4 + bdnz invalidloop + + li %r0, 3 + stw %r0, 0x2F78(%r7) + +ready_loop: + lwz %r0, 0x2F78(%r7) + cmpwi %r0, 3 + beq ready_loop + +wait_loop: + lwz %r0, 0x2F9C(%r7) + andi. %r0, %r0, 0x14 + cmpwi %r0, 0 + beq wait_loop + + lwz %r0, 0x2F90(%r7) + stw %r0, 0x04(%r6) + + cmpwi %r12, 0 + beq skip_cb + mtlr %r12 + li %r3, 0 + li %r4, 0 + blrl + +skip_cb: + lis %r7, 0xC000 + lwz %r3, 0x2F94(%r7) + mr %r4, %r3 + + lwz %r0, 0x14(%sp) + addi %sp, %sp, 0x10 + mtlr %r0 + blr + diff --git a/asm/CARDDelete.S b/asm/CARDDelete.S new file mode 100644 index 0000000..6df0289 --- /dev/null +++ b/asm/CARDDelete.S @@ -0,0 +1,80 @@ +#include + +# +# r3 chan +# r4 fileName +# r5 cb + +CARDDelete: + + cmpwi %r3, 0 + beq DoCode + li %r3, -3 + blr + +DoCode: + + mflr %r0 + stw %r0, 4(%sp) + stwu %sp, -0x10(%sp) + +#send cmd to DM + + mr %r12, %r5 + + li %r0, 0 + lis %r7, 0xC000 + addi %r7, %r7, 0x2F60 +#IPC area + dcbi %r0, %r7 + addi %r7, %r7, 0x20 + dcbi %r0, %r7 + + lis %r7, 0xC000 + lis %r0, 0xC600 + + stw %r0, 0x2F60(%r7) + stw %r4, 0x2F64(%r7) + +#cache workaround for the filename + li %r5, 8 + mtctr %r5 + lis %r5, 0xC000 +invalidloop: + lwz %r0, 0(%r4) + stw %r0, 0x17E0(%r5) + addi %r4, %r4, 4 + addi %r5, %r5, 4 + bdnz invalidloop + + li %r0, 3 + stw %r0, 0x2F78(%r7) + +ready_loop: + lwz %r0, 0x2F78(%r7) + cmpwi %r0, 3 + beq ready_loop + +wait_loop: + lwz %r0, 0x2F9C(%r7) + andi. %r0, %r0, 0x14 + cmpwi %r0, 0 + beq wait_loop + + cmpwi %r12, 0 + beq skip_cb + mtlr %r12 + lwz %r3, 0x2F94(%r7) + li %r4, 0 + blrl + +skip_cb: + lis %r7, 0xC000 + lwz %r3, 0x2F94(%r7) + mr %r4, %r3 + + lwz %r0, 0x14(%sp) + addi %sp, %sp, 0x10 + mtlr %r0 + blr + diff --git a/asm/CARDFastDelete.S b/asm/CARDFastDelete.S new file mode 100644 index 0000000..e7f168a --- /dev/null +++ b/asm/CARDFastDelete.S @@ -0,0 +1,69 @@ +#include + +# +# r3 chan +# r4 FileNo +# r5 cb + +CARDFastDelete: + + cmpwi %r3, 0 + beq DoCode + li %r3, -3 + blr + +DoCode: + + mflr %r0 + stw %r0, 4(%sp) + stwu %sp, -0x10(%sp) + +#send cmd to DM + + mr %r12, %r5 + + li %r0, 0 + lis %r7, 0xC000 + addi %r7, %r7, 0x2F60 +#IPC area + dcbi %r0, %r7 + addi %r7, %r7, 0x20 + dcbi %r0, %r7 + + lis %r7, 0xC000 + lis %r0, 0xCA00 + + stw %r0, 0x2F60(%r7) + stw %r4, 0x2F64(%r7) + + li %r0, 3 + stw %r0, 0x2F78(%r7) + +ready_loop: + lwz %r0, 0x2F78(%r7) + cmpwi %r0, 3 + beq ready_loop + +wait_loop: + lwz %r0, 0x2F9C(%r7) + andi. %r0, %r0, 0x14 + cmpwi %r0, 0 + beq wait_loop + + cmpwi %r12, 0 + beq skip_cb + mtlr %r12 + lwz %r3, 0x2F94(%r7) + li %r4, 0 + blrl + +skip_cb: + lis %r7, 0xC000 + lwz %r3, 0x2F94(%r7) + mr %r4, %r3 + + lwz %r0, 0x14(%sp) + addi %sp, %sp, 0x10 + mtlr %r0 + blr + diff --git a/asm/CARDFastOpen.S b/asm/CARDFastOpen.S new file mode 100644 index 0000000..c35acd6 --- /dev/null +++ b/asm/CARDFastOpen.S @@ -0,0 +1,62 @@ +#include + +# +# r3 chan +# r4 FileNo +# r5 FileInfo + +CARDFastOpen: + + mflr %r0 + stw %r0, 4(%sp) + stwu %sp, -0x10(%sp) + + cmpwi %r3, 0 + bne NoCard + +#send cmd to DM + + li %r0, 0 + lis %r7, 0xC000 + addi %r7, %r7, 0x2F60 +#IPC area + dcbi %r0, %r7 + addi %r7, %r7, 0x20 + dcbi %r0, %r7 + + lis %r7, 0xC000 + lis %r0, 0xC500 + + stw %r0, 0x2F60(%r7) + stw %r4, 0x2F64(%r7) + stw %r5, 0x2F68(%r7) + + li %r0, 3 + stw %r0, 0x2F78(%r7) + +ready_loop: + lwz %r0, 0x2F78(%r7) + cmpwi %r0, 3 + beq ready_loop + +wait_loop: + lwz %r0, 0x2F9C(%r7) + andi. %r0, %r0, 0x14 + cmpwi %r0, 0 + beq wait_loop + + lwz %r3, 0x2F90(%r7) + stw %r3, 0x10(%r5) + stw %r4, 0x04(%r5) + + lwz %r3, 0x2F94(%r7) + b end + +NoCard: + li %r3, -3 +end: + + lwz %r0, 0x14(%sp) + addi %sp, %sp, 0x10 + mtlr %r0 + blr diff --git a/asm/CARDFreeBlocks.S b/asm/CARDFreeBlocks.S new file mode 100644 index 0000000..275fdc1 --- /dev/null +++ b/asm/CARDFreeBlocks.S @@ -0,0 +1,32 @@ +#include + +# +# r3 chan +# r4 byteNotUsed +# r5 filesNotUsed + +CARDFreeBlocks: + + + mflr %r0 + cmpwi %r3, 0 + beq CARDPresent + + li %r3, -3 + b end + +CARDPresent: + + lis %r3, 0x7F + addi %r3, %r3, 0x6000 + stw %r3, 0(%r4) + + li %r3, 16 + stw %r3, 0(%r5) + + li %r3, 0 + +end: + + mtlr %r0 + blr diff --git a/asm/CARDGetEncoding.S b/asm/CARDGetEncoding.S new file mode 100644 index 0000000..b6e7905 --- /dev/null +++ b/asm/CARDGetEncoding.S @@ -0,0 +1,30 @@ +#include + +# +# r3 Channel +# r4 endcoding(u16) + +CARDGetEncoding: + + cmpwi %r3, 0 + beq DoCode + li %r3, -3 + blr + +DoCode: + + mflr %r0 + stw %r0, 4(%sp) + stwu %sp, -0x10(%sp) + +#0 is USA/EUR, 1 is JAP + li %r0, 0 + sth %r0, 0(%r4) + +end: + li %r3, 0 + + lwz %r0, 0x14(%sp) + addi %sp, %sp, 0x10 + mtlr %r0 + blr diff --git a/asm/CARDGetMemSize.S b/asm/CARDGetMemSize.S new file mode 100644 index 0000000..5cea1f3 --- /dev/null +++ b/asm/CARDGetMemSize.S @@ -0,0 +1,30 @@ +#include + +# +# r3 Channel +# r4 size(u16) + +CARDGetMemSize: + + cmpwi %r3, 0 + beq DoCode + li %r3, -3 + blr + +DoCode: + + mflr %r0 + stw %r0, 4(%sp) + stwu %sp, -0x10(%sp) + +#possible sizes 4,8,16,32,64,128 + li %r0, 8 + sth %r0, 0(%r4) + +end: + li %r3, 0 + + lwz %r0, 0x14(%sp) + addi %sp, %sp, 0x10 + mtlr %r0 + blr diff --git a/asm/CARDGetResultCode.S b/asm/CARDGetResultCode.S new file mode 100644 index 0000000..1ce7703 --- /dev/null +++ b/asm/CARDGetResultCode.S @@ -0,0 +1,19 @@ +#include + +# +# r3 Channel + +CARDGetResultCode: + + cmpwi %r3, 0 + beq CARDPresent + + li %r3, -3 + b end + +CARDPresent: + lis %r3, 0xC000 + lwz %r3, 0x2F94(%r3) +end: + + blr diff --git a/asm/CARDGetSerialNo.S b/asm/CARDGetSerialNo.S new file mode 100644 index 0000000..9da704f --- /dev/null +++ b/asm/CARDGetSerialNo.S @@ -0,0 +1,29 @@ +#include + +# +# r3 Channel +# r4 serialNo + +CARDGetSerialNo: + + cmpwi %r3, 0 + beq DoCode + li %r3, -3 + blr + +DoCode: + + mflr %r0 + + lis %r3, 0xc7bd + subi %r3, %r3, 0x26C + stw %r3, 0x00(%r4) + + lis %r3, 0xf47f + subi %r3, %r3, 0x3924 + stw %r3, 0x04(%r4) + + li %r3, 0 + + mtlr %r0 + blr diff --git a/asm/CARDGetStats.S b/asm/CARDGetStats.S new file mode 100644 index 0000000..b47df2a --- /dev/null +++ b/asm/CARDGetStats.S @@ -0,0 +1,74 @@ +#include + +# +# r3 chan +# r4 FileNo +# r5 Stat +# r6 cb + +CARDGetStats: + + cmpwi %r3, 0 + beq DoCode + li %r3, 0 + li %r4, -3 + blr + +DoCode: + + mflr %r0 + stw %r0, 4(%sp) + stwu %sp, -0x10(%sp) + +#send cmd to DM + + mr %r12, %r6 + + li %r0, 0 + lis %r7, 0xC000 + addi %r7, %r7, 0x2F60 +#IPC area + dcbi %r0, %r7 + addi %r7, %r7, 0x20 + dcbi %r0, %r7 + + lis %r7, 0xC000 + lis %r0, 0xC300 + + stw %r0, 0x2F60(%r7) + stw %r4, 0x2F64(%r7) + stw %r5, 0x2F68(%r7) + + li %r0, 3 + stw %r0, 0x2F78(%r7) + +ready_loop: + lwz %r0, 0x2F78(%r7) + cmpwi %r0, 3 + beq ready_loop + +wait_loop: + lwz %r0, 0x2F9C(%r7) + andi. %r0, %r0, 0x14 + cmpwi %r0, 0 + beq wait_loop + +#cache workaround + li %r6, 27 + mtctr %r6 + lis %r6, 0xC000 + addi %r6, %r6, 0x1780 +invalidloop: + lwz %r0, 0(%r6) + stw %r0, 0(%r5) + addi %r5, %r5, 4 + addi %r6, %r6, 4 + bdnz invalidloop + + lwz %r3, 0x2F94(%r7) + mr %r4, %r3 + + lwz %r0, 0x14(%sp) + addi %sp, %sp, 0x10 + mtlr %r0 + blr diff --git a/asm/CARDGetXferredBytes.S b/asm/CARDGetXferredBytes.S new file mode 100644 index 0000000..05b4f73 --- /dev/null +++ b/asm/CARDGetXferredBytes.S @@ -0,0 +1,17 @@ +#include + +# +# r3 Channel + +CARDGetEncoding: + + cmpwi %r3, 0 + beq DoCode + blr + +DoCode: + + lis %r3, 0xC000 + lwz %r3, 0x2FA0(%r3) + + blr diff --git a/asm/CARDMount.S b/asm/CARDMount.S new file mode 100644 index 0000000..583d69c --- /dev/null +++ b/asm/CARDMount.S @@ -0,0 +1,26 @@ +#include + +# +# r3 Channel +# r4 workarea +# r5 detachCallback + +CARDMount: + + mflr %r0 + stw %r0, 4(%sp) + + cmpwi %r3, 0 + beq CARDPresent + + li %r3, -3 + b end + +CARDPresent: + + li %r3, 0 +end: + + lwz %r0, 4(%sp) + mtlr %r0 + blr diff --git a/asm/CARDMountAsync.S b/asm/CARDMountAsync.S new file mode 100644 index 0000000..5904c90 --- /dev/null +++ b/asm/CARDMountAsync.S @@ -0,0 +1,42 @@ +#include + +# +# r3 Channel +# r4 workarea +# r5 detachCallback +# r6 attachCallback + +CARDMountAsync: + + cmpwi %r3, 0 + beq DoCode + li %r3, -3 + lis %r7, 0xC000 + stw %r3, 0x2F94(%r7) + blr + +DoCode: + + mflr %r0 + stw %r0, 4(%sp) + stwu %sp, -0x10(%sp) + + li %r3, 0 + li %r4, 0 + + cmpwi %r6, 0 + beq end + + mtctr %r6 + bctrl + +end: + li %r3, 0 + li %r4, 0 + lis %r7, 0xC000 + stw %r3, 0x2F94(%r7) + + lwz %r0, 0x14(%sp) + addi %sp, %sp, 0x10 + mtlr %r0 + blr diff --git a/asm/CARDOpen.S b/asm/CARDOpen.S new file mode 100644 index 0000000..f5f9b34 --- /dev/null +++ b/asm/CARDOpen.S @@ -0,0 +1,71 @@ +#include + +# +# r3 chan +# r4 FileName +# r5 FileInfo + +CARDOpen: + + mflr %r0 + stw %r0, 4(%sp) + stwu %sp, -0x10(%sp) + + cmpwi %r3, 0 + bne NoCard + +#send cmd to DM + + li %r0, 0 + lis %r7, 0xC000 + addi %r7, %r7, 0x2F60 +#IPC area + dcbi %r0, %r7 + addi %r7, %r7, 0x20 + dcbi %r0, %r7 + + lis %r7, 0xC000 +#cmd is the same as the offset, one opcode saved! + stw %r7, 0x2F60(%r7) + stw %r4, 0x2F64(%r7) + stw %r5, 0x2F68(%r7) + +#cache workaround for the filename + li %r3, 8 + mtctr %r3 + lis %r3, 0xC000 +invalidloop: + lwz %r0, 0(%r4) + stw %r0, 0x17E0(%r3) + addi %r4, %r4, 4 + addi %r3, %r3, 4 + bdnz invalidloop + + li %r0, 3 + stw %r0, 0x2F78(%r7) + +ready_loop: + lwz %r0, 0x2F78(%r7) + cmpwi %r0, 3 + beq ready_loop + +wait_loop: + lwz %r0, 0x2F9C(%r7) + andi. %r0, %r0, 0x14 + cmpwi %r0, 0 + beq wait_loop + + lwz %r0, 0x2F90(%r7) + stw %r0, 0x04(%r5) + lwz %r3, 0x2F94(%r7) + b end + +NoCard: + li %r3, -3 +end: + mr %r4, %r3 + + lwz %r0, 0x14(%sp) + addi %sp, %sp, 0x10 + mtlr %r0 + blr diff --git a/asm/CARDProbe.S b/asm/CARDProbe.S new file mode 100644 index 0000000..7ad1295 --- /dev/null +++ b/asm/CARDProbe.S @@ -0,0 +1,21 @@ +#include + +# +# r3 Channel + +CARDProbe: + + + mflr %r0 + cmpwi %r3, 0 + beq CARDPresent + + li %r3, 0 + b end + +CARDPresent: + li %r3, 1 +end: + + mtlr %r0 + blr diff --git a/asm/CARDProbeEX.S b/asm/CARDProbeEX.S new file mode 100644 index 0000000..b1c0799 --- /dev/null +++ b/asm/CARDProbeEX.S @@ -0,0 +1,41 @@ +#include + +# +# r3 Channel +# r4 MemSize (can be zero ptr) +# r5 SecSize (can be zero ptr) + +CARDProbeEX: + + + mflr %r0 + cmpwi %r3, 0 + beq CARDPresent + + li %r3, -3 + b end + +CARDPresent: + + cmpwi %r4, 0 + beq ZeroMemPtr + + li %r3, 8 + stw %r3, 0(%r4) + +ZeroMemPtr: + + cmpwi %r5, 0 + beq ZeroSecPtr + + li %r3, 0x2000 + stw %r3, 0(%r5) + +ZeroSecPtr: + + li %r3, 0 + +end: + + mtlr %r0 + blr diff --git a/asm/CARDRead.S b/asm/CARDRead.S new file mode 100644 index 0000000..7c50e61 --- /dev/null +++ b/asm/CARDRead.S @@ -0,0 +1,82 @@ +#include + +# +# r3 file * +# r4 buffer +# r5 length +# r6 offset +# r7 cb + +CardWrite: + + mflr %r0 + stw %r0, 4(%sp) + stwu %sp, -0x20(%sp) + stw %r31, 0x1C(%sp) + +#Update fileinfo + stw %r5, 0x0C(%r3) + stw %r6, 0x08(%r3) + +#send cmd to DM + + mr %r12, %r7 + + li %r0, 0 + lis %r7, 0xC000 + addi %r7, %r7, 0x2F60 +#IPC area + dcbi %r0, %r7 + + srwi %r7, %r5, 5 + mtctr %r7 + mr %r7, %r4 +invalidloop: + dcbi %r0, %r7 + addi %r7, %r7, 0x20 + bdnz invalidloop + + lis %r7, 0xC000 + lis %r0, 0xC900 + + stw %r0, 0x2F60(%r7) + + stw %r4, 0x2F64(%r7) + stw %r5, 0x2F68(%r7) + + stw %r6, 0x2F6C(%r7) + + lwz %r0, 0x04(%r3) + stw %r0, 0x2F70(%r7) + + li %r0, 3 + stw %r0, 0x2F78(%r7) + +ready_loop: + lwz %r0, 0x2F78(%r7) + cmpwi %r0, 3 + beq ready_loop + +wait_loop: + lwz %r0, 0x2F9C(%r7) + andi. %r0, %r0, 0x14 + cmpwi %r0, 0 + beq wait_loop + + + cmpwi %r12, 0 + beq skip_cb + mtctr %r12 + li %r3, 0 + li %r4, 0 + bctrl + +skip_cb: + li %r3, 0 + mr %r4, %r3 + + lwz %r0, 0x24(%sp) + lwz %r31, 0x1C(%sp) + addi %sp, %sp, 0x20 + mtlr %r0 + blr diff --git a/asm/CARDRename.S b/asm/CARDRename.S new file mode 100644 index 0000000..3d2366d --- /dev/null +++ b/asm/CARDRename.S @@ -0,0 +1,87 @@ +#include + +# +# r3 chan +# r4 old +# r5 new +# r6 cb + +CARDRename: + + mflr %r8 + + cmpwi %r3, 0 + bne NoCard + +#send cmd to DM + + mr %r12, %r6 + + li %r0, 0 + lis %r7, 0xC000 + addi %r7, %r7, 0x2F60 +#IPC area + dcbi %r0, %r7 + addi %r7, %r7, 0x20 + dcbi %r0, %r7 + + +#cache workaround for the filename + li %r3, 8 + mtctr %r3 + lis %r3, 0xC000 +invalidloopA: + lwz %r0, 0(%r4) + stw %r0, 0x17C0(%r3) + addi %r4, %r4, 4 + addi %r3, %r3, 4 + bdnz invalidloopA + +#cache workaround for the filename + li %r3, 8 + mtctr %r3 + lis %r3, 0xC000 +invalidloopB: + lwz %r0, 0(%r5) + stw %r0, 0x17E0(%r3) + addi %r5, %r5, 4 + addi %r3, %r3, 4 + bdnz invalidloopB + + lis %r7, 0xC000 + + lis %r0, 0xCB00 + stw %r0, 0x2F60(%r7) + + li %r0, 3 + stw %r0, 0x2F78(%r7) + +ready_loop: + lwz %r0, 0x2F78(%r7) + cmpwi %r0, 3 + beq ready_loop + +wait_loop: + lwz %r0, 0x2F9C(%r7) + andi. %r0, %r0, 0x14 + cmpwi %r0, 0 + beq wait_loop + + cmpwi %r12, 0 + beq skip_cb + mtlr %r12 + li %r3, 0 + lwz %r4, 0x2F94(%r7) + blrl + +skip_cb: + lwz %r3, 0x2F94(%r7) + b end + +NoCard: + li %r3, -3 +end: + mr %r4, %r3 + + mtlr %r8 + blr diff --git a/asm/CARDSetStats.S b/asm/CARDSetStats.S new file mode 100644 index 0000000..77fd30c --- /dev/null +++ b/asm/CARDSetStats.S @@ -0,0 +1,83 @@ +#include + +# +# r3 chan +# r4 fileNo +# r5 stat +# r6 cb + +CARDSetStat: + + cmpwi %r3, 0 + beq DoCode + li %r3, 0 + li %r4, -3 + blr + +DoCode: + + mflr %r0 + stw %r0, 4(%sp) + stwu %sp, -0x10(%sp) + +#send cmd to DM + + mr %r12, %r6 + + li %r0, 0 + lis %r7, 0xC000 + addi %r7, %r7, 0x2F60 +#IPC area + dcbst %r0, %r7 + +#stat + mr %r7, %r5 +#0x00 + dcbst %r0, %r7 + addi %r7, %r7, 0x20 +#0x20 + dcbst %r0, %r7 + addi %r7, %r7, 0x20 +#0x40 + dcbst %r0, %r7 + addi %r7, %r7, 0x20 +#0x60 + dcbst %r0, %r7 + sc + + lis %r7, 0xC000 + lis %r0, 0xC400 + + stw %r0, 0x2F60(%r7) + stw %r4, 0x2F64(%r7) + stw %r5, 0x2F68(%r7) + + li %r0, 3 + stw %r0, 0x2F78(%r7) + +ready_loop: + lwz %r0, 0x2F78(%r7) + cmpwi %r0, 3 + beq ready_loop + +wait_loop: + lwz %r0, 0x2F9C(%r7) + andi. %r0, %r0, 0x14 + cmpwi %r0, 0 + beq wait_loop + + cmpwi %r12, 0 + beq skip_cb + mtctr %r12 + li %r3, 0 + li %r4, 0 + bctrl + +skip_cb: + li %r3, 0 + mr %r4, %r3 + + lwz %r0, 0x14(%sp) + addi %sp, %sp, 0x10 + mtlr %r0 + blr diff --git a/asm/CARDWrite.S b/asm/CARDWrite.S new file mode 100644 index 0000000..e4f2529 --- /dev/null +++ b/asm/CARDWrite.S @@ -0,0 +1,80 @@ +#include + +# +# r3 file * +# r4 buffer +# r5 length +# r6 offset +# r7 cb + +CardWrite: + + mflr %r0 + stw %r0, 4(%sp) + stwu %sp, -0x10(%sp) + +#Update fileinfo + stw %r5, 0x0C(%r3) + stw %r6, 0x08(%r3) + +#send cmd to DM + + mr %r12, %r7 + + li %r0, 0 + lis %r7, 0xC000 + addi %r7, %r7, 0x2F60 +#IPC area + dcbi %r0, %r7 + + srwi %r7, %r5, 5 + mtctr %r7 + mr %r7, %r4 +invalidloop: + dcbst %r0, %r7 + addi %r7, %r7, 0x20 + bdnz invalidloop + sc + + lis %r7, 0xC000 + lis %r0, 0xC800 + + stw %r0, 0x2F60(%r7) + + stw %r4, 0x2F64(%r7) + stw %r5, 0x2F68(%r7) + + stw %r6, 0x2F6C(%r7) + + lwz %r0, 0x04(%r3) + stw %r0, 0x2F70(%r7) + + li %r0, 3 + stw %r0, 0x2F78(%r7) + +ready_loop: + lwz %r0, 0x2F78(%r7) + cmpwi %r0, 3 + beq ready_loop + +wait_loop: + lwz %r0, 0x2F9C(%r7) + andi. %r0, %r0, 0x14 + cmpwi %r0, 0 + beq wait_loop + + cmpwi %r12, 0 + beq skip_cb + mtctr %r12 + li %r3, 0 + li %r4, 0 + bctrl + +skip_cb: + li %r3, 0 + mr %r4, %r3 + + lwz %r0, 0x14(%sp) + addi %sp, %sp, 0x10 + mtlr %r0 + blr diff --git a/asm/DVDInquiryAsync.S b/asm/DVDInquiryAsync.S new file mode 100644 index 0000000..794189c --- /dev/null +++ b/asm/DVDInquiryAsync.S @@ -0,0 +1,60 @@ +#include + +# issue read command +# +# r3 dvdstruct +# r4 dst +# r5 cb + +DVDInquiryAsync: + + stwu %sp, -0x10(%sp) + mflr %r0 + stw %r0, 8(%sp) + +#update dvdstruct + + li %r0, 0 + stw %r0, 0x00(%r3) + stw %r0, 0x04(%r3) + stw %r0, 0x0C(%r3) +#offset + stw %r0, 0x10(%r3) + + li %r0, 2 + stw %r0, 0x08(%r3) + + li %r0, 0x20 +#TransferSize + stw %r0, 0x1C(%r3) + stw %r0, 0x20(%r3) +#size + stw %r0, 0x14(%r3) + +#ptr + stw %r4, 0x18(%r3) +#cb + stw %r5, 0x28(%r3) + +#Inquiry reply + + li %r0, 0 + dcbi %r0, %r4 + + li %r0, 0x0000 + sth %r0, 0x02(%r4) + + cmpwi %r5, 0 + beq skip_cb + mtctr %r5 + mr %r4, %r3 + li %r3, 0x20 + bctrl + +skip_cb: + li %r3, 1 + + lwz %r0, 8(%sp) + mtlr %r0 + addi %sp, %sp, 0x10 + blr diff --git a/asm/DVDSeekAbsAsyncPrio.S b/asm/DVDSeekAbsAsyncPrio.S new file mode 100644 index 0000000..fd31c91 --- /dev/null +++ b/asm/DVDSeekAbsAsyncPrio.S @@ -0,0 +1,47 @@ +#include + +# issue read command +# +# r3 dvdstruct +# r4 off +# r5 cb +# r6 prio + +DVDSeekAbsAsyncPrio: + + stwu %sp, -0x10(%sp) + mflr %r0 + stw %r0, 8(%sp) + +#update dvdstruct + + li %r0, 0 + stw %r0, 0x00(%r3) + stw %r0, 0x04(%r3) + stw %r0, 0x1C(%r3) + li %r0, 2 + stw %r0, 0x08(%r3) + + li %r0, 0 + stw %r0, 0x0C(%r3) + +#off + stw %r4, 0x10(%r3) +#cb + stw %r5, 0x28(%r3) + + cmpwi %r5, 0 + beq skip_cb + mtctr %r5 + mr %r4, %r3 + li %r3, 0 + bctrl + +skip_cb: + + li %r3, 1 + + lwz %r0, 8(%sp) + mtlr %r0 + addi %sp, %sp, 0x10 + blr diff --git a/asm/__CARDSync.S b/asm/__CARDSync.S new file mode 100644 index 0000000..663d96d --- /dev/null +++ b/asm/__CARDSync.S @@ -0,0 +1,9 @@ +#include + +# +# r3 *Async result + +__CARDSync: + + mr %r3, %r4 + blr diff --git a/asm/make.cmd b/asm/make.cmd new file mode 100644 index 0000000..0617458 --- /dev/null +++ b/asm/make.cmd @@ -0,0 +1,4 @@ +@echo off +for /R %%i IN (*.S) DO E:\devkitPro\devkitPPC\bin\powerpc-eabi-as.exe %%i -o %%~ni.elf +for /R %%i IN (*.S) DO E:\devkitPro\devkitPPC\bin\powerpc-eabi-strip.exe -s %%~ni.elf -O binary -o %%~ni.bin +for /R %%i in (*.bin) DO "C:\Users\crediar\Documents\Visual Studio 2010\Projects\bin2h\Release\bin2h.exe" %%~ni.bin diff --git a/asm/padipc.S b/asm/padipc.S new file mode 100644 index 0000000..e5544a0 --- /dev/null +++ b/asm/padipc.S @@ -0,0 +1,11 @@ +#include + + +PADRead: + + lis %r4, 0xCC00 + lwz %r0, 0x6404(%r4) + lwz %r4, 0x6408(%r4) + lis %r4, 0xC000 + stw %r0, 0x12FC(%r4) + blr diff --git a/bsdtypes.h b/bsdtypes.h new file mode 100644 index 0000000..9703b30 --- /dev/null +++ b/bsdtypes.h @@ -0,0 +1,32 @@ +#ifndef __BSDTYPES_H__ +#define __BSDTYPES_H__ + +#include "global.h" +#include "errno.h" + +typedef u32 u_int; +//typedef u32 u_int32_t; +typedef u16 u_int16_t; +typedef u8 u_int8_t; +typedef u8 u_char; + +typedef u32 bus_space_tag_t; +typedef u32 bus_space_handle_t; + +struct device { + char dv_xname[255]; + void *dummy; +}; + +#define MIN(a, b) (((a)>(b))?(b):(a)) + +#define wakeup(...) + +#define bzero(mem, size) memset8(mem, 0, size) + +#define ISSET(var, mask) (((var) & (mask)) ? 1 : 0) +#define SET(var, mask) ((var) |= (mask)) + +#endif + + diff --git a/common.c b/common.c new file mode 100644 index 0000000..2fea95f --- /dev/null +++ b/common.c @@ -0,0 +1,2 @@ +#include "string.h" +#include "global.h" diff --git a/dip.c b/dip.c new file mode 100644 index 0000000..a8df3d2 --- /dev/null +++ b/dip.c @@ -0,0 +1,292 @@ +#include "dip.h" + +u32 StreamBufferSize= 54*1024; +u32 Streaming = 0; +u32 StreamOffset = 0; +u32 StreamDiscOffset= 0; +s32 StreamSize = 0; +u32 StreamRAMOffset = 0; +u32 StreamTimer = 0; +u32 StreamStopEnd = 0; +u32 GameRun = 0; +u32 DOLMinOff = 0; +u32 DOLMaxOff = 0; +u32 DOLSize = 0; +u32 DOLOffset = 0; +s32 ELFNumberOfSections = 0; +u32 FSTMode = 0; + +FIL GameFile; +u32 read; +u32 DiscRead=0; + +char *getfilenamebyoffset(u32 offset) +{ + u32 fst_offset = read32(0x38) & ~0x80000000; + + u32 i; + for (i = fst_offset + 12; i < 0x01800000; i+=12) + { + if (read8(i) == 0 && offset >= read32(i + 4) && offset < read32(i + 4) + read32(i + 8)) + { + return (char *)(fst_offset + read32(fst_offset+8)*12 + (read32(i) & 0x00ffffff)); + } + } + return (char*)NULL; +} + +void DIInit( void ) +{ + memset32( (void*)DI_BASE, 0xdeadbeef, 0x30 ); + memset32( (void*)(DI_SHADOW), 0, 0x30 ); + + write32( DI_SCONFIG, 0xFF ); + write32( DI_SCOVER, 0 ); +} +u32 DIUpdateRegisters( void ) +{ + u32 read,i,j; + static u32 PatchState = 0; + static u32 DOLReadSize= 0; + + if( read32(DI_CONTROL) != 0xdeadbeef ) + { + write32( DI_SCONTROL, read32(DI_CONTROL) & 3 ); + + clear32( DI_SSTATUS, 0x14 ); + + write32( DI_CONTROL, 0xdeadbeef ); + + if( read32(DI_SCONTROL) & 1 ) + { + if( ConfigGetConfig(DML_CFG_ACTIVITY_LED) ) + set32( HW_GPIO_OUT, 1<<5 ); + + if( read32(DI_CMD_0) != 0xdeadbeef ) + { + write32( DI_SCMD_0, read32(DI_CMD_0) ); + write32( DI_CMD_0, 0xdeadbeef ); + } + + if( read32(DI_CMD_1) != 0xdeadbeef ) + { + write32( DI_SCMD_1, read32(DI_CMD_1) ); + write32( DI_CMD_1, 0xdeadbeef ); + } + + if( read32(DI_CMD_2) != 0xdeadbeef ) + { + write32( DI_SCMD_2, read32(DI_CMD_2) ); + write32( DI_CMD_2, 0xdeadbeef ); + } + + if( read32(DI_DMA_ADR) != 0xdeadbeef ) + { + write32( DI_SDMA_ADR, read32(DI_DMA_ADR) ); + write32( DI_DMA_ADR, 0xdeadbeef ); + } + + if( read32(DI_DMA_LEN) != 0xdeadbeef ) + { + write32( DI_SDMA_LEN, read32(DI_DMA_LEN) ); + write32( DI_DMA_LEN, 0xdeadbeef ); + } + + if( read32(DI_IMM) != 0xdeadbeef ) + { + write32( DI_SIMM, read32(DI_IMM) ); + write32( DI_IMM, 0xdeadbeef ); + } + + switch( read32(DI_SCMD_0) >> 24 ) + { + case 0xA7: + case 0xA9: + //dbgprintf("DIP:Async!\n"); + case 0xA8: + { + u32 Buffer = P2C(read32(DI_SDMA_ADR)); + u32 Length = read32(DI_SCMD_2); + u32 Offset = read32(DI_SCMD_1) << 2; + + // dbgprintf("DIP:DVDRead%02X( 0x%08x, 0x%08x, 0x%08x )\n", read32(DI_SCMD_0) >> 24, Offset, Length, Buffer|0x80000000 ); + + // udelay(250); + + if( FSTMode ) + { + FSTRead( (char*)Buffer, Length, Offset ); + + } else { + + if( GameFile.fptr != Offset ) + if( f_lseek( &GameFile, Offset ) != FR_OK ) + { + EXIControl(1); + dbgprintf("DIP:Failed to seek to 0x%08x\n", Offset ); + while(1); + } + if( f_read( &GameFile, (char*)Buffer, Length, &read ) != FR_OK ) + { + EXIControl(1); + dbgprintf("DIP:Failed to read from 0x%08x to 0x%08X\n", Offset, Buffer ); + while(1); + } + } + //if( ((read+31)&(~31)) != Length ) + //{ + // dbgprintf("DIP:DVDLowRead Offset:%08X Size:%08d Dst:%08X\n", Offset, Length, Buffer ); + // dbgprintf("DIP:Failed to read %d bytes, only got %d\n", Length, read ); + // break; + //} + + if( (u32)Buffer == 0x01300000 ) + { + DoPatches( (char*)(0x01300000), Length, 0x80000000 ); + } + + if( PatchState == 0 ) + { + if( Length == 0x100 ) + { + if( read32( (u32)Buffer ) == 0x100 ) + { + //quickly calc the size + DOLSize = sizeof(dolhdr); + dolhdr *dol = (dolhdr*)Buffer; + + for( i=0; i < 7; ++i ) + DOLSize += dol->sizeText[i]; + for( i=0; i < 11; ++i ) + DOLSize += dol->sizeData[i]; + + DOLReadSize = sizeof(dolhdr); + + DOLMinOff=0x81800000; + DOLMaxOff=0; + + for( i=0; i < 7; ++i ) + { + if( dol->addressText[i] == 0 ) + continue; + + if( DOLMinOff > dol->addressText[i]) + DOLMinOff = dol->addressText[i]; + + if( DOLMaxOff < dol->addressText[i] + dol->sizeText[i] ) + DOLMaxOff = dol->addressText[i] + dol->sizeText[i]; + } + DOLMinOff -= 0x80000000; + DOLMaxOff -= 0x80000000; + + dbgprintf("DIP:DOLSize:%d DOLMinOff:0x%08X DOLMaxOff:0x%08X\n", DOLSize, DOLMinOff, DOLMaxOff ); + + PatchState = 1; + } + } else if( read32(Buffer) == 0x7F454C46 ) + { + if (getfilenamebyoffset(Offset) != NULL) + { + dbgprintf("DIP:The Game is loading %s\n", getfilenamebyoffset(Offset)); + } else + { + dbgprintf("DIP:The Game is loading some .elf that is not in the fst...\n"); + } + for (i = ((*(u32 *)0x00000038) & ~0x80000000) + 16; i < 0x01800000; i+=12) // Search the fst for the dvd offset of the .elf file + { + if (*(u32 *)i == Offset) + { + DOLSize = *(u32 *)(i+4); + DOLReadSize = Length; + + if( DOLReadSize == DOLSize ) // The .elf is read completely already + { + dbgprintf("DIP:The .elf is read completely, file size: %u bytes\n", DOLSize); + DoPatches( (char*)(Buffer), Length, 0x80000000 ); + } else // a part of the .elf is read + { + PatchState = 2; + DOLMinOff=Buffer; + DOLMaxOff=Buffer+Length; + if (Length <= 4096) // The .elf header is read + { + ELFNumberOfSections = read16(Buffer+0x2c) -2; // Assume that 2 sections are .bss and .sbss which are not read + dbgprintf("DIP:The .elf header is read(%u bytes), .elf file size: %u bytes, number of sections to load: %u\n", Length, DOLSize, ELFNumberOfSections); + } else // The .elf is read into a buffer + { + ELFNumberOfSections = -1; // Make sure that ELFNumberOfSections == 0 does not become true + dbgprintf("DIP:The .elf is read into a buffer, read progress: %u/%u bytes\n", Length, DOLSize); + } + } + break; + } + } + } + + } else if ( PatchState != 0 ) + { + DOLReadSize += Length; + + if (PatchState == 2) + { + ELFNumberOfSections--; + + // DOLMinOff and DOLMaxOff are optimised when loading .dol files + if (DOLMinOff > Buffer) + DOLMinOff = Buffer; + + if (DOLMaxOff < Buffer+Length) + DOLMaxOff = Buffer+Length; + + if (ELFNumberOfSections < 0) + { + dbgprintf("DIP:.elf read progress: %u/%u bytes\n", DOLReadSize, DOLSize); + } else + { + dbgprintf("DIP:.elf read progress: %u/%u bytes, sections left: %u\n", DOLReadSize, DOLSize, ELFNumberOfSections); + } + } + + //dbgprintf("DIP:DOLSize:%d DOLReadSize:%d\n", DOLSize, DOLReadSize ); + if( DOLReadSize >= DOLSize || (PatchState == 2 && ELFNumberOfSections == 0)) + { + DoPatches( (char*)(DOLMinOff), DOLMaxOff-DOLMinOff, 0x80000000 ); + PatchState = 0; + } + } + + write32( DI_SDMA_LEN, 0 ); + + while( read32(DI_SCONTROL) & 1 ) + clear32( DI_SCONTROL, 1 ); + + set32( DI_SSTATUS, 0x3A ); + + if( (read32(DI_SCMD_0) >> 24) == 0xA7 ) + { + write32( 0x0d80000C, (1<<0) | (1<<4) ); + write32( HW_PPCIRQFLAG, read32(HW_PPCIRQFLAG) ); + write32( HW_ARMIRQFLAG, read32(HW_ARMIRQFLAG) ); + set32( 0x0d80000C, (1<<2) ); + } + + } break; + default: + { + EXIControl(1); + dbgprintf("DIP:Unknown CMD:%08X %08X %08X %08X %08X %08X\n", read32(DI_SCMD_0), read32(DI_SCMD_1), read32(DI_SCMD_2), read32(DI_SIMM), read32(DI_SDMA_ADR), read32(DI_SDMA_LEN) ); + while(1); + } break; + } + + if( ConfigGetConfig(DML_CFG_ACTIVITY_LED) ) + clear32( HW_GPIO_OUT, 1<<5 ); + + return 1; + } else { + ;//dbgprintf("DIP:DI_CONTROL:%08X:%08X\n", read32(DI_CONTROL), read32(DI_CONTROL) ); + } + } + + return 0; +} diff --git a/dip.h b/dip.h new file mode 100644 index 0000000..b7e047e --- /dev/null +++ b/dip.h @@ -0,0 +1,138 @@ +#ifndef _DIP_ +#define _DIP_ + +#include "global.h" +#include "string.h" +#include "alloc.h" +#include "ff.h" +#include "vsprintf.h" +#include "HW.h" +#include "dol.h" +#include "Patches.h" +#include "vsprintf.h" +#include "Config.h" +#include "DVD.h" + +enum opcodes +{ + DVD_IDENTIFY = 0x12, + DVD_READ_DISCID = 0x70, + DVD_LOW_READ = 0x71, + DVD_WAITFORCOVERCLOSE = 0x79, + DVD_READ_PHYSICAL = 0x80, + DVD_READ_COPYRIGHT = 0x81, + DVD_READ_DISCKEY = 0x82, + DVD_GETCOVER = 0x88, + DVD_RESET = 0x8A, + DVD_OPEN_PARTITION = 0x8B, + DVD_CLOSE_PARTITION = 0x8C, + DVD_READ_UNENCRYPTED = 0x8D, + DVD_REPORTKEY = 0xA4, + DVD_LOW_SEEK = 0xAB, + DVD_READ = 0xD0, + DVD_READ_CONFIG = 0xD1, + DVD_READ_BCA = 0xDA, + DVD_GET_ERROR = 0xE0, + DVD_SET_MOTOR = 0xE3, + + DVD_SELECT_GAME = 0x23, + DVD_GET_GAMECOUNT = 0x24, + DVD_EJECT_DISC = 0x27, + DVD_INSERT_DISC = 0x28, + DVD_READ_GAMEINFO = 0x30, +}; + +enum GameRegion +{ + JAP=0, + USA, + EUR, + KOR, + ASN, + LTN, +}; + + +#define DI_BASE 0x00002F00 + +#define DI_STATUS (DI_BASE+0x00) +#define DI_COVER (DI_BASE+0x04) +#define DI_CMD_0 (DI_BASE+0x08) +#define DI_CMD_1 (DI_BASE+0x0C) +#define DI_CMD_2 (DI_BASE+0x10) +#define DI_DMA_ADR (DI_BASE+0x14) +#define DI_DMA_LEN (DI_BASE+0x18) +#define DI_CONTROL (DI_BASE+0x1C) +#define DI_IMM (DI_BASE+0x20) +#define DI_CONFIG (DI_BASE+0x24) + +#define DI_SHADOW (DI_BASE + 0x30) + +#define DI_SSTATUS (DI_SHADOW+0x00) +#define DI_SCOVER (DI_SHADOW+0x04) +#define DI_SCMD_0 (DI_SHADOW+0x08) +#define DI_SCMD_1 (DI_SHADOW+0x0C) +#define DI_SCMD_2 (DI_SHADOW+0x10) +#define DI_SDMA_ADR (DI_SHADOW+0x14) +#define DI_SDMA_LEN (DI_SHADOW+0x18) +#define DI_SCONTROL (DI_SHADOW+0x1C) +#define DI_SIMM (DI_SHADOW+0x20) +#define DI_SCONFIG (DI_SHADOW+0x24) + +typedef struct +{ + union + { + struct + { + u32 Type :8; + u32 NameOffset :24; + }; + u32 TypeName; + }; + union + { + struct // File Entry + { + u32 FileOffset; + u32 FileLength; + }; + struct // Dir Entry + { + u32 ParentOffset; + u32 NextOffset; + }; + u32 entry[2]; + }; +} FEntry; + +typedef struct +{ + u32 Offset; + u32 Size; + FIL File; +} FileCache; + +#define FILECACHE_MAX 1 + +typedef struct +{ + u8 *data; + u32 len; +} vector; + +typedef struct +{ + u32 TMDSize; + u32 TMDOffset; + u32 CertChainSize; + u32 CertChainOffset; + u32 H3TableOffset; + u32 DataOffset; + u32 DataSize; +} PartitionInfo; + +void DIInit( void ); +u32 DIUpdateRegisters( void ); + +#endif diff --git a/diskio.c b/diskio.c new file mode 100644 index 0000000..020f781 --- /dev/null +++ b/diskio.c @@ -0,0 +1,93 @@ +#include "diskio.h" +#include "string.h" +#include "memory.h" + +extern u32 IsInit; + +DSTATUS disk_initialize( BYTE drv ) +{ + udelay( 50000 ); + + tiny_ehci_init(); + + int ret = -ENODEV; + + do { + + udelay( 4000 ); + ret = ehci_discover(); + + } while( ret == -ENODEV ); + + dbgprintf("ehci_discover():%d\n", ret ); + + s32 r = USBStorage_Init(); + + u32 s_size; + u32 s_cnt = USBStorage_Get_Capacity(&s_size); + + dbgprintf( "DIP: Drive size: %dMB SectorSize:%d\n", s_cnt / 1024 * s_size / 1024, s_size); + + return r; +} + +DSTATUS disk_status( BYTE drv ) +{ + (void)drv; + return 0; +} + +DRESULT disk_read( BYTE drv, BYTE *buff, DWORD sector, BYTE count ) +{ + u8 *buffer = (u8*)buff; + //dbgprintf("disk_read( %d, %d, %p, %p)\n", sector, count, buff, buffer ); + + if( (u32)buff & 0xF0000000 ) + { + buffer = (u8*)0x1000; + u32 i=0; + u32 Blocks = 3; + while(1) + { + if( (count-i) < Blocks ) + Blocks = (count-i); + + USBStorage_Read_Sectors( sector + i, Blocks, buffer ); + memcpy( buff + i * 512, buffer, Blocks * 512 ); + + i+=Blocks; + + if( i >= count ) + break; + } + } else { + USBStorage_Read_Sectors( sector, count, buffer ); + dc_flushrange( buffer, count*512 ); + ahb_flush_from( AHB_SDHC ); + } + + return RES_OK; +} +// Write Sector(s) +DRESULT disk_write( BYTE drv, const BYTE *buff, DWORD sector, BYTE count ) +{ + u8 *buffer = (u8*)buff; + + if( (u32)buff & 0xF0000000 ) + { + buffer = (u8*)0x1000; + u32 i; + for( i=0; i < count; ++i ) + { + memcpy( buffer, (void*)buff + i * 512, 512 ); + USBStorage_Write_Sectors( sector + i, 1, buffer ); + } + } else { + + ahb_flush_to( AHB_SDHC ); + USBStorage_Write_Sectors( sector, count, buffer ); + + } + + return RES_OK; +} diff --git a/diskio.h b/diskio.h new file mode 100644 index 0000000..ad01947 --- /dev/null +++ b/diskio.h @@ -0,0 +1,70 @@ +/*----------------------------------------------------------------------- +/ Low level disk interface modlue include file R0.07 (C)ChaN, 2009 +/----------------------------------------------------------------------- +/ FatFs module is an open source project to implement FAT file system to small +/ embedded systems. It is opened for education, research and development under +/ license policy of following trems. +/ +/ Copyright (C) 2009, ChaN, all right reserved. +/ +/ * The FatFs module is a free software and there is no warranty. +/ * You can use, modify and/or redistribute it for personal, non-profit or +/ commercial use without any restriction under your responsibility. +/ * Redistributions of source code must retain the above copyright notice. +/ +/----------------------------------------------------------------------------*/ +// original source: http://elm-chan.org/fsw/ff/00index_e.html + +#ifndef _DISKIO + +#define _READONLY 0 /* 1: Read-only mode */ +#define _USE_IOCTL 1 + +#include "integer.h" +#include "string.h" +#include "ehci.h" +#include "alloc.h" +#include "tiny_ehci_glue.h" +#include "dip.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 +#if _USE_IOCTL == 1 +DRESULT disk_ioctl (BYTE, BYTE, void*); +#endif + + +/* 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 */ + + +#if _USE_IOCTL == 1 +/* Command code for disk_ioctl() */ +#define CTRL_SYNC 0 /* Mandatory for write functions */ +#endif + +#define _DISKIO +#endif diff --git a/dol.h b/dol.h new file mode 100644 index 0000000..32b2d8a --- /dev/null +++ b/dol.h @@ -0,0 +1,16 @@ +#ifndef _DOL_ +#define _DOL_ + +typedef struct { + unsigned int offsetText[7]; + unsigned int offsetData[11]; + unsigned int addressText[7]; + unsigned int addressData[11]; + unsigned int sizeText[7]; + unsigned int sizeData[11]; + unsigned int addressBSS; + unsigned int sizeBSS; + unsigned int entrypoint; +} dolhdr; + +#endif diff --git a/ehci-mem.c b/ehci-mem.c new file mode 100644 index 0000000..94ce1fb --- /dev/null +++ b/ehci-mem.c @@ -0,0 +1,85 @@ +/* + * Original Copyright (c) 2001 by David Brownell + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* this file is part of ehci.c */ + + +static inline void ehci_qtd_init(struct ehci_qtd *qtd + ) +{ + dma_addr_t dma = ehci_virt_to_dma(qtd); + memset (qtd, 0, sizeof *qtd); + qtd->qtd_dma = dma; + qtd->hw_token = (QTD_STS_HALT); + qtd->hw_next = EHCI_LIST_END(); + qtd->hw_alt_next = EHCI_LIST_END(); +} +static inline struct ehci_qtd * ehci_qtd_alloc(void) +{ + struct ehci_qtd *qtd ; + //debug_printf("ehci_qtd used=%x\n",ehci->qtd_used); + BUG_ON(ehci->qtd_used>=EHCI_MAX_QTD); + qtd = ehci->qtds[ehci->qtd_used++]; + ehci_qtd_init(qtd); + return qtd; +} + +int ehci_mem_init (void) +{ + int i; + u32 ptr = 0x17FF000; +// ehci->periodic = ehci_maligned(DEFAULT_I_TDPS * sizeof(__le32),32,4096); + + ehci->periodic = (u32*)ptr; + + ehci->periodic_dma = ehci_virt_to_dma(ehci->periodic); + + for (i = 0; i < DEFAULT_I_TDPS; i++) + ehci->periodic[i] = EHCI_LIST_END(); + + ehci_writel(ehci->periodic_dma, &ehci->regs->frame_list); + + for(i=0;iqtds[i] = ehci_maligned(sizeof(struct ehci_qtd),32,4096); + + ptr -= sizeof(struct ehci_qtd); + ehci->qtds[i] = (struct ehci_qtd*)(ptr); + } + + ehci->qtd_used = 0; +// ehci->asyncqh = ehci_maligned(sizeof(struct ehci_qh),32,4096); + + ptr -= sizeof(struct ehci_qh); + ehci->asyncqh = (struct ehci_qh*)ptr; + + ehci->asyncqh->ehci = ehci; + ehci->asyncqh->qh_dma = ehci_virt_to_dma(ehci->asyncqh); + ehci->asyncqh->qtd_head = NULL; + +// ehci->async = ehci_maligned(sizeof(struct ehci_qh),32,4096); + + ptr -= sizeof(struct ehci_qh); + ehci->async = (struct ehci_qh*)ptr; + + ehci->async->ehci = ehci; + ehci->async->qh_dma = ehci_virt_to_dma(ehci->async); + ehci->async->qtd_head = NULL; + + return 0; +} diff --git a/ehci.c b/ehci.c new file mode 100644 index 0000000..47d40ca --- /dev/null +++ b/ehci.c @@ -0,0 +1,811 @@ +/* simplest usb-ehci driver which features: + + control and bulk transfers only + only one transfer pending + driver is synchronous (waiting for the end of the transfer) + endianess independant + no uncached memory allocation needed + + this driver is originally based on the GPL linux ehci-hcd driver + + * Original Copyright (c) 2001 by David Brownell + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* magic numbers that can affect system performance */ + +u32 IsInit = 0; + +#undef DEBUG + +#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ +#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ +#define EHCI_TUNE_RL_TT 0 +#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ +#define EHCI_TUNE_MULT_TT 1 +#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ +extern int verbose; +static void +dbg_qtd (const char *label, struct ehci_qtd *qtd) +{ + ehci_dbg( "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd, + hc32_to_cpup( &qtd->hw_next), + hc32_to_cpup( &qtd->hw_alt_next), + hc32_to_cpup( &qtd->hw_token), + hc32_to_cpup( &qtd->hw_buf [0])); + if (qtd->hw_buf [1]) + ehci_dbg( " p1=%08x p2=%08x p3=%08x p4=%08x\n", + hc32_to_cpup( &qtd->hw_buf[1]), + hc32_to_cpup( &qtd->hw_buf[2]), + hc32_to_cpup( &qtd->hw_buf[3]), + hc32_to_cpup( &qtd->hw_buf[4])); +} + +static void +dbg_qh (const char *label, struct ehci_qh *qh) +{ + ehci_dbg ( "%s qh %p n%08x info %x %x qtd %x\n", label, + qh, + hc32_to_cpu(qh->hw_next), + hc32_to_cpu(qh->hw_info1), + hc32_to_cpu(qh->hw_info2), + hc32_to_cpu(qh->hw_current)); + dbg_qtd ("overlay", (struct ehci_qtd *) &qh->hw_qtd_next); +} + +static void +dbg_command (void) +{ +#ifdef DEBUG + u32 command=ehci_readl( &ehci->regs->command); + u32 async=ehci_readl( &ehci->regs->async_next); + + ehci_dbg ("async_next: %08x\n",async); + ehci_dbg ( + "command %06x %s=%d ithresh=%d%s%s%s%s %s %s\n", + command, + (command & CMD_PARK) ? "park" : "(park)", + CMD_PARK_CNT (command), + (command >> 16) & 0x3f, + (command & CMD_LRESET) ? " LReset" : "", + (command & CMD_IAAD) ? " IAAD" : "", + (command & CMD_ASE) ? " Async" : "", + (command & CMD_PSE) ? " Periodic" : "", + (command & CMD_RESET) ? " Reset" : "", + (command & CMD_RUN) ? "RUN" : "HALT" + ); +#endif +} +static void +dbg_status (void) +{ +#ifdef DEBUG + u32 status=ehci_readl( &ehci->regs->status); + ehci_dbg ( + "status %04x%s%s%s%s%s%s%s%s%s%s\n", + status, + (status & STS_ASS) ? " Async" : "", + (status & STS_PSS) ? " Periodic" : "", + (status & STS_RECL) ? " Recl" : "", + (status & STS_HALT) ? " Halt" : "", + (status & STS_IAA) ? " IAA" : "", + (status & STS_FATAL) ? " FATAL" : "", + (status & STS_FLR) ? " FLR" : "", + (status & STS_PCD) ? " PCD" : "", + (status & STS_ERR) ? " ERR" : "", + (status & STS_INT) ? " INT" : "" + ); +#endif +} + +void debug_qtds(void) +{ + struct ehci_qh *qh = ehci->async; + struct ehci_qtd *qtd; + dbg_qh ("qh",qh); + dbg_command (); + dbg_status (); + for(qtd = qh->qtd_head; qtd; qtd = qtd->next) + { + ehci_dma_unmap_bidir(qtd->qtd_dma,sizeof(struct ehci_qtd)); + dbg_qtd("qtd",qtd); + ehci_dma_map_bidir(qtd,sizeof(struct ehci_qtd)); + } + +} +void dump_qh(struct ehci_qh *qh) +{ + struct ehci_qtd *qtd; + dbg_command (); + dbg_status (); + ehci_dma_unmap_bidir(qh->qh_dma,sizeof(struct ehci_qh)); + dbg_qh("qh",qh); + print_hex_dump_bytes("qh:",DUMP_PREFIX_OFFSET,(void*)qh,12*4); + for(qtd = qh->qtd_head; qtd; qtd = qtd->next){ + u32 *buf; + ehci_dma_unmap_bidir(qtd->qtd_dma,sizeof(struct ehci_qtd)); + dbg_qtd("qtd",qtd); + print_hex_dump_bytes("qtd:",DUMP_PREFIX_OFFSET,(void*)qtd,8*4); + buf = (u32*)hc32_to_cpu(qtd->hw_buf[0]); + if(buf) + print_hex_dump_bytes("qtd buf:",DUMP_PREFIX_OFFSET,(void*)(buf),8*4); + + } +} + +/*-------------------------------------------------------------------------*/ + +/* + * handshake - spin reading hc until handshake completes or fails + * @ptr: address of hc register to be read + * @mask: bits to look at in result of read + * @done: value of those bits when handshake succeeds + * @usec: timeout in microseconds + * + * Returns negative errno, or zero on success + * + * Success happens when the "mask" bits have the specified value (hardware + * handshake done). There are two failure modes: "usec" have passed (major + * hardware flakeout), or the register reads as all-ones (hardware removed). + * + * That last failure should_only happen in cases like physical cardbus eject + * before driver shutdown. But it also seems to be caused by bugs in cardbus + * bridge shutdown: shutting down the bridge before the devices using it. + */ +static int handshake (void __iomem *ptr, + u32 mask, u32 done, int usec) +{ + u32 result; + do { + result = ehci_readl( ptr); + if (result == ~(u32)0) /* card removed */ + return -ENODEV; + result &= mask; + if (result == done) + return 0; + udelay (1); + usec--; + } while (usec > 0); + + ehci_dbg("\nhandshake timeout!!\n\n"); + dump_qh(ehci->async); + dump_qh(ehci->asyncqh); + //BUG(); + return -ETIMEDOUT; +} + +#include "ehci-mem.c" +/* one-time init, only for memory state */ +static int ehci_init(void) +{ + int retval; + if ((retval = ehci_mem_init()) < 0) + return retval; + /* + * dedicate a qh for the async ring head, since we couldn't unlink + * a 'real' qh without stopping the async schedule [4.8]. use it + * as the 'reclamation list head' too. + * its dummy is used in hw_alt_next of many tds, to prevent the qh + * from automatically advancing to the next td after short reads. + */ + ehci->async->hw_next = QH_NEXT( ehci->async->qh_dma); + ehci->async->hw_info1 = cpu_to_hc32( QH_HEAD); + ehci->async->hw_token = cpu_to_hc32( QTD_STS_HALT); + ehci->async->hw_qtd_next = EHCI_LIST_END(); + ehci->async->hw_alt_next = EHCI_LIST_END();//QTD_NEXT( ehci->async->dummy->qtd_dma); + ehci->ctrl_buffer = USB_Alloc(sizeof(usbctrlrequest)); + ehci->command = 0; + ehci_writel( 0x00800002, &ehci->regs->command); + ehci_writel( ehci->periodic_dma, &ehci->regs->frame_list); + ehci_writel( ehci->async->qh_dma, &ehci->regs->async_next); + ehci_writel( 0x00010009, &ehci->regs->command); + ehci_writel( 1, &ehci->regs->configured_flag); + ehci_writel( 0x00010029, &ehci->regs->command); + + return 0; +} + +/* fill a qtd, returning how much of the buffer we were able to queue up */ +static int qtd_fill( struct ehci_qtd *qtd, dma_addr_t buf, size_t len, int token, int maxpacket) +{ + int i, count; + u64 addr = buf; + //ehci_dbg("fill qtd with dma %X len %X\n",buf,len); + /* one buffer entry per 4K ... first might be short or unaligned */ + qtd->hw_buf[0] = cpu_to_hc32( (u32)addr); + qtd->hw_buf_hi[0] = 0; + count = 0x1000 - (buf & 0x0fff); /* rest of that page */ + if (likely (len < count)) /* ... iff needed */ + count = len; + else { + buf += 0x1000; + buf &= ~0x0fff; + + /* per-qtd limit: from 16K to 20K (best alignment) */ + for (i = 1; count < len && i < 5; i++) { + addr = buf; + qtd->hw_buf[i] = cpu_to_hc32( (u32)addr); + qtd->hw_buf_hi[i] = cpu_to_hc32( + (u32)(addr >> 32)); + buf += 0x1000; + if ((count + 0x1000) < len) + count += 0x1000; + else + count = len; + } + + /* short packets may only terminate transfers */ + if (count != len) + count -= (count % maxpacket); + } + qtd->hw_token = cpu_to_hc32( (count << 16) | token); + qtd->length = count; + + return count; +} + +// high bandwidth multiplier, as encoded in highspeed endpoint descriptors +#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) +// ... and packet size, for any kind of endpoint descriptor +#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff) + +/* + * reverse of qh_urb_transaction: free a list of TDs. + * also count the actual transfer length. + * + */ +static void qh_end_transfer (void) +{ + struct ehci_qtd *qtd; + struct ehci_qh *qh = ehci->asyncqh; + u32 token; + int error = 0; + for(qtd = qh->qtd_head; qtd; qtd = qtd->next) + { + token = hc32_to_cpu( qtd->hw_token); + if (likely (QTD_PID (token) != 2)) + qtd->urb->actual_length += qtd->length - QTD_LENGTH (token); + + if (!(qtd->length ==0 && ((token & 0xff)==QTD_STS_HALT)) && (token & QTD_STS_HALT)) + { + ehci_dbg("\nqtd error!:"); + if (token & QTD_STS_BABBLE) + { + /* FIXME "must" disable babbling device's port too */ + ehci_dbg(" BABBLE"); + } + if (token & QTD_STS_MMF) + { + /* fs/ls interrupt xfer missed the complete-split */ + ehci_dbg(" missed micro frame"); + } + if (token & QTD_STS_DBE) + { + ehci_dbg(" databuffer error"); + } + if (token & QTD_STS_XACT) + { + ehci_dbg(" wrong ack"); + } + if (QTD_CERR (token)==0) + ehci_dbg(" toomany errors"); + + ehci_dbg("\n"); + error = -1; + } + } + if(error) + { + dump_qh(ehci->asyncqh); + qtd->urb->actual_length = error; + } + ehci->qtd_used = 0; +} + +/* + * create a list of filled qtds for this URB; won't link into qh. + */ +struct ehci_qtd *qh_urb_transaction ( + struct ehci_urb *urb +) { + struct ehci_qtd *qtd, *qtd_prev; + struct ehci_qtd *head; + dma_addr_t buf; + int len, maxpacket; + int is_input; + u32 token; + + /* + * URBs map to sequences of QTDs: one logical transaction + */ + head = qtd = ehci_qtd_alloc (); + qtd->urb = urb; + + urb->actual_length = 0; + token = QTD_STS_ACTIVE; + token |= (EHCI_TUNE_CERR << 10); + /* for split transactions, SplitXState initialized to zero */ + + len = urb->transfer_buffer_length; + is_input = urb->input; + if (urb->ep==0) {/* is control */ + /* SETUP pid */ + qtd_fill( qtd, urb->setup_dma, sizeof (usbctrlrequest), token | (2 /* "setup" */ << 8), 8); + + /* ... and always at least one more pid */ + token ^= QTD_TOGGLE; + qtd_prev = qtd; + qtd = ehci_qtd_alloc (); + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT( qtd->qtd_dma); + qtd_prev->next = qtd; + + /* for zero length DATA stages, STATUS is always IN */ + if (len == 0) + token |= (1 /* "in" */ << 8); + } + + /* + * data transfer stage: buffer setup + */ + buf = urb->transfer_dma; + + if (is_input) + token |= (1 /* "in" */ << 8); + /* else it's already initted to "out" pid (0 << 8) */ + + maxpacket = max_packet(urb->maxpacket); + + /* + * buffer gets wrapped in one or more qtds; + * last one may be "short" (including zero len) + * and may serve as a control status ack + */ + for (;;) { + int this_qtd_len; + + this_qtd_len = qtd_fill( qtd, buf, len, token, maxpacket); + len -= this_qtd_len; + buf += this_qtd_len; + + /* + * short reads advance to a "magic" dummy instead of the next + * qtd ... that forces the queue to stop, for manual cleanup. + * (this will usually be overridden later.) + */ + if (is_input) + qtd->hw_alt_next = ehci->asyncqh->hw_alt_next; + + /* qh makes control packets use qtd toggle; maybe switch it */ + if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0) + token ^= QTD_TOGGLE; + + if (likely (len <= 0)) + break; + + qtd_prev = qtd; + qtd = ehci_qtd_alloc (); + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT( qtd->qtd_dma); + qtd_prev->next = qtd; + } + + qtd->hw_alt_next = EHCI_LIST_END(); + + /* + * control requests may need a terminating data "status" ack; + * bulk ones may need a terminating short packet (zero length). + */ + if (likely (urb->transfer_buffer_length != 0)) { + int one_more = 0; + + if (urb->ep==0) { + one_more = 1; + token ^= 0x0100; /* "in" <--> "out" */ + token |= QTD_TOGGLE; /* force DATA1 */ + } else if(!(urb->transfer_buffer_length % maxpacket)) { + //one_more = 1; + } + if (one_more) { + qtd_prev = qtd; + qtd = ehci_qtd_alloc (); + qtd->urb = urb; + qtd_prev->hw_next = QTD_NEXT( qtd->qtd_dma); + qtd_prev->next = qtd; + + /* never any data in such packets */ + qtd_fill( qtd, 0, 0, token, 0); + } + } + + /* by default, enable interrupt on urb completion */ + qtd->hw_token |= cpu_to_hc32( QTD_IOC); + return head; +} +int ehci_do_urb ( struct ehci_device *dev, struct ehci_urb *urb) +{ + struct ehci_qh *qh; + struct ehci_qtd *qtd; + u32 info1 = 0, info2 = 0; + int is_input; + int maxp = 0; + int retval; + + //ehci_dbg ("do urb %X %X ep %X\n",urb->setup_buffer,urb->transfer_buffer,urb->ep); + if(urb->ep==0) //control message + urb->setup_dma = ehci_dma_map_to(urb->setup_buffer,sizeof (usbctrlrequest)); + + if( urb->transfer_buffer_length ) + { + if(urb->input) + urb->transfer_dma = ehci_dma_map_to( urb->transfer_buffer, urb->transfer_buffer_length ); + else + urb->transfer_dma = ehci_dma_map_from( urb->transfer_buffer, urb->transfer_buffer_length ); + } + + qh = ehci->asyncqh; + memset(qh,0,12*4); + qtd = qh_urb_transaction( urb ); + qh->qtd_head = qtd; + + info1 |= ((urb->ep)&0xf)<<8; + info1 |= dev->id; + is_input = urb->input; + maxp = urb->maxpacket; + + info1 |= (2 << 12); /* EPS "high" */ + if(urb->ep==0)// control + { + info1 |= (EHCI_TUNE_RL_HS << 28); + info1 |= 64 << 16; /* usb2 fixed maxpacket */ + info1 |= 1 << 14; /* toggle from qtd */ + info2 |= (EHCI_TUNE_MULT_HS << 30); + } else {//bulk + + info1 |= (EHCI_TUNE_RL_HS << 28); + /* The USB spec says that high speed bulk endpoints + * always use 512 byte maxpacket. But some device + * vendors decided to ignore that, and MSFT is happy + * to help them do so. So now people expect to use + * such nonconformant devices with Linux too; sigh. + */ + info1 |= max_packet(maxp) << 16; + info2 |= (EHCI_TUNE_MULT_HS << 30); + } + + //ehci_dbg("HW info: %08X\n",info1); + qh->hw_info1 = cpu_to_hc32( info1); + qh->hw_info2 = cpu_to_hc32( info2); + + qh->hw_next = QH_NEXT( qh->qh_dma ); + qh->hw_qtd_next = QTD_NEXT( qtd->qtd_dma ); + qh->hw_alt_next = EHCI_LIST_END(); + + if( urb->ep != 0 ) + { + if( get_toggle( dev, urb->ep ) ) + qh->hw_token |= cpu_to_hc32(QTD_TOGGLE); + else + qh->hw_token &= ~cpu_to_hc32(QTD_TOGGLE); + + //ehci_dbg("toggle for ep %x: %d %x\n", urb->ep, get_toggle(dev,urb->ep), qh->hw_token ); + } + + qh->hw_token &= cpu_to_hc32( QTD_TOGGLE | QTD_STS_PING); + + qh->hw_next = QH_NEXT(ehci->async->qh_dma); + + + ehci_dma_map_bidir(qh,sizeof(struct ehci_qh)); + for(qtd = qh->qtd_head; qtd; qtd = qtd->next) + ehci_dma_map_bidir(qtd,sizeof(struct ehci_qtd)); + + // start (link qh) + ehci->async->hw_next = QH_NEXT(qh->qh_dma); + ehci_dma_map_bidir(ehci->async,sizeof(struct ehci_qh)); + + retval = handshake(&ehci->regs->status,STS_INT,STS_INT,1000*1000); + + //print_hex_dump_bytes ("qh mem",0,(void*)qh,17*4); + //retval = poll_transfer_end(1000*1000); + ehci_dma_unmap_bidir(ehci->async->qh_dma,sizeof(struct ehci_qh)); + ehci_dma_unmap_bidir(qh->qh_dma,sizeof(struct ehci_qh)); + + for(qtd = qh->qtd_head; qtd; qtd = qtd->next) + ehci_dma_unmap_bidir(qtd->qtd_dma,sizeof(struct ehci_qtd)); + + // stop (unlink qh) + ehci->async->hw_next = QH_NEXT(ehci->async->qh_dma); + ehci_dma_map_bidir(ehci->async,sizeof(struct ehci_qh)); + ehci_dma_unmap_bidir(ehci->async->qh_dma,sizeof(struct ehci_qh)); + + // ack + ehci_writel( STS_RECL|STS_IAA|STS_INT, &ehci->regs->status); + + if(urb->ep!=0) + { + set_toggle(dev,urb->ep,(qh->hw_token & cpu_to_hc32(QTD_TOGGLE))?1:0); + //ehci_dbg("toggle for ep %x: %d %d %x %X\n",urb->ep,get_toggle(dev,urb->ep),(qh->hw_token & cpu_to_hc32(QTD_TOGGLE)),qh->hw_token,dev->toggles); + } + + if( retval >= 0 ) + // wait hc really stopped + retval = handshake(&ehci->regs->async_next,~0,ehci->async->qh_dma,1*1000); + //release memory, and actualise urb->actual_length + qh_end_transfer(); + + if(urb->transfer_buffer_length) + { + if(urb->input) + ehci_dma_unmap_to(urb->transfer_dma,urb->transfer_buffer_length); + else + ehci_dma_unmap_from(urb->transfer_dma,urb->transfer_buffer_length); + } + + if( urb->ep == 0 ) //control message + ehci_dma_unmap_to(urb->setup_dma,sizeof (usbctrlrequest)); + + if(retval==0) + { + return urb->actual_length; + } + ehci_dbg ( "un successfull urb %d!!\n", retval); + return retval; +} + +s32 ehci_control_message(struct ehci_device *dev,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *buf) +{ + struct ehci_urb urb; + int ret; + usbctrlrequest *req = (void*)0x17C0; + + if( verbose ) + ehci_dbg("control msg: rt%02X r%02X v%04X i%04X s%04x %p\n", bmRequestType, bmRequest, wValue, wIndex, wLength, buf ); + + u32 *_req = (u32*)req; + + _req[0] = (bmRequestType<<24) | (bmRequest<<16) | swab16(wValue); + _req[1] = (swab16(wIndex) << 16) | swab16(wLength); + + urb.setup_buffer = req; + urb.ep = 0; + urb.input = (bmRequestType&USB_CTRLTYPE_DIR_DEVICE2HOST)!=0; + urb.maxpacket = 64; + urb.transfer_buffer_length = wLength; + + if( urb.transfer_buffer_length ) + { + if( ((u32)buf >> 28 ) == 0xF ) + { + urb.transfer_buffer = USB_Alloc( wLength ); + + dbgprintf("memcpy(%p,%p,%u)\n", urb.transfer_buffer, buf, wLength ); + memcpy( urb.transfer_buffer, buf, wLength ); + + ret = ehci_do_urb( dev, &urb ); + + memcpy( buf, urb.transfer_buffer, wLength ); + USB_Free( urb.transfer_buffer ); + + } else { + urb.transfer_buffer = buf; + ret = ehci_do_urb( dev, &urb ); + } + + //hexdump( buf, wLength > 0x20 ? 0x20 : wLength ); + + } else { + + urb.transfer_buffer = NULL; + ret = ehci_do_urb( dev, &urb ); + } + + return ret; +} +s32 ehci_bulk_message(struct ehci_device *dev,u8 bEndpoint,u16 wLength,void *rpData) +{ + struct ehci_urb urb; + s32 ret; + urb.setup_buffer = NULL; + urb.ep = bEndpoint; + urb.input = (bEndpoint&0x80)!=0; + urb.maxpacket = 512; + urb.transfer_buffer_length = wLength; + urb.transfer_buffer = rpData; + + if (verbose) + ehci_dbg ( "bulk msg: ep:%02X size:%02X addr:%04X\n", bEndpoint, wLength, urb.transfer_buffer ); + +// hexdump( urb.transfer_buffer, urb.transfer_buffer_length ); + if( ((u32)rpData >> 28) == 0xF ) + { + memcpy( (void*)0x1800, rpData, wLength ); + + urb.transfer_buffer = (u8*)0x1800; + + ret = ehci_do_urb( dev, &urb ); + + memcpy( rpData, (void*)0x1800, wLength ); + + } else { + ret = ehci_do_urb( dev, &urb ); + } + +// hexdump( urb.transfer_buffer, urb.transfer_buffer_length ); + + if (verbose) + ehci_dbg ( "==>%d\n", ret); + + return ret; +} + + +int ehci_reset_port(int port) +{ + u32 __iomem *status_reg = &ehci->regs->port_status[port]; + struct ehci_device *dev = &ehci->devices[port]; + u32 status = ehci_readl(status_reg); + int retval = 0; + dev->id = 0; + if ((PORT_OWNER&status) || !(PORT_CONNECT&status)) + { + ehci_writel( PORT_OWNER, status_reg); + //ehci_dbg ( "port %d had no usb2 device connected at startup %X \n", port,ehci_readl(status_reg)); + return -ENODEV;// no USB2 device connected + } + + ehci_dbg ( "port %d has usb2 device connected! reset it...\n", port); + ehci_writel( 0x1803,status_reg); + + while ((ehci_readl(status_reg) & 0x1801) != 0x1801) + { + ehci_dbg ( "Waiting for port %d to settle...(%04x)\n", port, ehci_readl(status_reg)); + ehci_writel( 0x1803,status_reg); + msleep(500); + } + + ehci_writel( 0x1903,status_reg); + //ehci_writel( PORT_OWNER|PORT_POWER|PORT_RESET,status_reg); + msleep(50);// wait 50ms for the reset sequence + ehci_writel( 0x1001,status_reg); + retval = handshake( status_reg, PORT_RESET, 0, 2000); + + if (retval != 0) + { + ehci_dbg ( "port %d reset error %d\n", port, retval); + return retval; + } + ehci_dbg ( "port %d reseted status:%04x...\n", port,ehci_readl(status_reg)); + msleep(50); + // now the device has the default device id + retval = ehci_control_message( dev, USB_CTRLTYPE_DIR_DEVICE2HOST, USB_REQ_GETDESCRIPTOR, USB_DT_DEVICE<<8, 0, sizeof(dev->desc), &dev->desc ); + + if (retval < 0) + { + ehci_dbg("unable to get device desc...\n"); + return retval; + } + + retval = ehci_control_message( dev, USB_CTRLTYPE_DIR_HOST2DEVICE, USB_REQ_SETADDRESS,port+1,0,0,0); + if (retval < 0) + { + ehci_dbg("unable to set device addr...\n"); + return retval; + } + dev->toggles = 0; + + dev->id = port+1; + ehci_dbg ( "device %d: %X %X...\n", dev->id,le16_to_cpu(dev->desc.idVendor),le16_to_cpu(dev->desc.idProduct)); + return retval; +} +int ehci_reset_device(struct ehci_device *dev) +{ + return ehci_reset_port(dev->port); +} +#include "usbstorage.h" +int ehci_discover(void) +{ + int i; + int ret = 0 ; + // precondition: the ehci should be halted + for(i = 0;inum_port; i++) + { + struct ehci_device *dev = &ehci->devices[i]; + dev->port = i; + ret = ehci_reset_port(i); + + if( ret != -ENODEV ) + break; + } + return ret; +} +/* wii: quickly release non ehci or not connected ports, + as we can't kick OHCI drivers laters if we discover a device for them. +*/ +int ehci_release_ports(void) +{ + int i; + u32 __iomem *status_reg = &ehci->regs->port_status[2]; + while(ehci_readl(&ehci->regs->port_status[2]) == 0x1000);// wait port 2 to init + msleep(1);// wait another msec.. + for(i = 0;inum_port; i++){ + status_reg = &ehci->regs->port_status[i]; + u32 status = ehci_readl(status_reg); + if (i==2 || !(PORT_CONNECT&status) || PORT_USB11(status)) + ehci_writel( PORT_OWNER,status_reg); // release port. + } + return 0; +} + +int ehci_open_device(int vid,int pid,int fd) +{ + int i; + for(i=0;inum_port;i++) + { + ehci_dbg("try device: %d\n",i); + if(ehci->devices[i].fd == 0 && + le16_to_cpu(ehci->devices[i].desc.idVendor) == vid && + le16_to_cpu(ehci->devices[i].desc.idProduct) == pid) + { + ehci_dbg("found device: %x %x\n",vid,pid); + ehci->devices[i].fd = fd; + return fd; + } + } + return -6; +} +int ehci_close_device(struct ehci_device *dev) +{ + if (dev) + dev->fd = 0; + return 0; +} + void * ehci_fd_to_dev(int fd) +{ + int i; + for(i=0;inum_port;i++) + { + struct ehci_device *dev = &ehci->devices[i]; + ehci_dbg ( "device %d:fd:%d %X %X...\n", dev->id,dev->fd,le16_to_cpu(dev->desc.idVendor),le16_to_cpu(dev->desc.idProduct)); + if(dev->fd == fd){ + return dev; + } + } + ehci_dbg("unkown fd! %d\n",fd); + return 0; +} +#define g_ehci #error +int ehci_get_device_list(u8 maxdev,u8 b0,u8*num,u16*buf) +{ + int i,j = 0; + for(i=0;inum_port && jdevices[i]; + if(dev->id != 0){ + ehci_dbg ( "device %d: %X %X...\n", dev->id,le16_to_cpu(dev->desc.idVendor),le16_to_cpu(dev->desc.idProduct)); + buf[j*4] = 0; + buf[j*4+1] = 0; + buf[j*4+2] = le16_to_cpu(dev->desc.idVendor); + buf[j*4+3] = le16_to_cpu(dev->desc.idProduct); + j++; + } + } + ehci_dbg("found %d devices\n",j); + *num = j; + return 0; +} +#include "usb.c" +#include "usbstorage.c" diff --git a/ehci.h b/ehci.h new file mode 100644 index 0000000..c3ab9ed --- /dev/null +++ b/ehci.h @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2009 Kwiirk + * Original Copyright (c) 2001-2002 by David Brownell + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "ehci_types.h" + +#ifndef __LINUX_EHCI_HCD_H +#define __LINUX_EHCI_HCD_H +/* definitions used for the EHCI driver */ + +/* + * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to + * __leXX (normally) or __beXX (given EHCI_BIG_ENDIAN_DESC), depending on + * the host controller implementation. + * + * To facilitate the strongest possible byte-order checking from "sparse" + * and so on, we use __leXX unless that's not practical. + */ +#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_DESC +typedef __u32 __bitwise __hc32; +typedef __u16 __bitwise __hc16; +#else +#define __hc32 __le32 +#define __hc16 __le16 +#endif + + +#define EHCI_MAX_ROOT_PORTS 4 /* see HCS_N_PORTS */ +#define EHCI_MAX_QTD 8 +#include "usb.h" + +struct ehci_device{ + usb_devdesc desc; + int id; + int port; + int fd; + u32 toggles; +}; +#define ep_bit(ep) (((ep)&0xf)+(((ep)>>7)?16:0)) +#define get_toggle(dev,ep) (((dev)->toggles>>ep_bit(ep))&1) +#define set_toggle(dev,ep,v) (dev)->toggles = ((dev)->toggles &(~(1<>16) & 0x7fff) +#define QTD_IOC (1 << 15) /* interrupt on complete */ +#define QTD_CERR(tok) (((tok)>>10) & 0x3) +#define QTD_PID(tok) (((tok)>>8) & 0x3) +#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */ +#define QTD_STS_HALT (1 << 6) /* halted on error */ +#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */ +#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */ +#define QTD_STS_XACT (1 << 3) /* device gave illegal response */ +#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */ +#define QTD_STS_STS (1 << 1) /* split transaction state */ +#define QTD_STS_PING (1 << 0) /* issue PING? */ + +#define ACTIVE_BIT(ehci) cpu_to_hc32( QTD_STS_ACTIVE) +#define HALT_BIT(ehci) cpu_to_hc32( QTD_STS_HALT) +#define STATUS_BIT(ehci) cpu_to_hc32( QTD_STS_STS) + + __hc32 hw_buf [5]; /* see EHCI 3.5.4 */ + __hc32 hw_buf_hi [5]; /* Appendix B */ + + /* the rest is HCD-private */ + dma_addr_t qtd_dma; /* qtd address */ + struct ehci_qtd *next; /* sw qtd list */ + struct ehci_urb *urb; /* qtd's urb */ + size_t length; /* length of buffer */ +} __attribute__ ((aligned (32))); + +/* mask NakCnt+T in qh->hw_alt_next */ +#define QTD_MASK(ehci) cpu_to_hc32 ( ~0x1f) + +#define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1) + +/*-------------------------------------------------------------------------*/ + +/* type tag from {qh,itd,sitd,fstn}->hw_next */ +#define Q_NEXT_TYPE(dma) ((dma) & cpu_to_hc32( 3 << 1)) + +/* + * Now the following defines are not converted using the + * __constant_cpu_to_le32() macro anymore, since we have to support + * "dynamic" switching between be and le support, so that the driver + * can be used on one system with SoC EHCI controller using big-endian + * descriptors as well as a normal little-endian PCI EHCI controller. + */ +/* values for that type tag */ +#define Q_TYPE_ITD (0 << 1) +#define Q_TYPE_QH (1 << 1) +#define Q_TYPE_SITD (2 << 1) +#define Q_TYPE_FSTN (3 << 1) + +/* next async queue entry, or pointer to interrupt/periodic QH */ +#define QH_NEXT(dma) (cpu_to_hc32( (((u32)dma)&~0x01f)|Q_TYPE_QH)) + +/* for periodic/async schedules and qtd lists, mark end of list */ +#define EHCI_LIST_END() cpu_to_hc32( 1) /* "null pointer" to hw */ + +/* + * Entries in periodic shadow table are pointers to one of four kinds + * of data structure. That's dictated by the hardware; a type tag is + * encoded in the low bits of the hardware's periodic schedule. Use + * Q_NEXT_TYPE to get the tag. + * + * For entries in the async schedule, the type tag always says "qh". + */ +union ehci_shadow { + struct ehci_qh *qh; /* Q_TYPE_QH */ + struct ehci_itd *itd; /* Q_TYPE_ITD */ + struct ehci_sitd *sitd; /* Q_TYPE_SITD */ + struct ehci_fstn *fstn; /* Q_TYPE_FSTN */ + __hc32 *hw_next; /* (all types) */ + void *ptr; +}; + +/*-------------------------------------------------------------------------*/ + +/* + * EHCI Specification 0.95 Section 3.6 + * QH: describes control/bulk/interrupt endpoints + * See Fig 3-7 "Queue Head Structure Layout". + * + * These appear in both the async and (for interrupt) periodic schedules. + */ + +struct ehci_qh { + /* first part defined by EHCI spec */ + __hc32 hw_next; /* see EHCI 3.6.1 */ + __hc32 hw_info1; /* see EHCI 3.6.2 */ +#define QH_HEAD 0x00008000 + __hc32 hw_info2; /* see EHCI 3.6.2 */ +#define QH_SMASK 0x000000ff +#define QH_CMASK 0x0000ff00 +#define QH_HUBADDR 0x007f0000 +#define QH_HUBPORT 0x3f800000 +#define QH_MULT 0xc0000000 + __hc32 hw_current; /* qtd list - see EHCI 3.6.4 */ + + /* qtd overlay (hardware parts of a struct ehci_qtd) */ + __hc32 hw_qtd_next; + __hc32 hw_alt_next; + __hc32 hw_token; + __hc32 hw_buf [5]; + __hc32 hw_buf_hi [5]; + + /* the rest is HCD-private */ + dma_addr_t qh_dma; /* address of qh */ + struct ehci_qtd *qtd_head; /* sw qtd list */ + + struct ehci_hcd *ehci; + +#define NO_FRAME ((unsigned short)~0) /* pick new start */ +} __attribute__ ((aligned (32))); + +/*-------------------------------------------------------------------------*/ + + + +/*-------------------------------------------------------------------------*/ + +/* cpu to ehci */ +#define cpu_to_hc32(b) cpu_to_le32(b) +#define hc32_to_cpu(b) le32_to_cpu(b) +#define hc32_to_cpup(b) le32_to_cpu(*(b)) + +/*-------------------------------------------------------------------------*/ + +/* os specific functions */ +void*ehci_maligned(int size,int alignement,int crossing); +dma_addr_t ehci_virt_to_dma(void *); +dma_addr_t ehci_dma_map_to(void *buf,size_t len); +dma_addr_t ehci_dma_map_from(void *buf,size_t len); +dma_addr_t ehci_dma_map_bidir(void *buf,size_t len); +void ehci_dma_unmap_to(dma_addr_t buf,size_t len); +void ehci_dma_unmap_from(dma_addr_t buf,size_t len); +void ehci_dma_unmap_bidir(dma_addr_t buf,size_t len); + + +/* extern API */ + +s32 ehci_control_message(struct ehci_device *dev,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *buf); +s32 ehci_bulk_message(struct ehci_device *dev,u8 bEndpoint,u16 wLength,void *rpData); +int ehci_discover(void); +int ehci_get_device_list(u8 maxdev,u8 b0,u8*num,u16*buf); + +extern struct ehci_hcd *ehci; /* @todo put ehci as a static global and remove ehci from APIs.. */ +extern int ehci_open_device(int vid,int pid,int fd); +extern int ehci_close_device(struct ehci_device *dev); +extern void * ehci_fd_to_dev(int fd); +extern int ehci_release_ports(void); + +/* UMS API */ + +s32 USBStorage_Init(void); +s32 USBStorage_Get_Capacity(u32*sector_size); +s32 USBStorage_Read_Sectors(u32 sector, u32 numSectors, void *buffer); +s32 USBStorage_Read_Stress(u32 sector, u32 numSectors, void *buffer); +s32 USBStorage_Write_Sectors(u32 sector, u32 numSectors, const void *buffer); + +#ifndef DEBUG +#define STUB_DEBUG_FILES +#endif /* DEBUG */ + +/*-------------------------------------------------------------------------*/ + +#endif /* __LINUX_EHCI_HCD_H */ diff --git a/ehci_defs.h b/ehci_defs.h new file mode 100644 index 0000000..d1c1969 --- /dev/null +++ b/ehci_defs.h @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2001-2002 by David Brownell + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __LINUX_USB_EHCI_DEF_H +#define __LINUX_USB_EHCI_DEF_H + +/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ + +/* Section 2.2 Host Controller Capability Registers */ +struct ehci_caps { + /* these fields are specified as 8 and 16 bit registers, + * but some hosts can't perform 8 or 16 bit PCI accesses. + */ + u32 hc_capbase; +#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */ +#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */ + u32 hcs_params; /* HCSPARAMS - offset 0x4 */ +#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */ +#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */ +#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */ +#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */ +#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */ +#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */ +#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ + + u32 hcc_params; /* HCCPARAMS - offset 0x8 */ +#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */ +#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */ +#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */ +#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ +#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ +#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */ + u8 portroute [8]; /* nibbles for routing - offset 0xC */ +} __attribute__ ((packed)); + + +/* Section 2.3 Host Controller Operational Registers */ +struct ehci_regs { + + /* USBCMD: offset 0x00 */ + u32 command; +/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ +#define CMD_PARK (1<<11) /* enable "park" on async qh */ +#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ +#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */ +#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ +#define CMD_ASE (1<<5) /* async schedule enable */ +#define CMD_PSE (1<<4) /* periodic schedule enable */ +/* 3:2 is periodic frame list size */ +#define CMD_RESET (1<<1) /* reset HC not bus */ +#define CMD_RUN (1<<0) /* start/stop HC */ + + /* USBSTS: offset 0x04 */ + u32 status; +#define STS_ASS (1<<15) /* Async Schedule Status */ +#define STS_PSS (1<<14) /* Periodic Schedule Status */ +#define STS_RECL (1<<13) /* Reclamation */ +#define STS_HALT (1<<12) /* Not running (any reason) */ +/* some bits reserved */ + /* these STS_* flags are also intr_enable bits (USBINTR) */ +#define STS_IAA (1<<5) /* Interrupted on async advance */ +#define STS_FATAL (1<<4) /* such as some PCI access errors */ +#define STS_FLR (1<<3) /* frame list rolled over */ +#define STS_PCD (1<<2) /* port change detect */ +#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ +#define STS_INT (1<<0) /* "normal" completion (short, ...) */ + + /* USBINTR: offset 0x08 */ + u32 intr_enable; + + /* FRINDEX: offset 0x0C */ + u32 frame_index; /* current microframe number */ + /* CTRLDSSEGMENT: offset 0x10 */ + u32 segment; /* address bits 63:32 if needed */ + /* PERIODICLISTBASE: offset 0x14 */ + u32 frame_list; /* points to periodic list */ + /* ASYNCLISTADDR: offset 0x18 */ + u32 async_next; /* address of next async queue head */ + + u32 reserved [9]; + + /* CONFIGFLAG: offset 0x40 */ + u32 configured_flag; +#define FLAG_CF (1<<0) /* true: we'll support "high speed" */ + + /* PORTSC: offset 0x44 */ + u32 port_status [0]; /* up to N_PORTS */ +/* 31:23 reserved */ +#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */ +#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ +#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */ +/* 19:16 for port testing */ +#define PORT_LED_OFF (0<<14) +#define PORT_LED_AMBER (1<<14) +#define PORT_LED_GREEN (2<<14) +#define PORT_LED_MASK (3<<14) +#define PORT_OWNER (1<<13) /* true: companion hc owns this port */ +#define PORT_POWER (1<<12) /* true: has power (see PPC) */ +#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ +/* 11:10 for detecting lowspeed devices (reset vs release ownership) */ +/* 9 reserved */ +#define PORT_RESET (1<<8) /* reset port */ +#define PORT_SUSPEND (1<<7) /* suspend port */ +#define PORT_RESUME (1<<6) /* resume it */ +#define PORT_OCC (1<<5) /* over current change */ +#define PORT_OC (1<<4) /* over current active */ +#define PORT_PEC (1<<3) /* port enable change */ +#define PORT_PE (1<<2) /* port enable */ +#define PORT_CSC (1<<1) /* connect status change */ +#define PORT_CONNECT (1<<0) /* device connected */ +#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) +} __attribute__ ((packed)); + +#define USBMODE 0x68 /* USB Device mode */ +#define USBMODE_SDIS (1<<3) /* Stream disable */ +#define USBMODE_BE (1<<2) /* BE/LE endianness select */ +#define USBMODE_CM_HC (3<<0) /* host controller mode */ +#define USBMODE_CM_IDLE (0<<0) /* idle state */ + +/* Appendix C, Debug port ... intended for use with special "debug devices" + * that can help if there's no serial console. (nonstandard enumeration.) + */ +struct ehci_dbg_port { + u32 control; +#define DBGP_OWNER (1<<30) +#define DBGP_ENABLED (1<<28) +#define DBGP_DONE (1<<16) +#define DBGP_INUSE (1<<10) +#define DBGP_ERRCODE(x) (((x)>>7)&0x07) +# define DBGP_ERR_BAD 1 +# define DBGP_ERR_SIGNAL 2 +#define DBGP_ERROR (1<<6) +#define DBGP_GO (1<<5) +#define DBGP_OUT (1<<4) +#define DBGP_LEN(x) (((x)>>0)&0x0f) + u32 pids; +#define DBGP_PID_GET(x) (((x)>>16)&0xff) +#define DBGP_PID_SET(data, tok) (((data)<<8)|(tok)) + u32 data03; + u32 data47; + u32 address; +#define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep)) +} __attribute__ ((packed)); + +#endif /* __LINUX_USB_EHCI_DEF_H */ diff --git a/ehci_types.h b/ehci_types.h new file mode 100644 index 0000000..051b640 --- /dev/null +++ b/ehci_types.h @@ -0,0 +1,55 @@ +#ifndef __EHCI_TYPES_H__ +#define __EHCI_TYPES_H__ + +#include "global.h" + +/* linux kernel types needed by our code */ +#define __iomem + +typedef unsigned long uint32_t; + +#if 0 +typedef unsigned long u32; +typedef signed long s32; +typedef unsigned short u16; +typedef unsigned char u8; +typedef char s8; +typedef unsigned long long u64; +#endif + +#define __u32 u32 +#define __le32 u32 +#define dma_addr_t u32 +#define __GNUG__ +typedef u32 spinlock_t; +typedef enum +{ + GFP_KERNEL=1 +}gfp_t; +struct timer_list +{ + int time; +}; +enum{ + ENODEV =1, + ETIMEDOUT, + EINVAL, + ENOMEM, + +}; +#define jiffies 0 +#define likely(x) (x) +#define unlikely(x) (x) +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +#undef offsetof +#ifdef __compiler_offsetof +#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER) +#else +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + + +#endif diff --git a/ff.c b/ff.c new file mode 100644 index 0000000..e527393 --- /dev/null +++ b/ff.c @@ -0,0 +1,4048 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - FAT file system module R0.08b (C)ChaN, 2011 +/-----------------------------------------------------------------------------/ +/ FatFs module is a generic FAT file system module for small embedded systems. +/ This is a free software that opened for education, research and commercial +/ developments under license policy of following terms. +/ +/ Copyright (C) 2011, 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 products 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) partition. +/ 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 algorithm 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 physical 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 configuration option. (_FS_TINY) +/ Added long file name feature. +/ Added multiple code page feature. +/ 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 feature. +/ Jun 21,'09 R0.07c Fixed f_unlink() can return FR_OK on error. +/ Fixed wrong cache control in f_lseek(). +/ Added relative path feature. +/ Added f_chdir() and f_chdrive(). +/ Added proper case conversion to extended char. +/ Nov 03,'09 R0.07e Separated out configuration options from ff.h to ffconf.h. +/ Fixed f_unlink() fails to remove a sub-dir on _FS_RPATH. +/ Fixed name matching error on the 13 char boundary. +/ Added a configuration option, _LFN_UNICODE. +/ Changed f_readdir() to return the SFN with always upper case on non-LFN cfg. +/ +/ May 15,'10 R0.08 Added a memory configuration option. (_USE_LFN = 3) +/ Added file lock feature. (_FS_SHARE) +/ Added fast seek feature. (_USE_FASTSEEK) +/ Changed some types on the API, XCHAR->TCHAR. +/ Changed fname member in the FILINFO structure on Unicode cfg. +/ String functions support UTF-8 encoding files on Unicode cfg. +/ Aug 16,'10 R0.08a Added f_getcwd(). (_FS_RPATH = 2) +/ Added sector erase feature. (_USE_ERASE) +/ Moved file lock semaphore table from fs object to the bss. +/ Fixed a wrong directory entry is created on non-LFN cfg when the given name contains ';'. +/ Fixed f_mkfs() creates wrong FAT32 volume. +/ Jan 15,'11 R0.08b Fast seek feature is also applied to f_read() and f_write(). +/ f_lseek() reports required table size on creating CLMP. +/ Extended format syntax of f_printf function. +/ Ignores duplicated directory separators in given path names. +/---------------------------------------------------------------------------*/ + +#include "ff.h" /* FatFs configurations and declarations */ +#include "diskio.h" /* Declarations of low level disk I/O functions */ + + +/*-------------------------------------------------------------------------- + + Module Private Definitions + +---------------------------------------------------------------------------*/ + +#if _FATFS != 8237 +#error Wrong include file (ff.h). +#endif + + +/* Definitions on sector size */ +#if _MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096 +#error Wrong sector size. +#endif +#if _MAX_SS != 512 +#define SS(fs) ((fs)->ssize) /* Multiple sector size */ +#else +#define SS(fs) 512U /* Fixed sector size */ +#endif + + +/* Reentrancy related */ +#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); } + + +/* File shareing feature */ +#if _FS_SHARE +#if _FS_READONLY +#error _FS_SHARE must be 0 on read-only cfg. +#endif +typedef struct { + FATFS *fs; /* File ID 1, volume (NULL:blank entry) */ + DWORD clu; /* File ID 2, directory */ + WORD idx; /* File ID 3, directory index */ + WORD ctr; /* File open counter, 0:none, 0x01..0xFF:read open count, 0x100:write mode */ +} FILESEM; +#endif + + +/* Misc definitions */ +#define LD_CLUST(dir) (((DWORD)LD_WORD(dir+DIR_FstClusHI)<<16) | LD_WORD(dir+DIR_FstClusLO)) +#define ST_CLUST(dir,cl) {ST_WORD(dir+DIR_FstClusLO, cl); ST_WORD(dir+DIR_FstClusHI, (DWORD)cl>>16);} + + +/* DBCS code ranges and SBCS extend char conversion table */ + +#if _CODE_PAGE == 932 /* 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 /* 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 /* 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 /* Traditional Chinese Big5 */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0xA1 +#define _DS2E 0xFE + +#elif _CODE_PAGE == 437 /* U.S. (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F,0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 720 /* Arabic (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x45,0x41,0x84,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x49,0x49,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 737 /* Greek (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \ + 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xE7,0xE8,0xF1,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 775 /* Baltic (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 850 /* Multilingual Latin 1 (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 852 /* Latin 2 (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F,0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF} + +#elif _CODE_PAGE == 855 /* Cyrillic (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F,0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \ + 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \ + 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 857 /* Turkish (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x98,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0x59,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 858 /* Multilingual Latin 1 + Euro (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 862 /* Hebrew (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 866 /* Russian (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 874 /* Thai (OEM, Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1250 /* Central Europe (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xA3,0xB4,0xB5,0xB6,0xB7,0xB8,0xA5,0xAA,0xBB,0xBC,0xBD,0xBC,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} + +#elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x82,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x80,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ + 0xA0,0xA2,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB2,0xA5,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xA3,0xBD,0xBD,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF} + +#elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0xAd,0x9B,0x8C,0x9D,0xAE,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} + +#elif _CODE_PAGE == 1253 /* Greek (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xA2,0xB8,0xB9,0xBA, \ + 0xE0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xFB,0xBC,0xFD,0xBF,0xFF} + +#elif _CODE_PAGE == 1254 /* Turkish (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} + +#elif _CODE_PAGE == 1255 /* Hebrew (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1256 /* Arabic (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x8C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x41,0xE1,0x41,0xE3,0xE4,0xE5,0xE6,0x43,0x45,0x45,0x45,0x45,0xEC,0xED,0x49,0x49,0xF0,0xF1,0xF2,0xF3,0x4F,0xF5,0xF6,0xF7,0xF8,0x55,0xFA,0x55,0x55,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1257 /* Baltic (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xBC,0xBD,0xBE,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} + +#elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0xAC,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xEC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xFE,0x9F} + +#elif _CODE_PAGE == 1 /* ASCII (for only non-LFN cfg) */ +#if _USE_LFN +#error Cannot use LFN feature without valid code page. +#endif +#define _DF1S 0 + +#else +#error Unknown code page + +#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 /* Code page is DBCS */ + +#ifdef _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 + +#ifdef _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 /* Code page is SBCS */ + +#define IsDBCS1(c) 0 +#define IsDBCS2(c) 0 + +#endif /* _DF1S */ + + +/* Name status flags */ +#define NS 11 /* Index of name status byte in fn[] */ +#define NS_LOSS 0x01 /* Out of 8.3 format */ +#define NS_LFN 0x02 /* Force to create LFN entry */ +#define NS_LAST 0x04 /* Last segment */ +#define NS_BODY 0x08 /* Lower case flag (body) */ +#define NS_EXT 0x10 /* Lower case flag (ext) */ +#define NS_DOT 0x20 /* Dot entry */ + + +/* FAT sub-type boundaries */ +/* Note that the FAT spec by Microsoft says 4085 but Windows works with 4087! */ +#define MIN_FAT16 4086 /* Minimum number of clusters for FAT16 */ +#define MIN_FAT32 65526 /* Minimum number of clusters for FAT32 */ + + +/* FatFs refers the members in the FAT structures as byte array instead of +/ structure member because the structure is not binary compatible between +/ different platforms */ + +#define BS_jmpBoot 0 /* Jump instruction (3) */ +#define BS_OEMName 3 /* OEM name (8) */ +#define BPB_BytsPerSec 11 /* Sector size [byte] (2) */ +#define BPB_SecPerClus 13 /* Cluster size [sector] (1) */ +#define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (2) */ +#define BPB_NumFATs 16 /* Number of FAT copies (1) */ +#define BPB_RootEntCnt 17 /* Number of root dir entries for FAT12/16 (2) */ +#define BPB_TotSec16 19 /* Volume size [sector] (2) */ +#define BPB_Media 21 /* Media descriptor (1) */ +#define BPB_FATSz16 22 /* FAT size [sector] (2) */ +#define BPB_SecPerTrk 24 /* Track size [sector] (2) */ +#define BPB_NumHeads 26 /* Number of heads (2) */ +#define BPB_HiddSec 28 /* Number of special hidden sectors (4) */ +#define BPB_TotSec32 32 /* Volume size [sector] (4) */ +#define BS_DrvNum 36 /* Physical drive number (2) */ +#define BS_BootSig 38 /* Extended boot signature (1) */ +#define BS_VolID 39 /* Volume serial number (4) */ +#define BS_VolLab 43 /* Volume label (8) */ +#define BS_FilSysType 54 /* File system type (1) */ +#define BPB_FATSz32 36 /* FAT size [sector] (4) */ +#define BPB_ExtFlags 40 /* Extended flags (2) */ +#define BPB_FSVer 42 /* File system version (2) */ +#define BPB_RootClus 44 /* Root dir first cluster (4) */ +#define BPB_FSInfo 48 /* Offset of FSInfo sector (2) */ +#define BPB_BkBootSec 50 /* Offset of backup boot sectot (2) */ +#define BS_DrvNum32 64 /* Physical drive number (2) */ +#define BS_BootSig32 66 /* Extended boot signature (1) */ +#define BS_VolID32 67 /* Volume serial number (4) */ +#define BS_VolLab32 71 /* Volume label (8) */ +#define BS_FilSysType32 82 /* File system type (1) */ +#define FSI_LeadSig 0 /* FSI: Leading signature (4) */ +#define FSI_StrucSig 484 /* FSI: Structure signature (4) */ +#define FSI_Free_Count 488 /* FSI: Number of free clusters (4) */ +#define FSI_Nxt_Free 492 /* FSI: Last allocated cluster (4) */ +#define MBR_Table 446 /* MBR: Partition table offset (2) */ +#define SZ_PTE 16 /* MBR: Size of a partition table entry */ +#define BS_55AA 510 /* Boot sector signature (2) */ + +#define DIR_Name 0 /* Short file name (11) */ +#define DIR_Attr 11 /* Attribute (1) */ +#define DIR_NTres 12 /* NT flag (1) */ +#define DIR_CrtTime 14 /* Created time (2) */ +#define DIR_CrtDate 16 /* Created date (2) */ +#define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (2) */ +#define DIR_WrtTime 22 /* Modified time (2) */ +#define DIR_WrtDate 24 /* Modified date (2) */ +#define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (2) */ +#define DIR_FileSize 28 /* File size (4) */ +#define LDIR_Ord 0 /* LFN entry order and LLE flag (1) */ +#define LDIR_Attr 11 /* LFN attribute (1) */ +#define LDIR_Type 12 /* LFN type (1) */ +#define LDIR_Chksum 13 /* Sum of corresponding SFN entry */ +#define LDIR_FstClusLO 26 /* Filled by zero (0) */ +#define SZ_DIR 32 /* Size of a directory entry */ +#define LLE 0x40 /* Last long entry flag in LDIR_Ord */ +#define DDE 0xE5 /* Deleted directory enrty mark in DIR_Name[0] */ +#define NDDE 0x05 /* Replacement of a character collides with DDE */ + + +/*------------------------------------------------------------*/ +/* Work area */ + +#if _VOLUMES +static +FATFS *FatFs[_VOLUMES]; /* Pointer to the file system objects (logical drives) */ +#else +#error Number of drives must not be 0. +#endif + +static +WORD Fsid; /* File system mount ID */ + +#if _FS_RPATH +static +BYTE CurrVol; /* Current drive */ +#endif + +#if _FS_SHARE +static +FILESEM Files[_FS_SHARE]; /* File lock semaphores */ +#endif + +#if _USE_LFN == 0 /* No LFN */ +#define DEF_NAMEBUF BYTE sfn[12] +#define INIT_BUF(dobj) (dobj).fn = sfn +#define FREE_BUF() + +#elif _USE_LFN == 1 /* LFN with static LFN working buffer */ +static WCHAR LfnBuf[_MAX_LFN+1]; +#define DEF_NAMEBUF BYTE sfn[12] +#define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = LfnBuf; } +#define FREE_BUF() + +#elif _USE_LFN == 2 /* LFN with dynamic LFN working buffer on the stack */ +#define DEF_NAMEBUF BYTE sfn[12]; WCHAR lbuf[_MAX_LFN+1] +#define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = lbuf; } +#define FREE_BUF() + +#elif _USE_LFN == 3 /* LFN with dynamic LFN working buffer on the heap */ +#define DEF_NAMEBUF BYTE sfn[12]; WCHAR *lfn +#define INIT_BUF(dobj) { lfn = ff_memalloc((_MAX_LFN + 1) * 2); \ + if (!lfn) LEAVE_FF((dobj).fs, FR_NOT_ENOUGH_CORE); \ + (dobj).lfn = lfn; (dobj).fn = sfn; } +#define FREE_BUF() ff_memfree(lfn) + +#else +#error Wrong LFN configuration. +#endif + + + + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------*/ +/* String functions */ +/*-----------------------------------------------------------------------*/ + +/* Copy memory to memory */ +static +void mem_cpy (void* dst, const void* src, UINT cnt) { + BYTE *d = (BYTE*)dst; + const BYTE *s = (const BYTE*)src; + +#if _WORD_ACCESS == 1 + while (cnt >= sizeof(int)) { + *(int*)d = *(int*)s; + d += sizeof(int); s += sizeof(int); + cnt -= sizeof(int); + } +#endif + while (cnt--) + *d++ = *s++; +} + +/* Fill memory */ +static +void mem_set (void* dst, int val, UINT cnt) { + BYTE *d = (BYTE*)dst; + + while (cnt--) + *d++ = (BYTE)val; +} + +/* Compare memory to memory */ +static +int mem_cmp (const void* dst, const void* src, UINT cnt) { + const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)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 +int 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 + + + +/*-----------------------------------------------------------------------*/ +/* File shareing control functions */ +/*-----------------------------------------------------------------------*/ +#if _FS_SHARE + +static +FRESULT chk_lock ( /* Check if the file can be accessed */ + DIR* dj, /* Directory object pointing the file to be checked */ + int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */ +) +{ + UINT i, be; + + /* Search file semaphore table */ + for (i = be = 0; i < _FS_SHARE; i++) { + if (Files[i].fs) { /* Existing entry */ + if (Files[i].fs == dj->fs && /* Check if the file matched with an open file */ + Files[i].clu == dj->sclust && + Files[i].idx == dj->index) break; + } else { /* Blank entry */ + be++; + } + } + if (i == _FS_SHARE) /* The file is not opened */ + return (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES; /* Is there a blank entry for new file? */ + + /* The file has been opened. Reject any open against writing file and all write mode open */ + return (acc || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK; +} + + +static +int enq_lock ( /* Check if an entry is available for a new file */ + FATFS* fs /* File system object */ +) +{ + UINT i; + + for (i = 0; i < _FS_SHARE && Files[i].fs; i++) ; + return (i == _FS_SHARE) ? 0 : 1; +} + + +static +UINT inc_lock ( /* Increment file open counter and returns its index (0:int error) */ + DIR* dj, /* Directory object pointing the file to register or increment */ + int acc /* Desired access mode (0:Read, !0:Write) */ +) +{ + UINT i; + + + for (i = 0; i < _FS_SHARE; i++) { /* Find the file */ + if (Files[i].fs == dj->fs && + Files[i].clu == dj->sclust && + Files[i].idx == dj->index) break; + } + + if (i == _FS_SHARE) { /* Not opened. Register it as new. */ + for (i = 0; i < _FS_SHARE && Files[i].fs; i++) ; + if (i == _FS_SHARE) return 0; /* No space to register (int err) */ + Files[i].fs = dj->fs; + Files[i].clu = dj->sclust; + Files[i].idx = dj->index; + Files[i].ctr = 0; + } + + if (acc && Files[i].ctr) return 0; /* Access violation (int err) */ + + Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set semaphore value */ + + return i + 1; +} + + +static +FRESULT dec_lock ( /* Decrement file open counter */ + UINT i /* Semaphore index */ +) +{ + WORD n; + FRESULT res; + + + if (--i < _FS_SHARE) { + n = Files[i].ctr; + if (n == 0x100) n = 0; + if (n) n--; + Files[i].ctr = n; + if (!n) Files[i].fs = 0; + res = FR_OK; + } else { + res = FR_INT_ERR; + } + return res; +} + + +static +void clear_lock ( /* Clear lock entries of the volume */ + FATFS *fs +) +{ + UINT i; + + for (i = 0; i < _FS_SHARE; i++) { + if (Files[i].fs == fs) Files[i].fs = 0; + } +} +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Change window offset */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT move_window ( + FATFS *fs, /* File system object */ + DWORD sector /* Sector number to make appearance 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->drv, fs->win, wsect, 1) != RES_OK) + return FR_DISK_ERR; + fs->wflag = 0; + if (wsect < (fs->fatbase + fs->fsize)) { /* In FAT area */ + BYTE nf; + for (nf = fs->n_fats; nf > 1; nf--) { /* Reflect the change to all FAT copies */ + wsect += fs->fsize; + disk_write(fs->drv, fs->win, wsect, 1); + } + } + } +#endif + if (sector) { + if (disk_read(fs->drv, 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; + /* Create FSInfo structure */ + 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); + /* Write it into the FSInfo sector */ + disk_write(fs->drv, 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->drv, CTRL_SYNC, (void*)0) != RES_OK) + // res = FR_DISK_ERR; + } + + return res; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Get sector# from cluster# */ +/*-----------------------------------------------------------------------*/ + + +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->n_fatent - 2)) return 0; /* Invalid cluster# */ + return clst * fs->csize + fs->database; +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Read value of a FAT entry */ +/*-----------------------------------------------------------------------*/ + + +DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, Else:Cluster status */ + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to get the link information */ +) +{ + UINT wc, bc; + BYTE *p; + + + if (clst < 2 || clst >= fs->n_fatent) /* Chack range */ + return 1; + + switch (fs->fs_type) { + case FS_FAT12 : + bc = (UINT)clst; bc += bc / 2; + if (move_window(fs, fs->fatbase + (bc / SS(fs)))) break; + wc = fs->win[bc % SS(fs)]; bc++; + if (move_window(fs, fs->fatbase + (bc / SS(fs)))) break; + wc |= fs->win[bc % SS(fs)] << 8; + return (clst & 1) ? (wc >> 4) : (wc & 0xFFF); + + case FS_FAT16 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)))) break; + p = &fs->win[clst * 2 % SS(fs)]; + return LD_WORD(p); + + case FS_FAT32 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)))) break; + p = &fs->win[clst * 4 % SS(fs)]; + return LD_DWORD(p) & 0x0FFFFFFF; + } + + return 0xFFFFFFFF; /* An error occurred at the disk I/O layer */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Change value of a FAT entry */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY + +FRESULT put_fat ( + FATFS *fs, /* File system object */ + DWORD clst, /* Cluster# to be changed in range of 2 to fs->n_fatent - 1 */ + DWORD val /* New value to mark the cluster */ +) +{ + UINT bc; + BYTE *p; + FRESULT res; + + + if (clst < 2 || clst >= fs->n_fatent) { /* Check range */ + res = FR_INT_ERR; + + } else { + switch (fs->fs_type) { + case FS_FAT12 : + bc = clst; bc += bc / 2; + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != FR_OK) break; + p = &fs->win[bc % SS(fs)]; + *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; + bc++; + fs->wflag = 1; + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != FR_OK) break; + p = &fs->win[bc % SS(fs)]; + *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); + break; + + case FS_FAT16 : + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))); + if (res != FR_OK) break; + p = &fs->win[clst * 2 % SS(fs)]; + ST_WORD(p, (WORD)val); + break; + + case FS_FAT32 : + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))); + if (res != FR_OK) break; + p = &fs->win[clst * 4 % SS(fs)]; + val |= LD_DWORD(p) & 0xF0000000; + ST_DWORD(p, val); + break; + + default : + res = FR_INT_ERR; + } + fs->wflag = 1; + } + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Remove a cluster chain */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT remove_chain ( + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to remove a chain from */ +) +{ + FRESULT res; + DWORD nxt; +#if _USE_ERASE + DWORD scl = clst, ecl = clst, resion[2]; +#endif + + if (clst < 2 || clst >= fs->n_fatent) { /* Check range */ + res = FR_INT_ERR; + + } else { + res = FR_OK; + while (clst < fs->n_fatent) { /* Not a last link? */ + nxt = get_fat(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_fat(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; + } +#if _USE_ERASE + if (ecl + 1 == nxt) { /* Next cluster is contiguous */ + ecl = nxt; + } else { /* End of contiguous clusters */ + resion[0] = clust2sect(fs, scl); /* Start sector */ + resion[1] = clust2sect(fs, ecl) + fs->csize - 1; /* End sector */ + disk_ioctl(fs->drv, CTRL_ERASE_SECTOR, resion); /* Erase the block */ + scl = ecl = nxt; + } +#endif + clst = nxt; /* Next cluster */ + } + } + + return res; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - 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; + FRESULT res; + + + if (clst == 0) { /* Create a new chain */ + scl = fs->last_clust; /* Get suggested start point */ + if (!scl || scl >= fs->n_fatent) scl = 1; + } + else { /* Stretch the current chain */ + cs = get_fat(fs, clst); /* Check the cluster status */ + if (cs < 2) return 1; /* It is an invalid cluster */ + if (cs < fs->n_fatent) return cs; /* It is already followed by next cluster */ + scl = clst; + } + + ncl = scl; /* Start cluster */ + for (;;) { + ncl++; /* Next cluster */ + if (ncl >= fs->n_fatent) { /* Wrap around */ + ncl = 2; + if (ncl > scl) return 0; /* No free cluster */ + } + cs = get_fat(fs, ncl); /* Get the cluster status */ + if (cs == 0) break; /* Found a free cluster */ + if (cs == 0xFFFFFFFF || cs == 1)/* An error occurred */ + return cs; + if (ncl == scl) return 0; /* No free cluster */ + } + + res = put_fat(fs, ncl, 0x0FFFFFFF); /* Mark the new cluster "last link" */ + if (res == FR_OK && clst != 0) { + res = put_fat(fs, clst, ncl); /* Link it to the previous one if needed */ + } + if (res == FR_OK) { + fs->last_clust = ncl; /* Update FSINFO */ + if (fs->free_clust != 0xFFFFFFFF) { + fs->free_clust--; + fs->fsi_flag = 1; + } + } else { + ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; + } + + return ncl; /* Return new cluster number or error code */ +} +#endif /* !_FS_READONLY */ + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Convert offset into cluster with link map table */ +/*-----------------------------------------------------------------------*/ + +#if _USE_FASTSEEK +static +DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */ + FIL* fp, /* Pointer to the file object */ + DWORD ofs /* File offset to be converted to cluster# */ +) +{ + DWORD cl, ncl, *tbl; + + + tbl = fp->cltbl + 1; /* Top of CLMT */ + cl = ofs / SS(fp->fs) / fp->fs->csize; /* Cluster order from top of the file */ + for (;;) { + ncl = *tbl++; /* Number of cluters in the fragment */ + if (!ncl) return 0; /* End of table? (error) */ + if (cl < ncl) break; /* In this fragment? */ + cl -= ncl; tbl++; /* Next fragment */ + } + return cl + *tbl; /* Return the cluster number */ +} +#endif /* _USE_FASTSEEK */ + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Set directory index */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_sdi ( + 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->n_fatent) /* Check start cluster range */ + return FR_INT_ERR; + if (!clst && dj->fs->fs_type == FS_FAT32) /* Replace cluster# 0 with root cluster# if in FAT32 */ + clst = dj->fs->dirbase; + + if (clst == 0) { /* Static table (root-dir in FAT12/16) */ + dj->clust = clst; + if (idx >= dj->fs->n_rootdir) /* Index is out of range */ + return FR_INT_ERR; + dj->sect = dj->fs->dirbase + idx / (SS(dj->fs) / SZ_DIR); /* Sector# */ + } + else { /* Dynamic table (sub-dirs or root-dir in FAT32) */ + ic = SS(dj->fs) / SZ_DIR * dj->fs->csize; /* Entries per cluster */ + while (idx >= ic) { /* Follow cluster chain */ + clst = get_fat(dj->fs, clst); /* Get next cluster */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst < 2 || clst >= dj->fs->n_fatent) /* 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) / SZ_DIR); /* Sector# */ + } + + dj->dir = dj->fs->win + (idx % (SS(dj->fs) / SZ_DIR)) * SZ_DIR; /* Ptr to the entry in the sector */ + + return FR_OK; /* Seek succeeded */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Move directory index next */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_next ( /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not stretch */ + DIR *dj, /* Pointer to directory object */ + int stretch /* 0: Do not stretch table, 1: Stretch 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) / SZ_DIR))) { /* Sector changed? */ + dj->sect++; /* Next sector */ + + if (dj->clust == 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) / SZ_DIR)) & (dj->fs->csize - 1)) == 0) { /* Cluster changed? */ + clst = get_fat(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->n_fatent) { /* When it reached end of dynamic table */ +#if !_FS_READONLY + BYTE c; + if (!stretch) return FR_NO_FILE; /* When do not stretch, report EOT */ + clst = create_chain(dj->fs, dj->clust); /* Stretch 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 stretched 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) / SZ_DIR)) * SZ_DIR; + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* LFN handling - 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 */ + +inline char toupper(const char toUpper) +{ + if ((toUpper >= 'a') && (toUpper <= 'z')) + return (toUpper - 0x20); + return toUpper; +} + +unsigned short ff_wtoupper(unsigned short a) +{ + if(a>=256) return a; + return toupper(a); +} + +static +int cmp_lfn ( /* 1:Matched, 0:Not matched */ + WCHAR *lfnbuf, /* Pointer to the LFN to be compared */ + BYTE *dir /* Pointer to the directory entry containing a part of LFN */ +) +{ + UINT i, s; + WCHAR wc, uc; + + + i = ((dir[LDIR_Ord] & ~LLE) - 1) * 13; /* Get offset in the LFN buffer */ + s = 0; wc = 1; + do { + uc = LD_WORD(dir+LfnOfs[s]); /* Pick an LFN character from the entry */ + if (wc) { /* Last char has not been processed */ + wc = ff_wtoupper(uc); /* Convert it to upper case */ + if (i >= _MAX_LFN || wc != ff_wtoupper(lfnbuf[i++])) /* Compare it */ + return 0; /* Not matched */ + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } while (++s < 13); /* Repeat until all chars in the entry are checked */ + + if ((dir[LDIR_Ord] & LLE) && wc && lfnbuf[i]) /* Last segment matched but different length */ + return 0; + + return 1; /* The part of LFN matched */ +} + + + +static +int pick_lfn ( /* 1:Succeeded, 0:Buffer overflow */ + WCHAR *lfnbuf, /* Pointer to the Unicode-LFN buffer */ + BYTE *dir /* Pointer to the directory entry */ +) +{ + UINT i, s; + WCHAR wc, uc; + + + i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ + + s = 0; wc = 1; + do { + uc = LD_WORD(dir+LfnOfs[s]); /* Pick an LFN character from the entry */ + if (wc) { /* Last char has not been processed */ + if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ + lfnbuf[i++] = wc = uc; /* Store it */ + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } while (++s < 13); /* Read all character in the entry */ + + if (dir[LDIR_Ord] & LLE) { /* Put terminator if it is the last LFN part */ + if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ + lfnbuf[i] = 0; + } + + return 1; +} + + +#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 */ +) +{ + UINT i, s; + WCHAR wc; + + + 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; /* Get offset in the LFN buffer */ + s = wc = 0; + do { + if (wc != 0xFFFF) wc = lfnbuf[i++]; /* Get an effective char */ + ST_WORD(dir+LfnOfs[s], wc); /* Put it */ + if (!wc) wc = 0xFFFF; /* Padding chars following last char */ + } while (++s < 13); + if (wc == 0xFFFF || !lfnbuf[i]) ord |= LLE; /* 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 generated SFN */ + const BYTE *src, /* Pointer to source SFN to be modified */ + const WCHAR *lfn, /* Pointer to LFN */ + WORD seq /* Sequence number */ +) +{ + BYTE ns[8], c; + UINT i, j; + + + mem_cpy(dst, src, 11); + + if (seq > 5) { /* On many collisions, generate a hash number instead of sequential number */ + do seq = (seq >> 1) + (seq << 15) + (WORD)*lfn++; while (*lfn); + } + + /* itoa (hexdecimal) */ + i = 7; + do { + c = (seq % 16) + '0'; + if (c > '9') c += 7; + ns[i--] = c; + seq /= 16; + } while (seq); + 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; + UINT n = 11; + + do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n); + return sum; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - 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 c, *dir; +#if _USE_LFN + BYTE a, ord, sum; +#endif + + res = dir_sdi(dj, 0); /* Rewind directory object */ + if (res != FR_OK) return res; + +#if _USE_LFN + ord = sum = 0xFF; +#endif + 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 */ +#if _USE_LFN /* LFN configuration */ + a = dir[DIR_Attr] & AM_MASK; + if (c == DDE || ((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 & LLE) { /* Is it start of LFN sequence? */ + sum = dir[LDIR_Chksum]; + c &= ~LLE; ord = c; /* LFN start order */ + dj->lfn_idx = dj->index; + } + /* Check validity of the LFN entry and compare it with given name */ + ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF; + } + } else { /* An SFN entry is found */ + if (!ord && sum == sum_sfn(dir)) break; /* LFN matched? */ + ord = 0xFF; dj->lfn_idx = 0xFFFF; /* Reset LFN sequence */ + if (!(dj->fn[NS] & NS_LOSS) && !mem_cmp(dir, dj->fn, 11)) break; /* SFN matched? */ + } + } +#else /* Non LFN configuration */ + if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dj->fn, 11)) /* Is it a valid entry? */ + break; +#endif + res = dir_next(dj, 0); /* Next entry */ + } while (res == FR_OK); + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read an object from the directory */ +/*-----------------------------------------------------------------------*/ +#if _FS_MINIMIZE <= 1 +static +FRESULT dir_read ( + DIR *dj /* Pointer to the directory object that pointing the entry to be read */ +) +{ + FRESULT res; + BYTE c, *dir; +#if _USE_LFN + BYTE a, ord = 0xFF, sum = 0xFF; +#endif + + 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 */ +#if _USE_LFN /* LFN configuration */ + a = dir[DIR_Attr] & AM_MASK; + if (c == DDE || (!_FS_RPATH && 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 & LLE) { /* Is it start of LFN sequence? */ + sum = dir[LDIR_Chksum]; + c &= ~LLE; 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? */ + dj->lfn_idx = 0xFFFF; /* It has no LFN. */ + break; + } + } +#else /* Non LFN configuration */ + if (c != DDE && (_FS_RPATH || c != '.') && !(dir[DIR_Attr] & AM_VOL)) /* Is it a valid entry? */ + break; +#endif + res = dir_next(dj, 0); /* 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 (_FS_RPATH && (sn[NS] & NS_DOT)) /* Cannot create dot entry */ + return FR_INVALID_NAME; + + if (sn[NS] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */ + fn[NS] = 0; dj->lfn = 0; /* 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[NS] = sn[NS]; dj->lfn = lfn; + } + + if (sn[NS] & NS_LFN) { /* When LFN is to be created, reserve an SFN + LFN entries. */ + for (ne = 0; lfn[ne]; ne++) ; + ne = (ne + 25) / 13; + } else { /* Otherwise reserve only an SFN entry. */ + ne = 1; + } + + /* Reserve contiguous entries */ + res = dir_sdi(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 == DDE || c == 0) { /* Is it a blank entry? */ + if (n == 0) is = dj->index; /* First index of the contiguous entry */ + if (++n == ne) break; /* A contiguous entry that required count is found */ + } else { + n = 0; /* Not a blank entry. Restart to search */ + } + res = dir_next(dj, 1); /* Next entry with table stretch */ + } while (res == FR_OK); + + if (res == FR_OK && ne > 1) { /* Initialize LFN entry if needed */ + res = dir_sdi(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, 0); /* Next entry */ + } while (res == FR_OK && --ne); + } + } + +#else /* Non LFN configuration */ + res = dir_sdi(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 == DDE || c == 0) break; /* Is it a blank entry? */ + res = dir_next(dj, 1); /* Next entry with table stretch */ + } 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, SZ_DIR); /* Clean the entry */ + mem_cpy(dir, dj->fn, 11); /* Put SFN */ +#if _USE_LFN + dir[DIR_NTres] = *(dj->fn+NS) & (NS_BODY | NS_EXT); /* Put NT flag */ +#endif + 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_sdi(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 = DDE; /* Mark the entry "deleted" */ + dj->fs->wflag = 1; + if (dj->index >= i) break; /* When reached SFN, all entries of the object has been deleted. */ + res = dir_next(dj, 0); /* Next entry */ + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR; + } + +#else /* Non LFN configuration */ + res = dir_sdi(dj, dj->index); + if (res == FR_OK) { + res = move_window(dj->fs, dj->sect); + if (res == FR_OK) { + *dj->dir = DDE; /* 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 TCHAR **path /* Pointer to pointer to the segment in the path string */ +) +{ +#ifdef _EXCVT + static const BYTE excvt[] = _EXCVT; /* Upper conversion table for extended chars */ +#endif + +#if _USE_LFN /* LFN configuration */ + BYTE b, cf; + WCHAR w, *lfn; + UINT i, ni, si, di; + const TCHAR *p; + + /* Create LFN in Unicode */ + for (p = *path; *p == '/' || *p == '\\'; p++) ; /* Strip duplicated separator */ + lfn = dj->lfn; + si = di = 0; + for (;;) { + w = p[si++]; /* Get a character */ + if (w < ' ' || w == '/' || w == '\\') break; /* Break on end of segment */ + if (di >= _MAX_LFN) /* Reject too long name */ + return FR_INVALID_NAME; +#if !_LFN_UNICODE + w &= 0xFF; + if (IsDBCS1(w)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ + b = (BYTE)p[si++]; /* Get 2nd byte */ + if (!IsDBCS2(b)) + return FR_INVALID_NAME; /* Reject invalid sequence */ + w = (w << 8) + b; /* Create a DBC */ + } + w = ff_convert(w, 1); /* Convert ANSI/OEM to Unicode */ + if (!w) return FR_INVALID_NAME; /* Reject invalid code */ +#endif + if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) /* Reject illegal chars for LFN */ + return FR_INVALID_NAME; + lfn[di++] = w; /* Store the Unicode char */ + } + *path = &p[si]; /* Return pointer to the next segment */ + cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ +#if _FS_RPATH + if ((di == 1 && lfn[di-1] == '.') || /* Is this a dot entry? */ + (di == 2 && lfn[di-1] == '.' && lfn[di-2] == '.')) { + lfn[di] = 0; + for (i = 0; i < 11; i++) + dj->fn[i] = (i < di) ? '.' : ' '; + dj->fn[i] = cf | NS_DOT; /* This is a dot entry */ + return FR_OK; + } +#endif + while (di) { /* Strip trailing spaces and dots */ + w = lfn[di-1]; + if (w != ' ' && w != '.') break; + di--; + } + if (!di) return FR_INVALID_NAME; /* Reject nul string */ + + lfn[di] = 0; /* LFN is created */ + + /* Create SFN in directory form */ + mem_set(dj->fn, ' ', 11); + for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ; /* Strip leading spaces and dots */ + if (si) cf |= NS_LOSS | NS_LFN; + 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) break; /* Break on end of the LFN */ + if (w == ' ' || (w == '.' && si != di)) { /* Remove spaces and dots */ + cf |= NS_LOSS | NS_LFN; continue; + } + + if (i >= ni || si == di) { /* Extension or end of SFN */ + if (ni == 11) { /* Long extension */ + cf |= NS_LOSS | NS_LFN; break; + } + if (si != di) cf |= NS_LOSS | NS_LFN; /* Out of 8.3 format */ + if (si > di) break; /* No extension */ + si = di; i = 8; ni = 11; /* Enter extension section */ + b <<= 2; continue; + } + + if (w >= 0x80) { /* Non ASCII char */ +#ifdef _EXCVT + w = ff_convert(w, 0); /* Unicode -> OEM code */ + if (w) w = excvt[w - 0x80]; /* Convert extended char to upper (SBCS) */ +#else + w = ff_convert(ff_wtoupper(w), 0); /* Upper converted Unicode -> OEM code */ +#endif + cf |= NS_LFN; /* Force create LFN entry */ + } + + if (_DF1S && w >= 0x100) { /* Double byte char (always false on SBCS cfg) */ + if (i >= ni - 1) { + cf |= NS_LOSS | NS_LFN; i = ni; continue; + } + dj->fn[i++] = (BYTE)(w >> 8); + } else { /* Single byte char */ + if (!w || chk_chr("+,;=[]", w)) { /* Replace illegal chars for SFN */ + w = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */ + } else { + if (IsUpper(w)) { /* ASCII large capital */ + b |= 2; + } else { + if (IsLower(w)) { /* ASCII small capital */ + b |= 1; w -= 0x20; + } + } + } + } + dj->fn[i++] = (BYTE)w; + } + + if (dj->fn[0] == DDE) dj->fn[0] = NDDE; /* If the first char collides with deleted mark, replace it with 0x05 */ + + if (ni == 8) b <<= 2; + if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) /* Create LFN entry when there are composite capitals */ + cf |= NS_LFN; + if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended char, NT flags are created */ + if ((b & 0x03) == 0x01) cf |= NS_EXT; /* NT flag (Extension has only small capital) */ + if ((b & 0x0C) == 0x04) cf |= NS_BODY; /* NT flag (Filename has only small capital) */ + } + + dj->fn[NS] = cf; /* SFN is created */ + + return FR_OK; + + +#else /* Non-LFN configuration */ + BYTE b, c, d, *sfn; + UINT ni, si, i; + const char *p; + + /* Create file name in directory form */ + for (p = *path; *p == '/' || *p == '\\'; p++) ; /* Strip duplicated separator */ + sfn = dj->fn; + mem_set(sfn, ' ', 11); + si = i = b = 0; ni = 8; +#if _FS_RPATH + if (p[si] == '.') { /* Is this a dot entry? */ + for (;;) { + c = (BYTE)p[si++]; + if (c != '.' || si >= 3) break; + sfn[i++] = c; + } + if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; + *path = &p[si]; /* Return pointer to the next segment */ + sfn[NS] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of path */ + return FR_OK; + } +#endif + for (;;) { + c = (BYTE)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) { /* Extended char? */ + b |= 3; /* Eliminate NT flag */ +#ifdef _EXCVT + c = excvt[c-0x80]; /* Upper conversion (SBCS) */ +#else +#if !_DF1S /* ASCII only cfg */ + return FR_INVALID_NAME; +#endif +#endif + } + if (IsDBCS1(c)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ + d = (BYTE)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 { /* Single byte code */ + if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) /* Reject illegal chrs for SFN */ + return FR_INVALID_NAME; + if (IsUpper(c)) { /* ASCII large capital? */ + b |= 2; + } else { + if (IsLower(c)) { /* ASCII small capital? */ + b |= 1; c -= 0x20; + } + } + sfn[i++] = c; + } + } + *path = &p[si]; /* Return pointer to the next segment */ + c = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ + + if (!i) return FR_INVALID_NAME; /* Reject nul string */ + if (sfn[0] == DDE) sfn[0] = NDDE; /* When first char collides with DDE, replace it with 0x05 */ + + if (ni == 8) b <<= 2; + if ((b & 0x03) == 0x01) c |= NS_EXT; /* NT flag (Name extension has only small capital) */ + if ((b & 0x0C) == 0x04) c |= NS_BODY; /* NT flag (Name body has only small capital) */ + + sfn[NS] = c; /* Store NT flag, File name is created */ + + return FR_OK; +#endif +} + + + + +/*-----------------------------------------------------------------------*/ +/* 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 the file information to be filled */ +) +{ + UINT i; + BYTE nt, *dir; + TCHAR *p, c; + + + p = fno->fname; + if (dj->sect) { + dir = dj->dir; + nt = dir[DIR_NTres]; /* NT flag */ + for (i = 0; i < 8; i++) { /* Copy name body */ + c = dir[i]; + if (c == ' ') break; + if (c == NDDE) c = (TCHAR)DDE; + if (_USE_LFN && (nt & NS_BODY) && IsUpper(c)) c += 0x20; +#if _LFN_UNICODE + if (IsDBCS1(c) && i < 7 && IsDBCS2(dir[i+1])) + c = (c << 8) | dir[++i]; + c = ff_convert(c, 1); + if (!c) c = '?'; +#endif + *p++ = c; + } + if (dir[8] != ' ') { /* Copy name extension */ + *p++ = '.'; + for (i = 8; i < 11; i++) { + c = dir[i]; + if (c == ' ') break; + if (_USE_LFN && (nt & NS_EXT) && IsUpper(c)) c += 0x20; +#if _LFN_UNICODE + if (IsDBCS1(c) && i < 10 && IsDBCS2(dir[i+1])) + c = (c << 8) | dir[++i]; + c = ff_convert(c, 1); + if (!c) c = '?'; +#endif + *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; /* Terminate SFN str by a \0 */ + +#if _USE_LFN + if (fno->lfname && fno->lfsize) { + TCHAR *tp = fno->lfname; + WCHAR w, *lfn; + + i = 0; + if (dj->sect && dj->lfn_idx != 0xFFFF) {/* Get LFN if available */ + lfn = dj->lfn; + while ((w = *lfn++) != 0) { /* Get an LFN char */ +#if !_LFN_UNICODE + w = ff_convert(w, 0); /* Unicode -> OEM conversion */ + if (!w) { i = 0; break; } /* Could not convert, no LFN */ + if (_DF1S && w >= 0x100) /* Put 1st byte if it is a DBC (always false on SBCS cfg) */ + tp[i++] = (TCHAR)(w >> 8); +#endif + if (i >= fno->lfsize - 1) { i = 0; break; } /* Buffer overflow, no LFN */ + tp[i++] = (TCHAR)w; + } + } + tp[i] = 0; /* Terminate the LFN str by a \0 */ + } +#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 TCHAR *path /* Full-path string to find a file or directory */ +) +{ + FRESULT res; + BYTE *dir, ns; + + +#if _FS_RPATH + if (*path == '/' || *path == '\\') { /* There is a heading separator */ + path++; dj->sclust = 0; /* Strip it and start from the root dir */ + } else { /* No heading separator */ + dj->sclust = dj->fs->cdir; /* Start from the current dir */ + } +#else + if (*path == '/' || *path == '\\') /* Strip heading separator if exist */ + path++; + dj->sclust = 0; /* Start from the root dir */ +#endif + + if ((UINT)*path < ' ') { /* Nul path means the start directory itself */ + res = dir_sdi(dj, 0); + dj->dir = 0; + + } else { /* Follow path */ + for (;;) { + res = create_name(dj, &path); /* Get a segment */ + if (res != FR_OK) break; + res = dir_find(dj); /* Find it */ + ns = *(dj->fn+NS); + if (res != FR_OK) { /* Failed to find the object */ + if (res != FR_NO_FILE) break; /* Abort if any hard error occured */ + /* Object not found */ + if (_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exit */ + dj->sclust = 0; dj->dir = 0; /* It is the root dir */ + res = FR_OK; + if (!(ns & NS_LAST)) continue; + } else { /* Could not find the object */ + if (!(ns & NS_LAST)) res = FR_NO_PATH; + } + break; + } + if (ns & NS_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 = LD_CLUST(dir); + } + } + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Load boot record and check if it is an FAT boot record */ +/*-----------------------------------------------------------------------*/ + +static +BYTE check_fs ( /* 0:The FAT BR, 1:Valid BR but not an FAT, 2:Not a BR, 3:Disk 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->drv, 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 ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */ + return 0; + if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146) + return 0; + + return 1; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Check if the file system object is valid or not */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT chk_mounted ( /* FR_OK(0): successful, !=0: any error occurred */ + const TCHAR **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 */ +) +{ + BYTE fmt, b, *tbl; + UINT vol; + DSTATUS stat; + DWORD bsect, fasize, tsect, sysect, nclst, szbfat; + WORD nrsv; + const TCHAR *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] == ':') { /* Found a drive number, get and strip it */ + p += 2; *path = p; /* Return pointer to the path name */ + } else { /* No drive number is given */ +#if _FS_RPATH + vol = CurrVol; /* Use current drive */ +#else + vol = 0; /* Use drive 0 */ +#endif + } + + /* Check if the logical drive is valid or not */ + if (vol >= _VOLUMES) /* Is the drive number valid? */ + return FR_INVALID_DRIVE; + *rfs = fs = FatFs[vol]; /* Return pointer to the corresponding file system object */ + if (!fs) return FR_NOT_ENABLED; /* Is the file system object available? */ + + ENTER_FF(fs); /* Lock file system */ + + if (fs->fs_type) { /* If the logical drive has been mounted */ + stat = disk_status(fs->drv); + if (!(stat & STA_NOINIT)) { /* and the 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 mounted. */ + /* Following code attempts to mount a volume. (analyze BPB and initialize the fs object) */ + + fs->fs_type = 0; /* Clear the file system object */ + fs->drv = (BYTE)LD2PD(vol); /* Bind the logical drive and a physical drive */ + stat = disk_initialize(fs->drv); /* Initialize low level disk I/O layer */ + if (stat & STA_NOINIT) /* Check if the initialization succeeded */ + return FR_NOT_READY; /* Failed to initialize due to no media or hard error */ +#if _MAX_SS != 512 /* Get disk sector size (variable sector size cfg only) */ + //if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &fs->ssize) != RES_OK) + // return FR_DISK_ERR; +#endif +#if !_FS_READONLY + if (chk_wp && (stat & STA_PROTECT)) /* Check disk write protection if needed */ + return FR_WRITE_PROTECTED; +#endif + /* Search FAT partition on the drive. Supports only generic partitionings, FDISK and SFD. */ + fmt = check_fs(fs, bsect = 0); /* Check sector 0 if it is a VBR */ + if (fmt == 1) { /* Not an FAT-VBR, the disk may be partitioned */ + /* Check the partition listed in top of the partition table */ + tbl = &fs->win[MBR_Table + LD2PT(vol) * SZ_PTE];/* 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) return FR_NO_FILESYSTEM; /* No FAT volume is found */ + + /* Following code initializes the file system object */ + + if (LD_WORD(fs->win+BPB_BytsPerSec) != SS(fs)) /* (BPB_BytsPerSec must be equal to the physical sector size) */ + return FR_NO_FILESYSTEM; + + fasize = LD_WORD(fs->win+BPB_FATSz16); /* Number of sectors per FAT */ + if (!fasize) fasize = LD_DWORD(fs->win+BPB_FATSz32); + fs->fsize = fasize; + + fs->n_fats = b = fs->win[BPB_NumFATs]; /* Number of FAT copies */ + if (b != 1 && b != 2) return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */ + fasize *= b; /* Number of sectors for FAT area */ + + fs->csize = b = fs->win[BPB_SecPerClus]; /* Number of sectors per cluster */ + if (!b || (b & (b - 1))) return FR_NO_FILESYSTEM; /* (Must be power of 2) */ + + fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt); /* Number of root directory entries */ + if (fs->n_rootdir % (SS(fs) / SZ_DIR)) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be sector aligned) */ + + tsect = LD_WORD(fs->win+BPB_TotSec16); /* Number of sectors on the volume */ + if (!tsect) tsect = LD_DWORD(fs->win+BPB_TotSec32); + + nrsv = LD_WORD(fs->win+BPB_RsvdSecCnt); /* Number of reserved sectors */ + if (!nrsv) return FR_NO_FILESYSTEM; /* (BPB_RsvdSecCnt must not be 0) */ + + /* Determine the FAT sub type */ + sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZ_DIR); /* RSV+FAT+DIR */ + if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + nclst = (tsect - sysect) / fs->csize; /* Number of clusters */ + if (!nclst) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + fmt = FS_FAT12; + if (nclst >= MIN_FAT16) fmt = FS_FAT16; + if (nclst >= MIN_FAT32) fmt = FS_FAT32; + + /* Boundaries and Limits */ + fs->n_fatent = nclst + 2; /* Number of FAT entries */ + fs->database = bsect + sysect; /* Data start sector */ + fs->fatbase = bsect + nrsv; /* FAT start sector */ + if (fmt == FS_FAT32) { + if (fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */ + fs->dirbase = LD_DWORD(fs->win+BPB_RootClus); /* Root directory start cluster */ + szbfat = fs->n_fatent * 4; /* (Required FAT size) */ + } else { + if (!fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must not be 0) */ + fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */ + szbfat = (fmt == FS_FAT16) ? /* (Required FAT size) */ + fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1); + } + if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) /* (BPB_FATSz must not be less than required) */ + return FR_NO_FILESYSTEM; + +#if !_FS_READONLY + /* Initialize cluster allocation information */ + fs->free_clust = 0xFFFFFFFF; + fs->last_clust = 0; + + /* Get fsinfo if available */ + if (fmt == FS_FAT32) { + fs->fsi_flag = 0; + fs->fsi_sector = bsect + LD_WORD(fs->win+BPB_FSInfo); + if (disk_read(fs->drv, 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->fs_type = fmt; /* FAT sub-type */ + fs->id = ++Fsid; /* File system mount ID */ + fs->winsect = 0; /* Invalidate sector cache */ + fs->wflag = 0; +#if _FS_RPATH + fs->cdir = 0; /* Current directory (root dir) */ +#endif +#if _FS_SHARE /* Clear file lock semaphores */ + clear_lock(fs); +#endif + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* 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->drv) & STA_NOINIT) + return FR_NOT_READY; + + return FR_OK; +} + + + + +/*-------------------------------------------------------------------------- + + Public Functions + +--------------------------------------------------------------------------*/ + + + +/*-----------------------------------------------------------------------*/ +/* Mount/Unmount a Logical 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 >= _VOLUMES) /* Check if the drive number is valid */ + return FR_INVALID_DRIVE; + rfs = FatFs[vol]; /* Get current fs object */ + + if (rfs) { +#if _FS_SHARE + clear_lock(rfs); +#endif +#if _FS_REENTRANT /* Discard sync object of the current volume */ + if (!ff_del_syncobj(rfs->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 TCHAR *path, /* Pointer to the file name */ + BYTE mode /* Access mode and file open mode flags */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir; + DEF_NAMEBUF; + + + fp->fs = 0; /* Clear file object */ + +#if !_FS_READONLY + mode &= FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW; + res = chk_mounted(&path, &dj.fs, (BYTE)(mode & ~FA_READ)); +#else + mode &= FA_READ; + res = chk_mounted(&path, &dj.fs, 0); +#endif + INIT_BUF(dj); + if (res == FR_OK) + res = follow_path(&dj, path); /* Follow the file path */ + dir = dj.dir; + +#if !_FS_READONLY /* R/W configuration */ + if (res == FR_OK) { + if (!dir) /* Current dir itself */ + res = FR_INVALID_NAME; +#if _FS_SHARE + else + res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0); +#endif + } + /* Create or Open a file */ + if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) { + DWORD dw, cl; + + if (res != FR_OK) { /* No file, create new */ + if (res == FR_NO_FILE) /* There is no file to open, create a new entry */ +#if _FS_SHARE + res = enq_lock(dj.fs) ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES; +#else + res = dir_register(&dj); +#endif + mode |= FA_CREATE_ALWAYS; /* File is created */ + dir = dj.dir; /* New entry */ + } + else { /* Any object is already existing */ + if (dir[DIR_Attr] & (AM_RDO | AM_DIR)) { /* Cannot overwrite it (R/O or DIR) */ + res = FR_DENIED; + } else { + if (mode & FA_CREATE_NEW) /* Cannot create as new file */ + res = FR_EXIST; + } + } + if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* Truncate it if overwrite mode */ + dw = get_fattime(); /* Created time */ + ST_DWORD(dir+DIR_CrtTime, dw); + dir[DIR_Attr] = 0; /* Reset attribute */ + ST_DWORD(dir+DIR_FileSize, 0); /* size = 0 */ + cl = LD_CLUST(dir); /* Get start cluster */ + ST_CLUST(dir, 0); /* cluster = 0 */ + dj.fs->wflag = 1; + if (cl) { /* Remove the cluster chain if exist */ + dw = dj.fs->winsect; + res = remove_chain(dj.fs, cl); + if (res == FR_OK) { + dj.fs->last_clust = cl - 1; /* Reuse the cluster hole */ + res = move_window(dj.fs, dw); + } + } + } + } + else { /* Open an existing file */ + if (res == FR_OK) { /* Follow succeeded */ + if (dir[DIR_Attr] & AM_DIR) { /* It is a directory */ + res = FR_NO_FILE; + } else { + if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */ + res = FR_DENIED; + } + } + } + if (res == FR_OK) { + if (mode & FA_CREATE_ALWAYS) /* Set file change flag if created or overwritten */ + mode |= FA__WRITTEN; + fp->dir_sect = dj.fs->winsect; /* Pointer to the directory entry */ + fp->dir_ptr = dir; +#if _FS_SHARE + fp->lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0); + if (!fp->lockid) res = FR_INT_ERR; +#endif + } + +#else /* R/O configuration */ + if (res == FR_OK) { /* Follow succeeded */ + if (!dir) { /* Current dir itself */ + res = FR_INVALID_NAME; + } else { + if (dir[DIR_Attr] & AM_DIR) /* It is a directory */ + res = FR_NO_FILE; + } + } +#endif + FREE_BUF(); + + if (res == FR_OK) { + fp->flag = mode; /* File access mode */ + fp->sclust = LD_CLUST(dir); /* File start cluster */ + fp->fsize = LD_DWORD(dir+DIR_FileSize); /* File size */ + fp->fptr = 0; /* File pointer */ + fp->dsect = 0; +#if _USE_FASTSEEK + fp->cltbl = 0; /* Normal seek mode */ +#endif + fp->fs = dj.fs; fp->id = dj.fs->id; /* Validate file object */ + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* 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 csect, *rbuff = buff; + + + *br = 0; /* Initialize byte counter */ + + res = validate(fp->fs, fp->id); /* Check validity */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Aborted file? */ + 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 read */ + rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) { + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ + if (!csect) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->sclust; /* Follow from the origin */ + } else { /* Middle or end of the file */ +#if _USE_FASTSEEK + if (fp->cltbl) + clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ + else +#endif + clst = get_fat(fp->fs, fp->clust); /* Follow cluster chain on the FAT */ + } + if (clst < 2) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + } + sect = clust2sect(fp->fs, fp->clust); /* Get current sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += csect; + cc = btr / SS(fp->fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Read maximum contiguous sectors directly */ + if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */ + cc = fp->fs->csize - csect; + if (disk_read(fp->fs->drv, rbuff, sect, (BYTE)cc) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); +#if !_FS_READONLY && _FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */ +#if _FS_TINY + if (fp->fs->wflag && fp->fs->winsect - sect < cc) + mem_cpy(rbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), fp->fs->win, SS(fp->fs)); +#else + if ((fp->flag & FA__DIRTY) && fp->dsect - sect < cc) + mem_cpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs)); +#endif +#endif + rcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ + continue; + } +#if !_FS_TINY + if (fp->dsect != sect) { /* Load data sector if not in cache */ +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + if (disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK) /* Fill sector cache */ + ABORT(fp->fs, FR_DISK_ERR); + } +#endif + fp->dsect = sect; + } + 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; + BYTE csect; + + + *bw = 0; /* Initialize byte counter */ + + res = validate(fp->fs, fp->id); /* Check validity */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Aborted file? */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (!(fp->flag & FA_WRITE)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + if ((DWORD)(fp->fsize + btw) < fp->fsize) btw = 0; /* File size cannot reach 4GB */ + + for ( ; btw; /* Repeat until all data written */ + wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) { + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ + if (!csect) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->sclust; /* Follow from the origin */ + if (clst == 0) /* When no cluster is allocated, */ + fp->sclust = clst = create_chain(fp->fs, 0); /* Create a new cluster chain */ + } else { /* Middle or end of the file */ +#if _USE_FASTSEEK + if (fp->cltbl) + clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ + else +#endif + clst = create_chain(fp->fs, fp->clust); /* Follow or stretch cluster chain on the FAT */ + } + 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->clust = clst; /* Update current cluster */ + } +#if _FS_TINY + if (fp->fs->winsect == fp->dsect && move_window(fp->fs, 0)) /* Write-back sector cache */ + ABORT(fp->fs, FR_DISK_ERR); +#else + if (fp->flag & FA__DIRTY) { /* Write-back sector cache */ + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + sect = clust2sect(fp->fs, fp->clust); /* Get current sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += csect; + cc = btw / SS(fp->fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Write maximum contiguous sectors directly */ + if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */ + cc = fp->fs->csize - csect; + if (disk_write(fp->fs->drv, wbuff, sect, (BYTE)cc) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); +#if _FS_TINY + if (fp->fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + mem_cpy(fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs)); + fp->fs->wflag = 0; + } +#else + if (fp->dsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + mem_cpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs)); + fp->flag &= ~FA__DIRTY; + } +#endif + wcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ + continue; + } +#if _FS_TINY + if (fp->fptr >= fp->fsize) { /* Avoid silly cache 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 cache with file data */ + if (fp->fptr < fp->fsize && + disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + } +#endif + fp->dsect = sect; + } + 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 change 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->drv, fp->buf, fp->dsect, 1) != RES_OK) + LEAVE_FF(fp->fs, FR_DISK_ERR); + fp->flag &= ~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_CLUST(dir, fp->sclust); /* Update start cluster */ + tim = get_fattime(); /* Update updated time */ + ST_DWORD(dir+DIR_WrtTime, tim); + fp->flag &= ~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 + FATFS *fs = fp->fs; + res = validate(fs, fp->id); + if (res == FR_OK) fp->fs = 0; /* Discard file object */ + LEAVE_FF(fs, res); + +#else + res = f_sync(fp); /* Flush cached data */ +#if _FS_SHARE + if (res == FR_OK) { /* Decrement open counter */ +#if _FS_REENTRANT + res = validate(fp->fs, fp->id); + if (res == FR_OK) { + res = dec_lock(fp->lockid); + unlock_fs(fp->fs, FR_OK); + } +#else + res = dec_lock(fp->lockid); +#endif + } +#endif + if (res == FR_OK) fp->fs = 0; /* Discard file object */ + return res; +#endif +} + + + + +/*-----------------------------------------------------------------------*/ +/* Current Drive/Directory Handlings */ +/*-----------------------------------------------------------------------*/ + +#if _FS_RPATH >= 1 + +FRESULT f_chdrive ( + BYTE drv /* Drive number */ +) +{ + if (drv >= _VOLUMES) return FR_INVALID_DRIVE; + + CurrVol = drv; + + return FR_OK; +} + + + +FRESULT f_chdir ( + const TCHAR *path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj.fs, 0); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the path */ + FREE_BUF(); + if (res == FR_OK) { /* Follow completed */ + if (!dj.dir) { + dj.fs->cdir = dj.sclust; /* Start directory itself */ + } else { + if (dj.dir[DIR_Attr] & AM_DIR) /* Reached to the directory */ + dj.fs->cdir = LD_CLUST(dj.dir); + else + res = FR_NO_PATH; /* Reached but a file */ + } + } + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + + LEAVE_FF(dj.fs, res); +} + + +#if _FS_RPATH >= 2 +FRESULT f_getcwd ( + TCHAR *path, /* Pointer to the directory path */ + UINT sz_path /* Size of path */ +) +{ + FRESULT res; + DIR dj; + UINT i, n; + DWORD ccl; + TCHAR *tp; + FILINFO fno; + DEF_NAMEBUF; + + + *path = 0; + res = chk_mounted((const TCHAR**)&path, &dj.fs, 0); /* Get current volume */ + if (res == FR_OK) { + INIT_BUF(dj); + i = sz_path; /* Bottom of buffer (dir stack base) */ + dj.sclust = dj.fs->cdir; /* Start to follow upper dir from current dir */ + while ((ccl = dj.sclust) != 0) { /* Repeat while current dir is a sub-dir */ + res = dir_sdi(&dj, 1); /* Get parent dir */ + if (res != FR_OK) break; + res = dir_read(&dj); + if (res != FR_OK) break; + dj.sclust = LD_CLUST(dj.dir); /* Goto parent dir */ + res = dir_sdi(&dj, 0); + if (res != FR_OK) break; + do { /* Find the entry links to the child dir */ + res = dir_read(&dj); + if (res != FR_OK) break; + if (ccl == LD_CLUST(dj.dir)) break; /* Found the entry */ + res = dir_next(&dj, 0); + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */ + if (res != FR_OK) break; +#if _USE_LFN + fno.lfname = path; + fno.lfsize = i; +#endif + get_fileinfo(&dj, &fno); /* Get the dir name and push it to the buffer */ + tp = fno.fname; + if (_USE_LFN && *path) tp = path; + for (n = 0; tp[n]; n++) ; + if (i < n + 3) { + res = FR_NOT_ENOUGH_CORE; break; + } + while (n) path[--i] = tp[--n]; + path[--i] = '/'; + } + tp = path; + if (res == FR_OK) { + *tp++ = '0' + CurrVol; /* Put drive number */ + *tp++ = ':'; + if (i == sz_path) { /* Root-dir */ + *tp++ = '/'; + } else { /* Sub-dir */ + do /* Add stacked path str */ + *tp++ = path[i++]; + while (i < sz_path); + } + } + *tp = 0; + FREE_BUF(); + } + + LEAVE_FF(dj.fs, res); +} +#endif /* _FS_RPATH >= 2 */ +#endif /* _FS_RPATH >= 1 */ + +DWORD get_fattime (void) +{ + return 0; +} + +#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; + + + 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 _USE_FASTSEEK + if (fp->cltbl) { /* Fast seek */ + DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl; + + if (ofs == CREATE_LINKMAP) { /* Create CLMT */ + tbl = fp->cltbl; + tlen = *tbl++; ulen = 2; /* Given table size and required table size */ + cl = fp->sclust; /* Top of the chain */ + if (cl) { + do { + /* Get a fragment */ + tcl = cl; ncl = 0; ulen += 2; /* Top, length and used items */ + do { + pcl = cl; ncl++; + cl = get_fat(fp->fs, cl); + if (cl <= 1) ABORT(fp->fs, FR_INT_ERR); + if (cl == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + } while (cl == pcl + 1); + if (ulen <= tlen) { /* Store the length and top of the fragment */ + *tbl++ = ncl; *tbl++ = tcl; + } + } while (cl < fp->fs->n_fatent); /* Repeat until end of chain */ + } + *fp->cltbl = ulen; /* Number of items used */ + if (ulen <= tlen) + *tbl = 0; /* Terminate table */ + else + res = FR_NOT_ENOUGH_CORE; /* Given table size is smaller than required */ + + } else { /* Fast seek */ + if (ofs > fp->fsize) /* Clip offset at the file size */ + ofs = fp->fsize; + fp->fptr = ofs; /* Set file pointer */ + if (ofs) { + fp->clust = clmt_clust(fp, ofs - 1); + dsc = clust2sect(fp->fs, fp->clust); + if (!dsc) ABORT(fp->fs, FR_INT_ERR); + dsc += (ofs - 1) / SS(fp->fs) & (fp->fs->csize - 1); + if (fp->fptr % SS(fp->fs) && dsc != fp->dsect) { /* Refill sector cache if needed */ +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + if (disk_read(fp->fs->drv, fp->buf, dsc, 1) != RES_OK) /* Load current sector */ + ABORT(fp->fs, FR_DISK_ERR); +#endif + fp->dsect = dsc; + } + } + } + } else +#endif + + /* Normal Seek */ + { + DWORD clst, bcs, nsect, ifptr; + + 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 = nsect = 0; + if (ofs) { + 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->clust; + } else { /* When seek to back cluster, */ + clst = fp->sclust; /* 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->sclust = clst; + } +#endif + fp->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 stretch if in write mode */ + if (clst == 0) { /* When disk gets full, clip file size */ + ofs = bcs; break; + } + } else +#endif + clst = get_fat(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->n_fatent) ABORT(fp->fs, FR_INT_ERR); + fp->clust = clst; + fp->fptr += bcs; + ofs -= bcs; + } + fp->fptr += ofs; + if (ofs % SS(fp->fs)) { + nsect = clust2sect(fp->fs, clst); /* Current sector */ + if (!nsect) ABORT(fp->fs, FR_INT_ERR); + nsect += ofs / SS(fp->fs); + } + } + } + if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) { /* Fill sector cache if needed */ +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + if (disk_read(fp->fs->drv, fp->buf, nsect, 1) != RES_OK) /* Fill sector cache */ + ABORT(fp->fs, FR_DISK_ERR); +#endif + fp->dsect = nsect; + } +#if !_FS_READONLY + if (fp->fptr > fp->fsize) { /* Set file change 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 TCHAR *path /* Pointer to the directory path */ +) +{ + FRESULT res; + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj->fs, 0); + if (res == FR_OK) { + INIT_BUF(*dj); + res = follow_path(dj, path); /* Follow the path to the directory */ + FREE_BUF(); + if (res == FR_OK) { /* Follow completed */ + if (dj->dir) { /* It is not the root dir */ + if (dj->dir[DIR_Attr] & AM_DIR) { /* The object is a directory */ + dj->sclust = LD_CLUST(dj->dir); + } else { /* The object is not a directory */ + res = FR_NO_PATH; + } + } + if (res == FR_OK) { + dj->id = dj->fs->id; + res = dir_sdi(dj, 0); /* Rewind dir */ + } + } + 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; + DEF_NAMEBUF; + + + res = validate(dj->fs, dj->id); /* Check validity of the object */ + if (res == FR_OK) { + if (!fno) { + res = dir_sdi(dj, 0); /* Rewind the directory object */ + } else { + INIT_BUF(*dj); + res = dir_read(dj); /* Read an directory item */ + if (res == FR_NO_FILE) { /* Reached end of dir */ + 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, 0); /* Increment index for next */ + if (res == FR_NO_FILE) { + dj->sect = 0; + res = FR_OK; + } + } + FREE_BUF(); + } + } + + LEAVE_FF(dj->fs, res); +} + + + +#if _FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Get File Status */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_stat ( + const TCHAR *path, /* Pointer to the file path */ + FILINFO *fno /* Pointer to file information to return */ +) +{ + FRESULT res; + DIR dj; + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj.fs, 0); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) { /* Follow completed */ + if (dj.dir) /* Found an object */ + get_fileinfo(&dj, fno); + else /* It is root dir */ + res = FR_INVALID_NAME; + } + FREE_BUF(); + } + + LEAVE_FF(dj.fs, res); +} + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Get Number of Free Clusters */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getfree ( + const TCHAR *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, stat; + UINT i; + BYTE fat, *p; + + + /* Get drive number */ + res = chk_mounted(&path, fatfs, 0); + if (res == FR_OK) { + /* If free_clust is valid, return it without full cluster scan */ + if ((*fatfs)->free_clust <= (*fatfs)->n_fatent - 2) { + *nclst = (*fatfs)->free_clust; + } else { + /* Get number of free clusters */ + fat = (*fatfs)->fs_type; + n = 0; + if (fat == FS_FAT12) { + clst = 2; + do { + stat = get_fat(*fatfs, clst); + if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } + if (stat == 1) { res = FR_INT_ERR; break; } + if (stat == 0) n++; + } while (++clst < (*fatfs)->n_fatent); + } else { + clst = (*fatfs)->n_fatent; + sect = (*fatfs)->fatbase; + i = 0; p = 0; + do { + if (!i) { + res = move_window(*fatfs, sect++); + if (res != FR_OK) break; + p = (*fatfs)->win; + i = SS(*fatfs); + } + if (fat == FS_FAT16) { + if (LD_WORD(p) == 0) n++; + p += 2; i -= 2; + } else { + if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) n++; + p += 4; i -= 4; + } + } while (--clst); + } + (*fatfs)->free_clust = n; + if (fat == FS_FAT32) (*fatfs)->fsi_flag = 1; + *nclst = n; + } + } + LEAVE_FF(*fatfs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* 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) { + if (fp->flag & FA__ERROR) { /* Check abort flag */ + res = FR_INT_ERR; + } else { + if (!(fp->flag & FA_WRITE)) /* Check access mode */ + res = FR_DENIED; + } + } + if (res == FR_OK) { + 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->sclust); + fp->sclust = 0; + } else { /* When truncate a part of the file, remove remaining clusters */ + ncl = get_fat(fp->fs, fp->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->n_fatent) { + res = put_fat(fp->fs, fp->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); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Delete a File or Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_unlink ( + const TCHAR *path /* Pointer to the file or directory path */ +) +{ + FRESULT res; + DIR dj, sdj; + BYTE *dir; + DWORD dclst; + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; /* Cannot remove dot entry */ +#if _FS_SHARE + if (res == FR_OK) res = chk_lock(&dj, 2); /* Cannot remove open file */ +#endif + if (res == FR_OK) { /* The object is accessible */ + dir = dj.dir; + if (!dir) { + res = FR_INVALID_NAME; /* Cannot remove the start directory */ + } else { + if (dir[DIR_Attr] & AM_RDO) + res = FR_DENIED; /* Cannot remove R/O object */ + } + dclst = LD_CLUST(dir); + if (res == FR_OK && (dir[DIR_Attr] & AM_DIR)) { /* Is it a sub-dir? */ + if (dclst < 2) { + res = FR_INT_ERR; + } else { + mem_cpy(&sdj, &dj, sizeof(DIR)); /* Check if the sub-dir is empty or not */ + sdj.sclust = dclst; + res = dir_sdi(&sdj, 2); /* Exclude dot entries */ + if (res == FR_OK) { + res = dir_read(&sdj); + if (res == FR_OK /* Not empty dir */ +#if _FS_RPATH + || dclst == sdj.fs->cdir /* Current dir */ +#endif + ) res = FR_DENIED; + if (res == FR_NO_FILE) res = FR_OK; /* Empty */ + } + } + } + if (res == FR_OK) { + res = dir_remove(&dj); /* Remove the directory entry */ + if (res == FR_OK) { + if (dclst) /* Remove the cluster chain if exist */ + res = remove_chain(dj.fs, dclst); + if (res == FR_OK) res = sync(dj.fs); + } + } + } + FREE_BUF(); + } + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Create a Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mkdir ( + const TCHAR *path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir, n; + DWORD dsc, dcl, pcl, tim = get_fattime(); + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) res = FR_EXIST; /* Any object with same name is already existing */ + if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + if (res == FR_NO_FILE) { /* Can create a new directory */ + dcl = create_chain(dj.fs, 0); /* Allocate a cluster for the new directory table */ + res = FR_OK; + if (dcl == 0) res = FR_DENIED; /* No space to allocate a new cluster */ + if (dcl == 1) res = FR_INT_ERR; + if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (res == FR_OK) /* Flush FAT */ + res = move_window(dj.fs, 0); + if (res == FR_OK) { /* Initialize the new directory table */ + dsc = clust2sect(dj.fs, dcl); + dir = dj.fs->win; + mem_set(dir, 0, SS(dj.fs)); + mem_set(dir+DIR_Name, ' ', 8+3); /* Create "." entry */ + dir[DIR_Name] = '.'; + dir[DIR_Attr] = AM_DIR; + ST_DWORD(dir+DIR_WrtTime, tim); + ST_CLUST(dir, dcl); + mem_cpy(dir+SZ_DIR, dir, SZ_DIR); /* Create ".." entry */ + dir[33] = '.'; pcl = dj.sclust; + if (dj.fs->fs_type == FS_FAT32 && pcl == dj.fs->dirbase) + pcl = 0; + ST_CLUST(dir+SZ_DIR, pcl); + for (n = dj.fs->csize; n; n--) { /* Write dot entries and clear following sectors */ + dj.fs->winsect = dsc++; + dj.fs->wflag = 1; + res = move_window(dj.fs, 0); + if (res != FR_OK) break; + mem_set(dir, 0, SS(dj.fs)); + } + } + if (res == FR_OK) res = dir_register(&dj); /* Register the object to the directoy */ + if (res != FR_OK) { + remove_chain(dj.fs, dcl); /* Could not register, remove cluster chain */ + } else { + dir = dj.dir; + dir[DIR_Attr] = AM_DIR; /* Attribute */ + ST_DWORD(dir+DIR_WrtTime, tim); /* Created time */ + ST_CLUST(dir, dcl); /* Table start cluster */ + dj.fs->wflag = 1; + res = sync(dj.fs); + } + } + FREE_BUF(); + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Attribute */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_chmod ( + const TCHAR *path, /* Pointer to the file path */ + BYTE value, /* Attribute bits */ + BYTE mask /* Attribute mask to change */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir; + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + FREE_BUF(); + if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + 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 TCHAR *path, /* Pointer to the file/directory name */ + const FILINFO *fno /* Pointer to the time stamp to be set */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir; + DEF_NAMEBUF; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + FREE_BUF(); + if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + 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 TCHAR *path_old, /* Pointer to the old name */ + const TCHAR *path_new /* Pointer to the new name */ +) +{ + FRESULT res; + DIR djo, djn; + BYTE buf[21], *dir; + DWORD dw; + DEF_NAMEBUF; + + + res = chk_mounted(&path_old, &djo.fs, 1); + if (res == FR_OK) { + djn.fs = djo.fs; + INIT_BUF(djo); + res = follow_path(&djo, path_old); /* Check old object */ + if (_FS_RPATH && res == FR_OK && (djo.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; +#if _FS_SHARE + if (res == FR_OK) res = chk_lock(&djo, 2); +#endif + if (res == FR_OK) { /* Old object is found */ + if (!djo.dir) { /* Is root dir? */ + res = FR_NO_FILE; + } else { + mem_cpy(buf, djo.dir+DIR_Attr, 21); /* Save the object information except for name */ + mem_cpy(&djn, &djo, sizeof(DIR)); /* Check new object */ + res = follow_path(&djn, path_new); + 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? */ +/* Start critical section that any interruption or error can cause cross-link */ + res = dir_register(&djn); /* Register the new entry */ + if (res == FR_OK) { + dir = djn.dir; /* Copy object information except for name */ + mem_cpy(dir+13, buf+2, 19); + dir[DIR_Attr] = buf[0] | AM_ARC; + djo.fs->wflag = 1; + if (djo.sclust != djn.sclust && (dir[DIR_Attr] & AM_DIR)) { /* Update .. entry in the directory if needed */ + dw = clust2sect(djn.fs, LD_CLUST(dir)); + if (!dw) { + res = FR_INT_ERR; + } else { + res = move_window(djn.fs, dw); + dir = djn.fs->win+SZ_DIR; /* .. entry */ + if (res == FR_OK && dir[1] == '.') { + dw = (djn.fs->fs_type == FS_FAT32 && djn.sclust == djn.fs->dirbase) ? 0 : djn.sclust; + ST_CLUST(dir, dw); + djn.fs->wflag = 1; + } + } + } + if (res == FR_OK) { + res = dir_remove(&djo); /* Remove old entry */ + if (res == FR_OK) + res = sync(djo.fs); + } + } +/* End critical section */ + } + } + } + FREE_BUF(); + } + LEAVE_FF(djo.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 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; + BYTE csect; + + + *bf = 0; /* Initialize byte counter */ + + 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)(0, 0); /* Repeat until all data transferred or stream becomes busy */ + fp->fptr += rcnt, *bf += rcnt, btr -= rcnt) { + csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + if (!csect) { /* On the cluster boundary? */ + clst = (fp->fptr == 0) ? /* On the top of the file? */ + fp->sclust : get_fat(fp->fs, fp->clust); + if (clst <= 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + } + } + sect = clust2sect(fp->fs, fp->clust); /* Get current data sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += csect; + 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 /* Number of root dir entries for FAT12/16 */ +#define N_FATS 1 /* Number of FAT copies (1 or 2) */ + + +FRESULT f_mkfs ( + BYTE drv, /* Logical drive number */ + BYTE sfd, /* Partitioning rule 0:FDISK, 1:SFD */ + UINT au /* Allocation unit size [bytes] */ +) +{ + static const WORD vst[] = { 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 0}; + static const WORD cst[] = {32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512}; + BYTE fmt, md, *tbl; + DWORD n_clst, vs, n, wsect; + UINT i; + DWORD b_vol, b_fat, b_dir, b_data; /* Offset (LBA) */ + DWORD n_vol, n_rsv, n_fat, n_dir; /* Size */ + FATFS *fs; + DSTATUS stat; + + + /* Check mounted drive and clear work area */ + if (drv >= _VOLUMES) return FR_INVALID_DRIVE; + 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 _MAX_SS != 512 /* Get disk sector size */ + if (disk_ioctl(drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK) + return FR_DISK_ERR; +#endif + if (disk_ioctl(drv, GET_SECTOR_COUNT, &n_vol) != RES_OK || n_vol < 128) + return FR_DISK_ERR; + b_vol = (sfd) ? 0 : 63; /* Volume start sector */ + n_vol -= b_vol; + if (au & (au - 1)) au = 0; /* Check validity of the AU size */ + if (!au) { /* AU auto selection */ + vs = n_vol / (2000 / (SS(fs) / 512)); + for (i = 0; vs < vst[i]; i++) ; + au = cst[i]; + } + au /= SS(fs); /* Number of sectors per cluster */ + if (au == 0) au = 1; + if (au > 128) au = 128; + + /* Pre-compute number of clusters and FAT syb-type */ + n_clst = n_vol / au; + fmt = FS_FAT12; + if (n_clst >= MIN_FAT16) fmt = FS_FAT16; + if (n_clst >= MIN_FAT32) fmt = FS_FAT32; + + /* Determine offset and size of FAT structure */ + if (fmt == FS_FAT32) { + n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs); + n_rsv = 32; + n_dir = 0; + } else { + n_fat = (fmt == FS_FAT12) ? (n_clst * 3 + 1) / 2 + 3 : (n_clst * 2) + 4; + n_fat = (n_fat + SS(fs) - 1) / SS(fs); + n_rsv = 1; + n_dir = (DWORD)N_ROOTDIR * SZ_DIR / SS(fs); + } + b_fat = b_vol + n_rsv; /* FAT area start sector */ + b_dir = b_fat + n_fat * N_FATS; /* Directory area start sector */ + b_data = b_dir + n_dir; /* Data area start sector */ + if (n_vol < b_data + au) return FR_MKFS_ABORTED; /* Too small volume */ + + /* Align data start sector to erase block boundary (for flash memory media) */ + if (disk_ioctl(drv, GET_BLOCK_SIZE, &n) != RES_OK || !n || n > 32768) n = 1; + n = (b_data + n - 1) & ~(n - 1); /* Next nearest erase block from current data start */ + n = (n - b_data) / N_FATS; + if (fmt == FS_FAT32) { /* FAT32: Move FAT offset */ + n_rsv += n; + b_fat += n; + } else { /* FAT12/16: Expand FAT size */ + n_fat += n; + } + + /* Determine number of clusters and final check of validity of the FAT sub-type */ + n_clst = (n_vol - n_rsv - n_fat * N_FATS - n_dir) / au; + if ( (fmt == FS_FAT16 && n_clst < MIN_FAT16) + || (fmt == FS_FAT32 && n_clst < MIN_FAT32)) + return FR_MKFS_ABORTED; + + /* Create partition table if required */ + if (sfd) { /* No patition table (SFD) */ + md = 0xF0; + } else { /* With patition table (FDISK) */ + DWORD n_disk = b_vol + n_vol; + + mem_set(fs->win, 0, SS(fs)); + 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); /* CHS saturated */ + } + tbl[5] = 254; + if (fmt != FS_FAT32) /* System ID */ + tbl[4] = (n_vol < 0x10000) ? 0x04 : 0x06; + else + tbl[4] = 0x0c; + ST_DWORD(tbl+8, 63); /* Partition start in LBA */ + ST_DWORD(tbl+12, n_vol); /* Partition size in LBA */ + ST_WORD(fs->win+BS_55AA, 0xAA55); /* MBR signature */ + if (disk_write(drv, fs->win, 0, 1) != RES_OK) /* Put the MBR into first physical sector */ + return FR_DISK_ERR; + md = 0xF8; + } + + /* Create volume boot record */ + tbl = fs->win; /* Clear sector */ + mem_set(tbl, 0, SS(fs)); + mem_cpy(tbl, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code, OEM name */ + i = SS(fs); /* Sector size */ + ST_WORD(tbl+BPB_BytsPerSec, i); + tbl[BPB_SecPerClus] = (BYTE)au; /* Sectors per cluster */ + ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv); /* Reserved sectors */ + tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */ + i = (fmt == FS_FAT32) ? 0 : N_ROOTDIR; /* Number of rootdir entries */ + ST_WORD(tbl+BPB_RootEntCnt, i); + if (n_vol < 0x10000) { /* Number of total sectors */ + ST_WORD(tbl+BPB_TotSec16, n_vol); + } else { + ST_DWORD(tbl+BPB_TotSec32, n_vol); + } + tbl[BPB_Media] = md; /* Media descriptor */ + 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_vol); /* Hidden sectors */ + n = get_fattime(); /* Use current time as VSN */ + if (fmt == FS_FAT32) { + ST_DWORD(tbl+BS_VolID32, n); /* VSN */ + ST_DWORD(tbl+BPB_FATSz32, n_fat); /* Number of sectors per FAT */ + ST_DWORD(tbl+BPB_RootClus, 2); /* Root directory start cluster (2) */ + ST_WORD(tbl+BPB_FSInfo, 1); /* FSInfo record offset (VBR+1) */ + ST_WORD(tbl+BPB_BkBootSec, 6); /* Backup boot record offset (VBR+6) */ + tbl[BS_DrvNum32] = 0x80; /* Drive number */ + tbl[BS_BootSig32] = 0x29; /* Extended boot signature */ + mem_cpy(tbl+BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ + } else { + ST_DWORD(tbl+BS_VolID, n); /* VSN */ + ST_WORD(tbl+BPB_FATSz16, n_fat); /* Number of sectors 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 label, FAT signature */ + } + ST_WORD(tbl+BS_55AA, 0xAA55); /* Signature (Offset is fixed here regardless of sector size) */ + if (disk_write(drv, tbl, b_vol, 1) != RES_OK) /* Write VBR */ + return FR_DISK_ERR; + if (fmt == FS_FAT32) /* Write backup VBR if needed (VBR+6) */ + disk_write(drv, tbl, b_vol + 6, 1); + + /* Initialize FAT area */ + wsect = b_fat; + for (i = 0; i < N_FATS; i++) { /* Initialize each FAT copy */ + mem_set(tbl, 0, SS(fs)); /* 1st sector of the FAT */ + n = md; /* Media descriptor byte */ + if (fmt != FS_FAT32) { + n |= (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00; + ST_DWORD(tbl+0, n); /* Reserve cluster #0-1 (FAT12/16) */ + } else { + n |= 0xFFFFFF00; + ST_DWORD(tbl+0, n); /* 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, wsect++, 1) != RES_OK) + return FR_DISK_ERR; + mem_set(tbl, 0, SS(fs)); /* Fill following FAT entries with zero */ + for (n = 1; n < n_fat; n++) { /* This loop may take a time on FAT32 volume due to many single sector writes */ + if (disk_write(drv, tbl, wsect++, 1) != RES_OK) + return FR_DISK_ERR; + } + } + + /* Initialize root directory */ + i = (fmt == FS_FAT32) ? au : n_dir; + do { + if (disk_write(drv, tbl, wsect++, 1) != RES_OK) + return FR_DISK_ERR; + } while (--i); + +#if _USE_ERASE /* Erase data area if needed */ + { + DWORD eb[2]; + + eb[0] = wsect; eb[1] = wsect + (n_clst - ((fmt == FS_FAT32) ? 1 : 0)) * au - 1; + disk_ioctl(drv, CTRL_ERASE_SECTOR, eb); + } +#endif + + /* Create FSInfo if needed */ + if (fmt == FS_FAT32) { + ST_DWORD(tbl+FSI_LeadSig, 0x41615252); + ST_DWORD(tbl+FSI_StrucSig, 0x61417272); + ST_DWORD(tbl+FSI_Free_Count, n_clst - 1); /* Number of free clusters */ + ST_DWORD(tbl+FSI_Nxt_Free, 2); /* Last allocated cluster# */ + ST_WORD(tbl+BS_55AA, 0xAA55); + disk_write(drv, tbl, b_vol + 1, 1); /* Write original (VBR+1) */ + disk_write(drv, tbl, b_vol + 7, 1); /* Write backup (VBR+7) */ + } + + return (disk_ioctl(drv, CTRL_SYNC, (void*)0) == RES_OK) ? FR_OK : FR_DISK_ERR; +} + +#endif /* _USE_MKFS && !_FS_READONLY */ + + + + +#if _USE_STRFUNC +/*-----------------------------------------------------------------------*/ +/* Get a string from the file */ +/*-----------------------------------------------------------------------*/ +TCHAR* f_gets ( + TCHAR* buff, /* Pointer to the string buffer to read */ + int len, /* Size of string buffer (characters) */ + FIL* fil /* Pointer to the file object */ +) +{ + int n = 0; + TCHAR c, *p = buff; + BYTE s[2]; + UINT rc; + + + while (n < len - 1) { /* Read bytes until buffer gets filled */ + f_read(fil, s, 1, &rc); + if (rc != 1) break; /* Break on EOF or error */ + c = s[0]; +#if _LFN_UNICODE /* Read a character in UTF-8 encoding */ + if (c >= 0x80) { + if (c < 0xC0) continue; /* Skip stray trailer */ + if (c < 0xE0) { /* Two-byte sequense */ + f_read(fil, s, 1, &rc); + if (rc != 1) break; + c = ((c & 0x1F) << 6) | (s[0] & 0x3F); + if (c < 0x80) c = '?'; + } else { + if (c < 0xF0) { /* Three-byte sequense */ + f_read(fil, s, 2, &rc); + if (rc != 2) break; + c = (c << 12) | ((s[0] & 0x3F) << 6) | (s[1] & 0x3F); + if (c < 0x800) c = '?'; + } else { /* Reject four-byte sequense */ + c = '?'; + } + } + } +#endif +#if _USE_STRFUNC >= 2 + if (c == '\r') continue; /* Strip '\r' */ +#endif + *p++ = c; + n++; + if (c == '\n') break; /* Break on EOL */ + } + *p = 0; + return n ? buff : 0; /* When no data read (eof or error), return with error. */ +} + + +#if !_FS_READONLY +#include +/*-----------------------------------------------------------------------*/ +/* Put a character to the file */ +/*-----------------------------------------------------------------------*/ +int f_putc ( + TCHAR c, /* A character to be output */ + FIL* fil /* Pointer to the file object */ +) +{ + UINT bw, btw; + BYTE s[3]; + + +#if _USE_STRFUNC >= 2 + if (c == '\n') f_putc ('\r', fil); /* LF -> CRLF conversion */ +#endif + +#if _LFN_UNICODE /* Write the character in UTF-8 encoding */ + if (c < 0x80) { /* 7-bit */ + s[0] = (BYTE)c; + btw = 1; + } else { + if (c < 0x800) { /* 11-bit */ + s[0] = (BYTE)(0xC0 | (c >> 6)); + s[1] = (BYTE)(0x80 | (c & 0x3F)); + btw = 2; + } else { /* 16-bit */ + s[0] = (BYTE)(0xE0 | (c >> 12)); + s[1] = (BYTE)(0x80 | ((c >> 6) & 0x3F)); + s[2] = (BYTE)(0x80 | (c & 0x3F)); + btw = 3; + } + } +#else /* Write the character without conversion */ + s[0] = (BYTE)c; + btw = 1; +#endif + f_write(fil, s, btw, &bw); /* Write the char to the file */ + return (bw == btw) ? 1 : EOF; /* Return the result */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a string to the file */ +/*-----------------------------------------------------------------------*/ +int f_puts ( + const TCHAR* 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 TCHAR* str, /* Pointer to the format string */ + ... /* Optional arguments... */ +) +{ + va_list arp; + BYTE f, r; + UINT i, j, w; + ULONG v; + TCHAR c, d, s[16], *p; + int 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 character */ + 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++; + } else { + if (c == '-') { /* Flag: left justified */ + f = 2; c = *str++; + } + } + while (IsDigit(c)) { /* Precision */ + w = w * 10 + c - '0'; + c = *str++; + } + if (c == 'l' || c == 'L') { /* Prefix: Size is long int */ + f |= 4; c = *str++; + } + if (!c) break; + d = c; + if (IsLower(d)) d -= 0x20; + switch (d) { /* Type is... */ + case 'S' : /* String */ + p = va_arg(arp, TCHAR*); + for (j = 0; p[j]; j++) ; + res = 0; + while (!(f & 2) && j++ < w) res += (cc = f_putc(' ', fil)); + res += (cc = f_puts(p, fil)); + while (j++ < w) res += (cc = f_putc(' ', fil)); + if (cc != EOF) cc = res; + continue; + case 'C' : /* Character */ + cc = f_putc((TCHAR)va_arg(arp, int), fil); continue; + case 'B' : /* Binary */ + r = 2; break; + case 'O' : /* Octal */ + r = 8; break; + case 'D' : /* Signed decimal */ + case 'U' : /* Unsigned decimal */ + r = 10; break; + case 'X' : /* Hexdecimal */ + r = 16; break; + default: /* Unknown type (passthrough) */ + cc = f_putc(c, fil); continue; + } + + /* Get an argument and put it in numeral */ + v = (f & 4) ? va_arg(arp, long) : ((d == 'D') ? (long)va_arg(arp, int) : va_arg(arp, unsigned int)); + if (d == 'D' && (v & 0x80000000)) { + v = 0 - v; + f |= 8; + } + i = 0; + do { + d = (TCHAR)(v % r); v /= r; + if (d > 9) d += (c == 'x') ? 0x27 : 0x07; + s[i++] = d + '0'; + } while (v && i < sizeof(s) / sizeof(s[0])); + if (f & 8) s[i++] = '-'; + j = i; d = (f & 1) ? '0' : ' '; + res = 0; + while (!(f & 2) && j++ < w) res += (cc = f_putc(d, fil)); + do res += (cc = f_putc(s[--i], fil)); while(i); + while (j++ < w) res += (cc = f_putc(' ', fil)); + if (cc != EOF) cc = res; + } + + va_end(arp); + return (cc == EOF) ? cc : res; +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_STRFUNC */ + + +#if _USE_LFN && _CODE_PAGE == 437 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP437(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, + 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, + 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, + 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, + 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; +#endif + + +#if _TBLDEF + +WCHAR ff_convert ( /* Converted code, 0 means conversion error */ + WCHAR src, /* Character code to be converted */ + UINT dir /* 0: Unicode to OEMCP, 1: OEMCP to Unicode */ +) +{ + WCHAR c; + + if (src < 0x80) { /* ASCII */ + c = src; + + } else { + if (dir) { /* OEMCP to Unicode */ + c = (src >= 0x100) ? 0 : Tbl[src - 0x80]; + + } else { /* Unicode to OEMCP */ + for (c = 0; c < 0x80; c++) { + if (src == Tbl[c]) break; + } + c = (c + 0x80) & 0xFF; + } + } + + return c; +} +#endif + diff --git a/ff.h b/ff.h new file mode 100644 index 0000000..251d261 --- /dev/null +++ b/ff.h @@ -0,0 +1,335 @@ +/*---------------------------------------------------------------------------/ +/ FatFs - FAT file system module include file R0.08b (C)ChaN, 2011 +/----------------------------------------------------------------------------/ +/ FatFs module is a generic FAT file system module for small embedded systems. +/ This is a free software that opened for education, research and commercial +/ developments under license policy of following trems. +/ +/ Copyright (C) 2011, 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 product UNDER YOUR RESPONSIBILITY. +/ * Redistributions of source code must retain the above copyright notice. +/ +/----------------------------------------------------------------------------*/ + +#ifndef _FATFS +#define _FATFS 8237 /* Revision ID */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "integer.h" /* Basic integer types */ +#include "ffconf.h" /* FatFs configuration options */ + +#if _FATFS != _FFCONF +#error Wrong configuration file (ffconf.h). +#endif + + + +/* Definitions of volume management */ + +#if _MULTI_PARTITION /* Multiple partition configuration */ +#define LD2PD(vol) (VolToPart[vol].pd) /* Get physical drive# */ +#define LD2PT(vol) (VolToPart[vol].pt) /* Get partition# */ +typedef struct { + BYTE pd; /* Physical drive# */ + BYTE pt; /* Partition # (0-3) */ +} PARTITION; +extern const PARTITION VolToPart[]; /* Volume - Physical location resolution table */ + +#else /* Single partition configuration */ +#define LD2PD(vol) (vol) /* Logical drive# is bound to the same physical drive# */ +#define LD2PT(vol) 0 /* Always mounts the 1st partition */ + +#endif + + + +/* Type of path name strings on FatFs API */ + +#if _LFN_UNICODE /* Unicode string */ +#if !_USE_LFN +#error _LFN_UNICODE must be 0 in non-LFN cfg. +#endif +#ifndef _INC_TCHAR +typedef WCHAR TCHAR; +#define _T(x) L ## x +#define _TEXT(x) L ## x +#endif + +#else /* ANSI/OEM string */ +#ifndef _INC_TCHAR +typedef char TCHAR; +#define _T(x) x +#define _TEXT(x) x +#endif + +#endif + + + +/* File system object structure (FATFS) */ + +typedef struct { + BYTE fs_type; /* FAT sub-type (0:Not mounted) */ + BYTE drv; /* Physical drive number */ + BYTE csize; /* Sectors per cluster (1,2,4...128) */ + BYTE n_fats; /* Number of FAT copies (1,2) */ + BYTE wflag; /* win[] dirty flag (1:must be written back) */ + BYTE fsi_flag; /* fsinfo dirty flag (1:must be written back) */ + WORD id; /* File system mount ID */ + WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ +#if _MAX_SS != 512 + WORD ssize; /* Bytes per sector (512,1024,2048,4096) */ +#endif +#if _FS_REENTRANT + _SYNC_t sobj; /* Identifier of sync object */ +#endif +#if !_FS_READONLY + DWORD last_clust; /* Last allocated cluster */ + DWORD free_clust; /* Number of free clusters */ + DWORD fsi_sector; /* fsinfo sector (FAT32) */ +#endif +#if _FS_RPATH + DWORD cdir; /* Current directory start cluster (0:root) */ +#endif + DWORD n_fatent; /* Number of FAT entries (= number of clusters + 2) */ + DWORD fsize; /* Sectors per FAT */ + DWORD fatbase; /* FAT start sector */ + DWORD dirbase; /* Root directory start sector (FAT32:Cluster#) */ + DWORD database; /* Data start sector */ + DWORD winsect; /* Current sector appearing in the win[] */ + BYTE win[_MAX_SS]; /* Disk access window for Directory, FAT (and Data on tiny cfg) */ +} FATFS; + + + +/* File object structure (FIL) */ + +typedef struct { + FATFS* fs; /* Pointer to the owner file system object */ + WORD id; /* Owner file system mount ID */ + BYTE flag; /* File status flags */ + BYTE pad1; + DWORD fptr; /* File read/write pointer (0 on file open) */ + DWORD fsize; /* File size */ + DWORD sclust; /* File start cluster (0 when fsize==0) */ + DWORD 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 _USE_FASTSEEK + DWORD* cltbl; /* Pointer to the cluster link map table (null on file open) */ +#endif +#if _FS_SHARE + UINT lockid; /* File lock ID (index of file semaphore table) */ +#endif +#if !_FS_TINY + BYTE buf[_MAX_SS]; /* File data read/write buffer */ +#endif +} FIL; + + + +/* Directory object structure (DIR) */ + +typedef struct { + FATFS* fs; /* Pointer to the owner file system object */ + WORD id; /* Owner file system mount ID */ + WORD index; /* Current read/write index number */ + DWORD sclust; /* Table start cluster (0:Root dir) */ + 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 number (0xFFFF:No LFN) */ +#endif +} DIR; + + + +/* File status structure (FILINFO) */ + +typedef struct { + DWORD fsize; /* File size */ + WORD fdate; /* Last modified date */ + WORD ftime; /* Last modified time */ + BYTE fattrib; /* Attribute */ + TCHAR fname[13]; /* Short file name (8.3 format) */ +#if _USE_LFN + TCHAR* lfname; /* Pointer to the LFN buffer */ + UINT lfsize; /* Size of LFN buffer in TCHAR */ +#endif +} FILINFO; + + + +/* File function return code (FRESULT) */ + +typedef enum { + FR_OK = 0, /* (0) Succeeded */ + FR_DISK_ERR, /* (1) A hard error occured in the low level disk I/O layer */ + FR_INT_ERR, /* (2) Assertion failed */ + FR_NOT_READY, /* (3) The physical drive cannot work */ + FR_NO_FILE, /* (4) Could not find the file */ + FR_NO_PATH, /* (5) Could not find the path */ + FR_INVALID_NAME, /* (6) The path name format is invalid */ + FR_DENIED, /* (7) Acces denied due to prohibited access or directory full */ + FR_EXIST, /* (8) Acces denied due to prohibited access */ + FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */ + FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */ + FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */ + FR_NOT_ENABLED, /* (12) The volume has no work area */ + FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume on the physical drive */ + FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any parameter error */ + FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */ + FR_LOCKED, /* (16) The operation is rejected according to the file shareing policy */ + FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */ + FR_TOO_MANY_OPEN_FILES /* (18) Number of open files > _FS_SHARE */ +} FRESULT; + + + +/*--------------------------------------------------------------*/ +/* FatFs module application interface */ + +FRESULT f_mount (BYTE, FATFS*); /* Mount/Unmount a logical drive */ +FRESULT f_open (FIL*, const TCHAR*, BYTE); /* Open or create a file */ +FRESULT f_read (FIL*, void*, UINT, UINT*); /* Read data from 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 TCHAR*); /* Open an existing directory */ +FRESULT f_readdir (DIR*, FILINFO*); /* Read a directory item */ +FRESULT f_stat (const TCHAR*, FILINFO*); /* Get file status */ +FRESULT f_write (FIL*, const void*, UINT, UINT*); /* Write data to a file */ +FRESULT f_getfree (const TCHAR*, 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 TCHAR*); /* Delete an existing file or directory */ +FRESULT f_mkdir (const TCHAR*); /* Create a new directory */ +FRESULT f_chmod (const TCHAR*, BYTE, BYTE); /* Change attriburte of the file/dir */ +FRESULT f_utime (const TCHAR*, const FILINFO*); /* Change timestamp of the file/dir */ +FRESULT f_rename (const TCHAR*, const TCHAR*); /* 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, UINT); /* Create a file system on the drive */ +FRESULT f_chdrive (BYTE); /* Change current drive */ +FRESULT f_chdir (const TCHAR*); /* Change current directory */ +FRESULT f_getcwd (TCHAR*, UINT); /* Get current directory */ +int f_putc (TCHAR, FIL*); /* Put a character to the file */ +int f_puts (const TCHAR*, FIL*); /* Put a string to the file */ +int f_printf (FIL*, const TCHAR*, ...); /* Put a formatted string to the file */ +TCHAR* f_gets (TCHAR*, int, FIL*); /* Get a string from the file */ + +#ifndef EOF +#define EOF (-1) +#endif + +#define f_eof(fp) (((fp)->fptr == (fp)->fsize) ? 1 : 0) +#define f_error(fp) (((fp)->flag & FA__ERROR) ? 1 : 0) +#define f_tell(fp) ((fp)->fptr) +#define f_size(fp) ((fp)->fsize) + + + + +/*--------------------------------------------------------------*/ +/* Additional user defined functions */ + +/* RTC function */ +#if !_FS_READONLY +DWORD get_fattime (void); +#endif + +/* Unicode support functions */ +#if _USE_LFN /* Unicode - OEM code conversion */ +WCHAR ff_convert (WCHAR, UINT); /* OEM-Unicode bidirectional conversion */ +WCHAR ff_wtoupper (WCHAR); /* Unicode upper-case conversion */ +#if _USE_LFN == 3 /* Memory functions */ +void* ff_memalloc (UINT); /* Allocate memory block */ +void ff_memfree (void*); /* Free memory block */ +#endif +#endif + +/* Sync functions */ +#if _FS_REENTRANT +int ff_cre_syncobj (BYTE, _SYNC_t*);/* Create a sync object */ +int ff_req_grant (_SYNC_t); /* Lock sync object */ +void ff_rel_grant (_SYNC_t); /* Unlock sync object */ +int ff_del_syncobj (_SYNC_t); /* Delete a sync object */ +#endif + + + + +/*--------------------------------------------------------------*/ +/* Flags and offset address */ + + +/* File access control and file status flags (FIL.flag) */ + +#define FA_READ 0x01 +#define FA_OPEN_EXISTING 0x00 +#define FA__ERROR 0x80 + +#if !_FS_READONLY +#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 + + +/* 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 */ + + +/* Fast seek function */ +#define CREATE_LINKMAP 0xFFFFFFFF + + + +/*--------------------------------*/ +/* 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 + +#ifdef __cplusplus +} +#endif + +#endif /* _FATFS */ diff --git a/ffconf.h b/ffconf.h new file mode 100644 index 0000000..3c0c787 --- /dev/null +++ b/ffconf.h @@ -0,0 +1,189 @@ +/*---------------------------------------------------------------------------/ +/ FatFs - FAT file system module configuration file R0.08b (C)ChaN, 2011 +/----------------------------------------------------------------------------/ +/ +/ CAUTION! Do not forget to make clean the project after any changes to +/ the configuration options. +/ +/----------------------------------------------------------------------------*/ +#ifndef _FFCONF +#define _FFCONF 8237 /* Revision ID */ + + +/*---------------------------------------------------------------------------/ +/ Function and Buffer Configurations +/----------------------------------------------------------------------------*/ + +#define _FS_TINY 0 /* 0:Normal or 1:Tiny */ +/* 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 _FS_READONLY 0 /* 0:Read/Write or 1:Read only */ +/* 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 /* 0 to 3 */ +/* 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 1. +/ 3: f_lseek is removed in addition to 2. */ + + +#define _USE_STRFUNC 0 /* 0:Disable or 1/2:Enable */ +/* To enable string functions, set _USE_STRFUNC to 1 or 2. */ + + +#define _USE_MKFS 0 /* 0:Disable or 1:Enable */ +/* To enable f_mkfs function, set _USE_MKFS to 1 and set _FS_READONLY to 0 */ + + +#define _USE_FORWARD 0 /* 0:Disable or 1:Enable */ +/* To enable f_forward function, set _USE_FORWARD to 1 and set _FS_TINY to 1. */ + + +#define _USE_FASTSEEK 1 /* 0:Disable or 1:Enable */ +/* To enable fast seek feature, set _USE_FASTSEEK to 1. */ + + + +/*---------------------------------------------------------------------------/ +/ Locale and Namespace Configurations +/----------------------------------------------------------------------------*/ + +#define _CODE_PAGE 437 +/* The _CODE_PAGE specifies the OEM code page to be used on the target system. +/ Incorrect setting of the code page can cause a file open failure. +/ +/ 932 - Japanese Shift-JIS (DBCS, OEM, Windows) +/ 936 - Simplified Chinese GBK (DBCS, OEM, Windows) +/ 949 - Korean (DBCS, OEM, Windows) +/ 950 - Traditional Chinese Big5 (DBCS, OEM, Windows) +/ 1250 - Central Europe (Windows) +/ 1251 - Cyrillic (Windows) +/ 1252 - Latin 1 (Windows) +/ 1253 - Greek (Windows) +/ 1254 - Turkish (Windows) +/ 1255 - Hebrew (Windows) +/ 1256 - Arabic (Windows) +/ 1257 - Baltic (Windows) +/ 1258 - Vietnam (OEM, Windows) +/ 437 - U.S. (OEM) +/ 720 - Arabic (OEM) +/ 737 - Greek (OEM) +/ 775 - Baltic (OEM) +/ 850 - Multilingual Latin 1 (OEM) +/ 858 - Multilingual Latin 1 + Euro (OEM) +/ 852 - Latin 2 (OEM) +/ 855 - Cyrillic (OEM) +/ 866 - Russian (OEM) +/ 857 - Turkish (OEM) +/ 862 - Hebrew (OEM) +/ 874 - Thai (OEM, Windows) +/ 1 - ASCII only (Valid for non LFN cfg.) +*/ + + +#define _USE_LFN 1 /* 0 to 3 */ +#define _MAX_LFN 255 /* Maximum LFN length to handle (12 to 255) */ +/* The _USE_LFN option switches the LFN support. +/ +/ 0: Disable LFN feature. _MAX_LFN and _LFN_UNICODE have no effect. +/ 1: Enable LFN with static working buffer on the BSS. Always NOT reentrant. +/ 2: Enable LFN with dynamic working buffer on the STACK. +/ 3: Enable LFN with dynamic working buffer on the HEAP. +/ +/ The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes. To enable LFN, +/ Unicode handling functions ff_convert() and ff_wtoupper() must be added +/ to the project. When enable to use heap, memory control functions +/ ff_memalloc() and ff_memfree() must be added to the project. */ + + +#define _LFN_UNICODE 0 /* 0:ANSI/OEM or 1:Unicode */ +/* To switch the character code set on FatFs API to Unicode, +/ enable LFN feature and set _LFN_UNICODE to 1. */ + + +#define _FS_RPATH 1 /* 0 to 2 */ +/* The _FS_RPATH option configures relative path feature. +/ +/ 0: Disable relative path feature and remove related functions. +/ 1: Enable relative path. f_chdrive() and f_chdir() are available. +/ 2: f_getcwd() is available in addition to 1. +/ +/ Note that output of the f_readdir fnction is affected by this option. */ + + + +/*---------------------------------------------------------------------------/ +/ Physical Drive Configurations +/----------------------------------------------------------------------------*/ + +#define _VOLUMES 1 +/* Number of volumes (logical drives) to be used. */ + + +#define _MAX_SS 512 /* 512, 1024, 2048 or 4096 */ +/* Maximum sector size to be handled. +/ Always set 512 for memory card and hard disk but a larger value may be +/ required for on-board flash memory, floppy disk and optical disk. +/ When _MAX_SS is larger than 512, it configures FatFs to variable sector size +/ and GET_SECTOR_SIZE command must be implememted to the disk_ioctl function. */ + + +#define _MULTI_PARTITION 0 /* 0:Single partition or 1:Multiple partition */ +/* When set to 0, each volume is bound to the same physical drive number and +/ it can mount only first primaly partition. When it is set to 1, each volume +/ is tied to the partitions listed in VolToPart[]. */ + + +#define _USE_ERASE 0 /* 0:Disable or 1:Enable */ +/* To enable sector erase feature, set _USE_ERASE to 1. CTRL_ERASE_SECTOR command +/ should be added to the disk_ioctl functio. */ + + + +/*---------------------------------------------------------------------------/ +/ System Configurations +/----------------------------------------------------------------------------*/ + +#define _WORD_ACCESS 0 /* 0 or 1 */ +/* Set 0 first and it is always compatible with all platforms. The _WORD_ACCESS +/ option defines which access method is used to the word data on the FAT volume. +/ +/ 0: Byte-by-byte access. +/ 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 size. */ + + +/* A header file that defines sync object types on the O/S, such as +/ windows.h, ucos_ii.h and semphr.h, must be included prior to ff.h. */ + +#define _FS_REENTRANT 0 /* 0:Disable or 1:Enable */ +#define _FS_TIMEOUT 1000 /* Timeout period in unit of time ticks */ +#define _SYNC_t HANDLE /* O/S dependent type of sync object. e.g. HANDLE, OS_EVENT*, ID and etc.. */ + +/* The _FS_REENTRANT option switches the reentrancy (thread safe) of the FatFs module. +/ +/ 0: Disable reentrancy. _SYNC_t and _FS_TIMEOUT have no effect. +/ 1: Enable reentrancy. Also user provided synchronization handlers, +/ ff_req_grant, ff_rel_grant, ff_del_syncobj and ff_cre_syncobj +/ function must be added to the project. */ + + +#define _FS_SHARE 0 /* 0:Disable or >=1:Enable */ +/* To enable file shareing feature, set _FS_SHARE to 1 or greater. The value + defines how many files can be opened simultaneously. */ + + +#endif /* _FFCONFIG */ diff --git a/global.h b/global.h new file mode 100644 index 0000000..574fa4e --- /dev/null +++ b/global.h @@ -0,0 +1,62 @@ +#ifndef __GLOBAL_H__ +#define __GLOBAL_H__ + +#define UINT_MAX ((unsigned int)0xffffffff) + +#define CARD_DEBUG 1 + +#define CHEATHOOK 1 +//#define DEBUGGER 1 +//#define DEBUGGERWAIT 1 +//#define ACTIVITYLED 1 +//#define CARDMODE 1 +//#define CARDDEBUG 1 +#define REALNAND 1 +#define PADHOOK 1 + +#define CONFIG_VERSION 0x00000001 +#define DM_VERSION 0x00020000 + +#define DI_SUCCESS 1 +#define DI_ERROR 2 +#define DI_FATAL 64 + +//#define DEBUG 0 +#define false 0 +#define true 1 + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; + +typedef int bool; +typedef unsigned int sec_t; + +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; +typedef signed long long s64; + +typedef volatile unsigned char vu8; +typedef volatile unsigned short vu16; +typedef volatile unsigned int vu32; +typedef volatile unsigned long long vu64; + +typedef volatile signed char vs8; +typedef volatile signed short vs16; +typedef volatile signed int vs32; +typedef volatile signed long long vs64; + +typedef s32 size_t; + +typedef u32 u_int32_t; + +typedef s32(*ipccallback)(s32 result,void *usrdata); + +#define NULL ((void *)0) + +#define ALIGNED(x) __attribute__((aligned(x))) + +#endif + diff --git a/hollywood.h b/hollywood.h new file mode 100644 index 0000000..8244db6 --- /dev/null +++ b/hollywood.h @@ -0,0 +1,180 @@ +#ifndef __HOLLYWOOD_H__ +#define __HOLLYWOOD_H__ + +/* Hollywood Registers */ + +#define HW_PPC_REG_BASE 0xd000000 +#define HW_REG_BASE 0xd800000 + +#define IPC_CTRL_Y1 0x01 +#define IPC_CTRL_X2 0x02 +#define IPC_CTRL_X1 0x04 +#define IPC_CTRL_Y2 0x08 + +#define IPC_CTRL_IX1 0x10 +#define IPC_CTRL_IX2 0x20 + +// Our definitions for this IPC interface +#define IPC_CTRL_OUT IPC_CTRL_Y1 +#define IPC_CTRL_IN IPC_CTRL_X1 +#define IPC_CTRL_IRQ_IN IPC_CTRL_IX1 + +// reset both flags (X* for ARM and Y* for PPC) +#define IPC_CTRL_RESET 0x06 + +// The PPC can only see the first three IPC registers +#define HW_IPC_PPCMSG (HW_REG_BASE + 0x000) +#define HW_IPC_PPCCTRL (HW_REG_BASE + 0x004) +#define HW_IPC_ARMMSG (HW_REG_BASE + 0x008) +#define HW_IPC_ARMCTRL (HW_REG_BASE + 0x00c) + +#define HW_TIMER (HW_REG_BASE + 0x010) +#define HW_ALARM (HW_REG_BASE + 0x014) + +#define HW_PPCIRQFLAG (HW_REG_BASE + 0x030) +#define HW_PPCIRQMASK (HW_REG_BASE + 0x034) + +#define HW_ARMIRQFLAG (HW_REG_BASE + 0x038) +#define HW_ARMIRQMASK (HW_REG_BASE + 0x03c) + +#define HW_MEMMIRR (HW_REG_BASE + 0x060) + +// something to do with PPCBOOT +// and legacy DI it seems ?!? +#define HW_EXICTRL (HW_REG_BASE + 0x070) +#define EXICTRL_ENABLE_EXI 1 + +// PPC side of GPIO1 (Starlet can access this too) +// Output state +#define HW_GPIO1BOUT (HW_REG_BASE + 0x0c0) +// Direction (1=output) +#define HW_GPIO1BDIR (HW_REG_BASE + 0x0c4) +// Input state +#define HW_GPIO1BIN (HW_REG_BASE + 0x0c8) +// Interrupt level +#define HW_GPIO1BINTLVL (HW_REG_BASE + 0x0cc) +// Interrupt flags (write 1 to clear) +#define HW_GPIO1BINTFLAG (HW_REG_BASE + 0x0d0) +// Interrupt propagation enable +// Do these interrupts go anywhere??? +#define HW_GPIO1BINTENABLE (HW_REG_BASE + 0x0d4) +//??? seems to be a mirror of inputs at some point... power-up state? +#define HW_GPIO1BINMIR (HW_REG_BASE + 0x0d8) +// 0xFFFFFF by default, if cleared disables respective outputs. Top bits non-settable. +#define HW_GPIO1ENABLE (HW_REG_BASE + 0x0dc) + +#define HW_GPIO1_SLOT 0x000020 +#define HW_GPIO1_DEBUG 0xFF0000 +#define HW_GPIO1_DEBUG_SH 16 + +// Starlet side of GPIO1 +// Output state +#define HW_GPIO1OUT (HW_REG_BASE + 0x0e0) +// Direction (1=output) +#define HW_GPIO1DIR (HW_REG_BASE + 0x0e4) +// Input state +#define HW_GPIO1IN (HW_REG_BASE + 0x0e8) +// Interrupt level +#define HW_GPIO1INTLVL (HW_REG_BASE + 0x0ec) +// Interrupt flags (write 1 to clear) +#define HW_GPIO1INTFLAG (HW_REG_BASE + 0x0f0) +// Interrupt propagation enable (interrupts go to main interrupt 0x800) +#define HW_GPIO1INTENABLE (HW_REG_BASE + 0x0f4) +//??? seems to be a mirror of inputs at some point... power-up state? +#define HW_GPIO1INMIR (HW_REG_BASE + 0x0f8) +// Owner of each GPIO bit. If 1, GPIO1B registers assume control. If 0, GPIO1 registers assume control. +#define HW_GPIO1OWNER (HW_REG_BASE + 0x0fc) + +// ???? +#define HW_DIFLAGS (HW_REG_BASE + 0x180) +#define DIFLAGS_BOOT_CODE 0x100000 + +// maybe a GPIO??? +#define HW_CLOCKS (HW_REG_BASE + 0x190) +#define HW_RESETS (HW_REG_BASE + 0x194) + +#define HW_GPIO2OUT (HW_REG_BASE + 0x1c8) +#define HW_GPIO2DIR (HW_REG_BASE + 0x1cc) +#define HW_GPIO2IN (HW_REG_BASE + 0x1d0) + +#define HW_OTPCMD (HW_REG_BASE + 0x1ec) +#define HW_OTPDATA (HW_REG_BASE + 0x1f0) +#define HW_VERSION (HW_REG_BASE + 0x214) + +/* NAND Registers */ + +#define NAND_REG_BASE 0xd010000 + +#define NAND_CMD (NAND_REG_BASE + 0x000) +#define NAND_STATUS NAND_CMD +#define NAND_CONF (NAND_REG_BASE + 0x004) +#define NAND_ADDR0 (NAND_REG_BASE + 0x008) +#define NAND_ADDR1 (NAND_REG_BASE + 0x00c) +#define NAND_DATA (NAND_REG_BASE + 0x010) +#define NAND_ECC (NAND_REG_BASE + 0x014) +#define NAND_UNK1 (NAND_REG_BASE + 0x018) +#define NAND_UNK2 (NAND_REG_BASE + 0x01c) + +/* AES Registers */ + +#define AES_REG_BASE 0xd020000 + +#define AES_CMD (AES_REG_BASE + 0x000) +#define AES_SRC (AES_REG_BASE + 0x004) +#define AES_DEST (AES_REG_BASE + 0x008) +#define AES_KEY (AES_REG_BASE + 0x00c) +#define AES_IV (AES_REG_BASE + 0x010) + +/* SHA-1 Registers */ + +#define SHA_REG_BASE 0xd030000 + +#define SHA_CMD (SHA_REG_BASE + 0x000) +#define SHA_SRC (SHA_REG_BASE + 0x004) +#define SHA_H0 (SHA_REG_BASE + 0x008) +#define SHA_H1 (SHA_REG_BASE + 0x00c) +#define SHA_H2 (SHA_REG_BASE + 0x010) +#define SHA_H3 (SHA_REG_BASE + 0x014) +#define SHA_H4 (SHA_REG_BASE + 0x018) + +/* SD Host Controller Registers */ + +#define SDHC_REG_BASE 0xd070000 + +/* EXI Registers */ + +#define EXI_REG_BASE 0xd806800 +#define EXI0_REG_BASE (EXI_REG_BASE+0x000) +#define EXI1_REG_BASE (EXI_REG_BASE+0x014) +#define EXI2_REG_BASE (EXI_REG_BASE+0x028) + +#define EXI0_CSR (EXI0_REG_BASE+0x000) +#define EXI0_MAR (EXI0_REG_BASE+0x004) +#define EXI0_LENGTH (EXI0_REG_BASE+0x008) +#define EXI0_CR (EXI0_REG_BASE+0x00c) +#define EXI0_DATA (EXI0_REG_BASE+0x010) + +#define EXI1_CSR (EXI1_REG_BASE+0x000) +#define EXI1_MAR (EXI1_REG_BASE+0x004) +#define EXI1_LENGTH (EXI1_REG_BASE+0x008) +#define EXI1_CR (EXI1_REG_BASE+0x00c) +#define EXI1_DATA (EXI1_REG_BASE+0x010) + +#define EXI2_CSR (EXI2_REG_BASE+0x000) +#define EXI2_MAR (EXI2_REG_BASE+0x004) +#define EXI2_LENGTH (EXI2_REG_BASE+0x008) +#define EXI2_CR (EXI2_REG_BASE+0x00c) +#define EXI2_DATA (EXI2_REG_BASE+0x010) + +#define EXI_BOOT_BASE (EXI_REG_BASE+0x040) + +/* MEMORY CONTROLLER Registers */ + +#define MEM_REG_BASE 0xd8b4000 +#define MEM_PROT (MEM_REG_BASE+0x20a) +#define MEM_PROT_START (MEM_REG_BASE+0x20c) +#define MEM_PROT_END (MEM_REG_BASE+0x20e) +#define MEM_FLUSHREQ (MEM_REG_BASE+0x228) +#define MEM_FLUSHACK (MEM_REG_BASE+0x22a) + +#endif diff --git a/integer.h b/integer.h new file mode 100644 index 0000000..137b988 --- /dev/null +++ b/integer.h @@ -0,0 +1,37 @@ +/*-------------------------------------------*/ +/* Integer type definitions for FatFs module */ +/*-------------------------------------------*/ + +#ifndef _INTEGER +#define _INTEGER + +#ifdef _WIN32 /* FatFs development platform */ + +#include +#include + +#else /* Embedded platform */ + +/* 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 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; + +#endif + +#endif diff --git a/iosmodule.ld b/iosmodule.ld new file mode 100644 index 0000000..32026a6 --- /dev/null +++ b/iosmodule.ld @@ -0,0 +1,81 @@ +OUTPUT_FORMAT("elf32-bigarm") +OUTPUT_ARCH(arm) +ENTRY(_start) + +__stack_size = 0x4000; + +MEMORY +{ + sram : ORIGIN = 0xFFFF0000, LENGTH = 0x10000 + stack : ORIGIN = 0xFFFE0000, LENGTH = 0x4000 +} + +PHDRS +{ + sram PT_LOAD AT ( 0xFFFF0000 ) ; + stack PT_LOAD AT ( 0xFFFE0000 ) ; +} + +SECTIONS +{ + .init : + { + *(.init) + . = ALIGN(4); + } >sram :sram + + .text : + { + *(.text*) + *(.text.*) + *(.gnu.warning) + *(.gnu.linkonce.t*) + *(.glue_7) + *(.glue_7t) + . = ALIGN(4); + } >sram :sram + + .rodata : + { + *(.rodata) + *all.rodata*(*) + *(.roda) + *(.rodata.*) + *(.gnu.linkonce.r*) + . = ALIGN(4); + } >sram :sram + + .data : + { + *(.data) + *(.data.*) + *(.gnu.linkonce.d*) + . = ALIGN(4); + } >sram :sram + + .bss : + { + __bss_start = . ; + *(.dynbss) + *(.gnu.linkonce.b*) + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end = . ; + } >sram :sram + + + .stack : + { + __stack_end = .; + . = . +__stack_size; + . = ALIGN(4); + __stack_addr = .; + } >stack :stack + + /DISCARD/ : + { + *(.ARM.exidx*) + *(.ARM.extab*) + } +} diff --git a/ipc.h b/ipc.h new file mode 100644 index 0000000..e703c41 --- /dev/null +++ b/ipc.h @@ -0,0 +1,67 @@ +#ifndef __IPC_H__ +#define __IPC_H__ 1 + +struct ioctl_vector { + void *data; + unsigned int len; +} __attribute__((packed)); + +struct ipcmessage +{ + unsigned int command; // 0 + unsigned int result; // 4 + union + { + unsigned int fd; // 8 + }; + union + { + struct + { + char *device; // 12 + unsigned int mode; // 16 + unsigned int resultfd; // 20 + } open; + + struct + { + void *data; + unsigned int length; + } read, write; + + struct + { + int offset; + int origin; + } seek; + + struct + { + unsigned int command; + + unsigned int *buffer_in; + unsigned int length_in; + unsigned int *buffer_io; + unsigned int length_io; + } ioctl; + + struct + { + unsigned int command; // C + + unsigned int argc_in; // 10 + unsigned int argc_io; // 14 + struct ioctl_vector *argv; // 18 + } ioctlv; + }; +} __attribute__((packed)) ipcmessage; + +#define IOS_OPEN 0x01 +#define IOS_CLOSE 0x02 +#define IOS_READ 0x03 +#define IOS_WRITE 0x04 +#define IOS_SEEK 0x05 +#define IOS_IOCTL 0x06 +#define IOS_IOCTLV 0x07 + +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..114c7f6 --- /dev/null +++ b/main.c @@ -0,0 +1,299 @@ +/* + +DIOS MIOS - Gamecube USB loader for Nintendo Wii + +Copyright (C) 2010-2012 crediar + +*/ +#include "string.h" +#include "global.h" +#include "ipc.h" +#include "alloc.h" +#include "ff.h" +#include "diskio.h" +#include "dol.h" +#include "GCPad.h" +#include "HW.h" +#include "Patches.h" +#include "Config.h" +#include "Card.h" +#include "DVD.h" +#include "Drive.h" +#include "dip.h" + +char __aeabi_unwind_cpp_pr0[0]; + +void Syscall( u32 a, u32 b, u32 c, u32 d ) +{ + dbgprintf("Syscall,%d,%d,%d,%d\n", a, b, c, d); + return; +} +void SWI( u32 a, u32 b ) +{ + dbgprintf("SWI:%X,%X\n", a, b ); + return; +} +void PrefetchAbort( void ) +{ + u32 val; + __asm("mov %0,lr": "=r" (val) ); + + *(vu32*)0xD800070 |= 1; + + dbgprintf("PrefetchAbort LR:%08x\n", val-8 ); + while(1); + return; +} +void DataAbort( u32 a, u32 b, u32 c, u32 d, u32 e, u32 f, u32 g, u32 h ) +{ + u32 val; + + __asm("mov %0,lr": "=r" (val) ); + + *(vu32*)0xD800070 |= 1; + + dbgprintf("DataAbort: LR:%08x, %x, %x, %x, %x, %x, %x, %x\n",val-8,b,c,d,e,f,g,h); + Shutdown(); +} +void IRQHandler( void ) +{ + u32 IRQs = read32(HW_ARMIRQFLAG) /*& read32(HW_ARMIRQMASK)*/; + + if( IRQs & IRQ_GPIO1 ) // Starlet GPIO IRQ + { + if( read32(HW_GPIO_INTFLAG) & (1) ) + { + set32( HW_EXICTRL, 1 ); + + int i; + for( i = 0; i < 0x20; i+=4 ) + dbgprintf("0x%08X:0x%08X\t0x%08X\n", i, read32( CARD_BASE + i ), read32( CARD_SHADOW + i ) ); + dbgprintf("\n"); + for( i = 0; i < 0x30; i+=4 ) + dbgprintf("0x%08X:0x%08X\t0x%08X\n", i, read32( 0x00002F00 + i ), read32( 0x00002F30 + i ) ); + dbgprintf("\n"); + + for( i = 0; i < 0x30; i+=4 ) + dbgprintf("0x%08X:0x%08X\t0x%08X\n", 0x0D806000 + i, read32( 0x0D806000 + i ), read32( 0x0D006000 + i ) ); + + dbgprintf("DVD:Error:%08X\n", DVDLowGetError() ); + + udelay(10000); + + set32( HW_GPIO_ENABLE, GPIO_POWER ); + set32( HW_GPIO_OUT, GPIO_POWER ); + + while(1); + } + } else if( IRQs & IRQ_RESET ) + { + ; + } else { + + set32( HW_EXICTRL, 1 ); + + udelay(1000); + dbgprintf("IRQ:%08X %08X\n", read32(HW_ARMIRQFLAG), read32(HW_GPIO_INTFLAG) ); + set32( HW_EXICTRL, 0 ); + } + + return; +} +void FIQHandler( void ) +{ + //dbgprintf("FIQHandler\n"); + return; +} +void DebugPoke( u8 Value ) +{ + clear32( 0xD8000E0, 0xFF0000 ); + set32( 0xD8000E0, Value<<16 ); +} +void SysReset( void ) +{ + write32( HW_RESETS, (read32( HW_RESETS ) | 0x20 ) & (~1) ); +} +void SysShutdown( void ) +{ + set32( HW_GPIO_ENABLE, GPIO_POWER ); + set32( HW_GPIO_OUT, GPIO_POWER ); + + while(1); +} + +u32 fail; +FIL Log; + +int main( int argc, char *argv[] ) +{ + udelay(800); + +#ifndef REALNAND + PPCReset(); + clear32( HW_RESETS, 0x48000 ); + clear32( 0xD800184, 0x438E ); + + ChangeClock(); + + DRAMInit(1,0); + + set32( HW_RESETS, 0x48000 ); + set32( 0xD800184, 0x438E ); + + UNKInit( 1, 1 ); +#endif + + set32( 0xD800038, IRQ_RESET|IRQ_GPIO1 ); + set32( 0xD80003C, IRQ_RESET|IRQ_GPIO1 ); + udelay(200); + + u32 SP[2]; + GetRevision( SP+1, SP ); + if( SP[1] == 0 ) + { + write32( HW_MEMIRR, 0x67 ); + } else { + write32( HW_MEMIRR, 0x27 ); + } + + MIOSInit(); + +#ifdef DEBUG + dbgprintf("DIOS-MIOS [DEBUG] v%d.%d\n", DM_VERSION>>16, DM_VERSION & 0xFFFF ); +#else +#ifdef REALNAND + dbgprintf("DIOS-MIOS v%d.%db\n", DM_VERSION>>16, DM_VERSION & 0xFFFF ); +#else + dbgprintf("DIOS-MIOS v%d.%da\n", DM_VERSION>>16, DM_VERSION & 0xFFFF ); +#endif +#endif + dbgprintf("Built: " __DATE__ " " __TIME__ "\n"); + dbgprintf("This software is licensed under GPLv3, for more details visit:\nhttp://code.google.com/p/diosmioslite\n"); + + //dbgprintf("CPU Ver:%d.%d\n", SP[1], SP[0] ); + + //dbgprintf("MEMInitLow()...\n"); + MEMInitLow(); + +// EHCI + + *(vu32*)0x0D0400A4 = 0x00004026; + *(vu32*)0x0D0400B0 = 0x0002422E; + *(vu32*)0x0D0400B4 = 0x03802E14; + +// DDR control + + *(vu16*)0x0D8B4034 = 0x0000; + *(vu16*)0x0D8B403C = 0x0000; + *(vu16*)0x0D8B4034 = 0x0000; + *(vu16*)0x0D8B403C = 0x0000; + *(vu16*)0x0D8B4040 = 0x0000; + *(vu16*)0x0D8B4044 = 0x0000; + *(vu16*)0x0D8B4048 = 0x0000; + *(vu16*)0x0D8B404C = 0x0000; + *(vu16*)0x0D8B4050 = 0x0000; + *(vu16*)0x0D8B4054 = 0x13EB; + *(vu16*)0x0D8B4058 = 0x09B5; + *(vu16*)0x0D8B4060 = 0x0000; + *(vu16*)0x0D8B4064 = 0x0000; + *(vu16*)0x0D8B420C = 0x3620; + *(vu16*)0x0D8B4220 = 0xF000; + + udelay(8000); + + HeapInit( (u8*)0x13600000 ); + + set32( HW_EXICTRL, 1 ); + + DVDInit(); + + ConfigInit( (DML_CFG*)0x01200000 ); + + if( !ConfigGetConfig(DML_CFG_BOOT_DISC) ) + { + if( DVDSelectGame() == DI_SUCCESS ) + { + if( ConfigGetConfig(DML_CFG_NMM) ) + CardInit(); + } else { + dbgprintf("Loading disc\n"); + + ((DML_CFG*)0x01200000)->Version = CONFIG_VERSION; + ((DML_CFG*)0x01200000)->Magicbytes = 0xD1050CF6; + ((DML_CFG*)0x01200000)->VideoMode = DML_VID_DML_AUTO; + ((DML_CFG*)0x01200000)->Config = DML_CFG_BOOT_DISC; + } + } + + DIInit(); + +//Switch mem2 to ARAM + DRAMCTRLWrite( 0x49, 0x0E ); + udelay(2); + + DRAMCTRLWrite( 0x49, 0x0F ); + udelay(2); + + HeapInit( (u8*)0xFFFE5000 ); + + write32( HW_PPCIRQFLAG, read32(HW_PPCIRQFLAG) ); + write32( HW_ARMIRQFLAG, read32(HW_ARMIRQFLAG) ); + + set32( HW_PPCIRQMASK, (1<<31) ); + set32( HW_IPC_PPCCTRL, 0x30 ); + + write32( 0x0D806008, 0 ); + + EXIControl(0); + + write32( 0x1860, 0xdeadbeef ); // Clear OSReport area + write32( 0x30F8, 0 ); // Tell PPC side to start + + ahb_flush_to( AHB_PPC ); + + while (1) + { + ahb_flush_from( AHB_STARLET ); //flush to arm + + if( (((read32(0x12FC) >> 16) & 0x1030) == 0x1030 ) ) + { + SysReset(); + } + if( (((read32(0x12FC) >> 16) & 0x234) == 0x234 ) ) + { + SysShutdown(); + } + + //Baten Kaitos save hax + if( read32(0) == 0x474B4245 ) + { + if( read32( 0x0073E640 ) == 0xFFFFFFFF ) + { + write32( 0x0073E640, 0 ); + } + } + + //if( read32(0x1860) != 0xdeadbeef ) + //{ + // if( read32(0x1860) != 0 ) + // { + // dbgprintf( (char*)(P2C(read32(0x1860))), + // (char*)(P2C(read32(0x1864))), + // (char*)(P2C(read32(0x1868))), + // (char*)(P2C(read32(0x186C))), + // (char*)(P2C(read32(0x1870))), + // (char*)(P2C(read32(0x1864))) + // ); + // } + + // write32(0x1860, 0xdeadbeef); + //} + + DIUpdateRegisters(); + + if( ConfigGetConfig(DML_CFG_NMM) ) + CARDUpdateRegisters(); + + ahb_flush_to( AHB_PPC ); //flush to ppc + } +} diff --git a/memory.c b/memory.c new file mode 100644 index 0000000..86e29fe --- /dev/null +++ b/memory.c @@ -0,0 +1,251 @@ +#include "memory.h" + +void _dc_inval_entries(void *start, int count); +void _dc_flush_entries(const void *start, int count); +void _dc_flush(void); +void _ic_inval(void); +void _drain_write_buffer(void); +u32 irq_kill(void); +void irq_restore(u32 cookie); + +#define LINESIZE 0x20 +#define CACHESIZE 0x4000 + + +#define CR_MMU (1 << 0) +#define CR_DCACHE (1 << 2) +#define CR_ICACHE (1 << 12) + +// TODO: move to hollywood.h once we figure out WTF +#define HW_100 (HW_REG_BASE + 0x100) +#define HW_104 (HW_REG_BASE + 0x104) +#define HW_108 (HW_REG_BASE + 0x108) +#define HW_10c (HW_REG_BASE + 0x10c) +#define HW_110 (HW_REG_BASE + 0x110) +#define HW_114 (HW_REG_BASE + 0x114) +#define HW_118 (HW_REG_BASE + 0x118) +#define HW_11c (HW_REG_BASE + 0x11c) +#define HW_120 (HW_REG_BASE + 0x120) +#define HW_124 (HW_REG_BASE + 0x124) +#define HW_130 (HW_REG_BASE + 0x130) +#define HW_134 (HW_REG_BASE + 0x134) +#define HW_138 (HW_REG_BASE + 0x138) +#define HW_188 (HW_REG_BASE + 0x188) +#define HW_18C (HW_REG_BASE + 0x18c) + +// what is this thing doing anyway? +// and why only on reads? +u32 _mc_read32(u32 addr) +{ + u32 data; + u32 tmp130 = 0; + // this seems to be a bug workaround + if(!(read32(HW_VERSION) & 0xF0)) + { + tmp130 = read32(HW_130); + write32(HW_130, tmp130 | 0x400); + // Dummy reads? + read32(HW_138); + read32(HW_138); + read32(HW_138); + read32(HW_138); + } + data = read32(addr); + read32(HW_VERSION); //??? + + if(!(read32(HW_VERSION) & 0xF0)) + write32(HW_130, tmp130); + + return data; +} + +// this is ripped from IOS, because no one can figure out just WTF this thing is doing +void _ahb_flush_to(enum AHBDEV dev) { + u32 mask = 10; + switch(dev) { + case AHB_STARLET: mask = 0x8000; break; + case AHB_PPC: mask = 0x4000; break; + //case 2: mask = 0x0001; break; + case AHB_NAND: mask = 0x0002; break; + case AHB_AES: mask = 0x0004; break; + case AHB_SHA1: mask = 0x0008; break; + case AHB_EHCI: mask = 0x0010; break; + //case 7: mask = 0x0020; break; + //case 8: mask = 0x0040; break; + case AHB_SDHC: mask = 0x0080; break; + //case 10: mask = 0x0100; break; + //case 11: mask = 0x1000; break; + //case 12: mask = 0x0000; break; + default: + dbgprintf("ahb_invalidate(%d): Invalid device\n", dev); + return; + } + //NOTE: 0xd8b000x, not 0xd8b400x! + u32 val = _mc_read32(0xd8b0008); + if(!(val & mask)) { + switch(dev) { + // 2 to 10 in IOS, add more + case AHB_NAND: + case AHB_AES: + case AHB_SHA1: + case AHB_EHCI: + case AHB_SDHC: + while((read32(HW_18C) & 0xF) == 9) + set32(HW_188, 0x10000); + clear32(HW_188, 0x10000); + set32(HW_188, 0x2000000); + mask32(HW_124, 0x7c0, 0x280); + set32(HW_134, 0x400); + while((read32(HW_18C) & 0xF) != 9); + set32(HW_100, 0x400); + set32(HW_104, 0x400); + set32(HW_108, 0x400); + set32(HW_10c, 0x400); + set32(HW_110, 0x400); + set32(HW_114, 0x400); + set32(HW_118, 0x400); + set32(HW_11c, 0x400); + set32(HW_120, 0x400); + write32(0xd8b0008, _mc_read32(0xd8b0008) & (~mask)); + write32(0xd8b0008, _mc_read32(0xd8b0008) | mask); + clear32(HW_134, 0x400); + clear32(HW_100, 0x400); + clear32(HW_104, 0x400); + clear32(HW_108, 0x400); + clear32(HW_10c, 0x400); + clear32(HW_110, 0x400); + clear32(HW_114, 0x400); + clear32(HW_118, 0x400); + clear32(HW_11c, 0x400); + clear32(HW_120, 0x400); + clear32(HW_188, 0x2000000); + mask32(HW_124, 0x7c0, 0xc0); + //0, 1, 11 in IOS, add more + case AHB_STARLET: + case AHB_PPC: + write32(0xd8b0008, val & (~mask)); + // wtfux + write32(0xd8b0008, val | mask); + write32(0xd8b0008, val | mask); + write32(0xd8b0008, val | mask); + } + } +} + +// invalidate device and then starlet +void ahb_flush_to(enum AHBDEV type) +{ + u32 cookie = irq_kill(); + _ahb_flush_to(type); + if(type != AHB_STARLET) + _ahb_flush_to(AHB_STARLET); + + irq_restore(cookie); +} + +// flush device and also invalidate memory +void ahb_flush_from(enum AHBDEV dev) +{ + u32 cookie = irq_kill(); + u16 req = 0; + u16 ack; + int i; + + switch(dev) + { + case AHB_STARLET: + case AHB_PPC: + req = 1; + break; + case AHB_AES: + case AHB_SHA1: + req = 2; + break; + case AHB_NAND: + case AHB_SDHC: + req = 8; + break; + case AHB_EHCI: + req = 4; + break; + default: + dbgprintf("ahb_flush(%d): Invalid device\n", dev); + goto done; + } + + write16(MEM_FLUSHREQ, req); + + for(i=0;i<1000000;i++) { + ack = read16(MEM_FLUSHACK); + _ahb_flush_to(AHB_STARLET); + if(ack == req) + break; + } + write16(MEM_FLUSHREQ, 0); + if(i>=1000000) { + dbgprintf("ahb_flush(%d): Flush (0x%x) did not ack!\n", dev, req); + } +done: + irq_restore(cookie); +} + +void dc_flushrange(const void *start, u32 size) +{ + u32 cookie = irq_kill(); + if(size > 0x4000) { + _dc_flush(); + } else { + void *end = ALIGN_FORWARD(((u8*)start) + size, LINESIZE); + start = ALIGN_BACKWARD(start, LINESIZE); + _dc_flush_entries(start, (end - start) / LINESIZE); + } + _drain_write_buffer(); + ahb_flush_from(AHB_PPC); + irq_restore(cookie); +} + +void dc_invalidaterange(void *start, u32 size) +{ + u32 cookie = irq_kill(); + void *end = ALIGN_FORWARD(((u8*)start) + size, LINESIZE); + start = ALIGN_BACKWARD(start, LINESIZE); + _dc_inval_entries(start, (end - start) / LINESIZE); + ahb_flush_to(AHB_STARLET); + irq_restore(cookie); +} + +void dc_flushall(void) +{ + u32 cookie = irq_kill(); + _dc_flush(); + _drain_write_buffer(); + ahb_flush_from(AHB_PPC); + irq_restore(cookie); +} + +void ic_invalidateall(void) +{ + u32 cookie = irq_kill(); + _ic_inval(); + ahb_flush_to(AHB_STARLET); + irq_restore(cookie); +} + +u32 dma_addr(void *p) +{ + u32 addr = (u32)p; + + switch(addr>>20) { + case 0xfff: + case 0x0d4: + case 0x0dc: + if(read32(HW_MEMMIRR) & 0x20) { + addr ^= 0x10000; + } + addr &= 0x0001FFFF; + addr |= 0x0d400000; + break; + } + dbgprintf("DMA to %p: address %08x\n", p, addr); + return addr; +} diff --git a/memory.h b/memory.h new file mode 100644 index 0000000..c645c22 --- /dev/null +++ b/memory.h @@ -0,0 +1,108 @@ +#ifndef __MEMORY_H__ +#define __MEMORY_H__ + +#include "string.h" +#include "global.h" +#include "ipc.h" +#include "utils.h" +#include "hollywood.h" +#include "vsprintf.h" + +#define ALIGN_FORWARD(x,align) \ + ((typeof(x))((((u32)(x)) + (align) - 1) & (~(align-1)))) + +#define ALIGN_BACKWARD(x,align) \ + ((typeof(x))(((u32)(x)) & (~(align-1)))) + +enum AHBDEV { + AHB_STARLET = 0, //or MEM2 or some controller or bus or ?? + AHB_PPC = 1, //ppc or something else??? + AHB_NAND = 3, + AHB_AES = 4, + AHB_SHA1 = 5, + AHB_EHCI = 6, + AHB_SDHC = 9, +}; + +void dc_flushrange(const void *start, u32 size); +void dc_invalidaterange(void *start, u32 size); +void dc_flushall(void); +void ic_invalidateall(void); +void ahb_flush_from(enum AHBDEV dev); +void ahb_flush_to(enum AHBDEV dev); +u32 dma_addr(void *p); + +static inline u32 get_cr(void) +{ + u32 data; + __asm__ volatile ( "mrc\tp15, 0, %0, c1, c0, 0" : "=r" (data) ); + return data; +} + +static inline u32 get_ttbr(void) +{ + u32 data; + __asm__ volatile ( "mrc\tp15, 0, %0, c2, c0, 0" : "=r" (data) ); + return data; +} + +static inline u32 get_dacr(void) +{ + u32 data; + __asm__ volatile ( "mrc\tp15, 0, %0, c3, c0, 0" : "=r" (data) ); + return data; +} + +static inline void set_cr(u32 data) +{ + __asm__ volatile ( "mcr\tp15, 0, %0, c1, c0, 0" :: "r" (data) ); +} + +static inline void set_ttbr(u32 data) +{ + __asm__ volatile ( "mcr\tp15, 0, %0, c2, c0, 0" :: "r" (data) ); +} + +static inline void set_dacr(u32 data) +{ + __asm__ volatile ( "mcr\tp15, 0, %0, c3, c0, 0" :: "r" (data) ); +} + +static inline u32 get_dfsr(void) +{ + u32 data; + __asm__ volatile ( "mrc\tp15, 0, %0, c5, c0, 0" : "=r" (data) ); + return data; +} + +static inline u32 get_ifsr(void) +{ + u32 data; + __asm__ volatile ( "mrc\tp15, 0, %0, c5, c0, 1" : "=r" (data) ); + return data; +} + +static inline u32 get_far(void) +{ + u32 data; + __asm__ volatile ( "mrc\tp15, 0, %0, c6, c0, 0" : "=r" (data) ); + return data; +} + +void _ahb_flush_to(enum AHBDEV dev); + +static inline void dc_inval_block_fast(void *block) +{ + __asm__ volatile ( "mcr\tp15, 0, %0, c7, c6, 1" :: "r" (block) ); + _ahb_flush_to(AHB_STARLET); //TODO: check if really needed and if not, remove +} + +static inline void dc_flush_block_fast(void *block) +{ + __asm__ volatile ( "mcr\tp15, 0, %0, c7, c10, 1" :: "r" (block) ); + __asm__ volatile ( "mcr\tp15, 0, %0, c7, c10, 4" :: "r" (0) ); + ahb_flush_from(AHB_PPC); //TODO: check if really needed and if not, remove +} + +#endif + diff --git a/memory_asm.S b/memory_asm.S new file mode 100644 index 0000000..dd934b2 --- /dev/null +++ b/memory_asm.S @@ -0,0 +1,70 @@ +#define CPSR_IRQDIS 0x80 +#define CPSR_FIQDIS 0x40 + +.arm + +.globl _dc_inval_entries +.globl _dc_flush_entries +.globl _dc_flush +.globl _dc_inval +.globl _ic_inval +.globl _drain_write_buffer +.globl _tlb_inval +.globl irq_kill +.globl irq_restore + +.text + +irq_kill: + mrs r1, cpsr + and r0, r1, #(CPSR_IRQDIS|CPSR_FIQDIS) + orr r1, r1, #(CPSR_IRQDIS|CPSR_FIQDIS) + msr cpsr_c, r1 + bx lr + +irq_restore: + mrs r1, cpsr + bic r1, r1, #(CPSR_IRQDIS|CPSR_FIQDIS) + orr r1, r1, r0 + msr cpsr_c, r1 + bx lr + +_dc_inval_entries: + mcr p15, 0, r0, c7, c6, 1 + add r0, #0x20 + subs r1, #1 + bne _dc_inval_entries + bx lr + +_dc_flush_entries: + mcr p15, 0, r0, c7, c10, 1 + add r0, #0x20 + subs r1, #1 + bne _dc_flush_entries + bx lr + +_dc_flush: + mrc p15, 0, pc, c7, c10, 3 + bne _dc_flush + bx lr + +_dc_inval: + mov r0, #0 + mcr p15, 0, r0, c7, c6, 0 + bx lr + +_ic_inval: + mov r0, #0 + mcr p15, 0, r0, c7, c5, 0 + bx lr + +_drain_write_buffer: + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 + bx lr + +_tlb_inval: + mov r0, #0 + mcr p15, 0, r0, c8, c7 + bx lr + diff --git a/start.s b/start.s new file mode 100644 index 0000000..573b1ac --- /dev/null +++ b/start.s @@ -0,0 +1,213 @@ +.global _start +.global start + +.global udelay + +.global RegWrite +.global RegRead + +.global DRAMWrite +.global DRAMRead + +.global DRAMCTRLRead +.global DRAMCTRLWrite + +.extern main +.extern Syscall +.extern SWI +.extern PrefetchAbort +.extern DataAbort +.extern IRQHandler +.extern FIQHandler +.arm + +.section ".init" +_start: + ldr pc, =start + ldr pc, =Syscall + ldr pc, =SWI + ldr pc, =PrefetchAbort + ldr pc, =DataAbort + ldr pc, =0 + ldr pc, =IRQHandler + movs pc, lr + +start: + mov r0, #0 + ldr r1, =0xd80003c + str r0, [r1] + + mov r0, #0 + mcr p15, 0, r0,c7,c5 + mcr p15, 0, r0,c7,c6 + + mrc p15, 0, r0,c1,c0 + bic r0, r0, #4 + bic r0, r0, #0x1000 + mcr p15, 0, r0,c1,c0 + + ldr r0,=__bss_start + ldr r1,=__bss_end + mov r2,#0 + mov r3,#4 +clearbss: + cmp r0, r1 + bcs clearbss_end + str r2, [r0], r3 + b clearbss + +clearbss_end: + + msr CPSR_c, #211 + ldr sp, =0xFFFF7E60 + msr CPSR_c, #210 + ldr sp, =0xFFFF7E60 + msr CPSR_c, #209 + ldr sp, =0xFFFF7E60 + msr CPSR_c, #215 + ldr sp, =0xFFFF7E60 + msr CPSR_c, #219 + ldr sp, =0xFFFF7E60 + msr CPSR_c, #31 + ldr sp, =0xFFFE4000 + +#enable IRQs + + mrs r1, cpsr + bic r1, r1, #0xC0 + msr cpsr_c, r1 + + mov lr, pc + ldr pc, =main + +RegWrite: + ldr r3,=0xd8b4000 + lsl r0, r0, #1 + add r0, r0, r3 + strh r1, [r0] + bx lr + +RegRead: + ldr r3,=0xd8b4000 + lsl r0, r0, #1 + add r0, r0, r3 + ldrh r0, [r0] + lsl r0, r0, #0x10 + lsr r0, r0, #0x10 + bx lr + +DRAMWrite: + push {r4,lr} + add r3, r0, #0 + add r4, r1, #0 + mov r0, #0x3a + add r1, r3, #0 + bl RegWrite + + mov r0, #0x3a + bl RegRead + + mov r0, #0x3b + add r1, r4, #0 + bl RegWrite + + pop {r4} + pop {r0} + bx r0 + +DRAMRead: + push {lr} + add r1, r0, #0 + mov r0, #0x3a + bl RegWrite + + mov r0, #0x3a + bl RegRead + + mov r0, #0x3b + bl RegRead + + pop {r1} + bx r1 + +DRAMCTRLWrite: + push {r4,r5,lr} + ldr r4, =0x163 + add r3, r0, #0 + add r5, r1, #0 + add r0, r4, #0 + add r1, r3, #0 + bl DRAMWrite + + add r0, r4, #0 + bl DRAMRead + + mov r0, #0xb1 + add r1, r5, #0 + lsl r0, r0, #1 + bl DRAMWrite + + pop {r4,r5} + pop {r0} + bx r0 + +DRAMCTRLRead: + push {r4,lr} + ldr r4, =0x163 + add r1, r0, #0 + add r0, r4, #0 + bl DRAMWrite + + add r0, r4, #0 + bl DRAMRead + + ldr r0, =0x162 + bl DRAMRead + + pop {r4} + pop {r1} + bx r1 + +udelay: + + push {lr} + + lsr r3, r0, #2 + add r3, r3, r0 + lsr r2, r0, #6 + + add r0, r3, r2 + cmp r0, #1 + bhi loc_ffff20e8 + + mov r0, #2 + +loc_ffff20e8: + ldr r1, =0xd800010 + ldr r3, [r1] + add r2, r3, r0 + cmp r2, r3 + bls loc_ffff211c + +loc_ffff20f2: + ldr r3, [r1] + cmp r3, r2 + bcc loc_ffff20f2 + +loc_ffff20f8: + pop {r0} + bx r0 + +loc_ffff211c: + add r3, r1, #0 + +loc_ffff211e: + ldr r0, [r3] + cmp r0, #0 + blt loc_ffff211e + + cmp r0, r2 + bcc loc_ffff211e + b loc_ffff20f8 + +.end \ No newline at end of file diff --git a/string.c b/string.c new file mode 100644 index 0000000..07cf81b --- /dev/null +++ b/string.c @@ -0,0 +1,125 @@ +/* + * linux/lib/string.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include "string.h" + +size_t strnlen(const char *s, size_t count) +{ + const char *sc; + + for (sc = s; count-- && *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} + +size_t strlen(const char *s) +{ + const char *sc; + + for (sc = s; *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} + +char *strncpy(char *dst, const char *src, size_t n) +{ + char *ret = dst; + + while (n && (*dst++ = *src++)) + n--; + + while (n--) + *dst++ = 0; + + return ret; +} + +char *strcpy(char *dst, const char *src) +{ + char *ret = dst; + + while ((*dst++ = *src++)) + ; + + return ret; +} + +int strcmp(const char *p, const char *q) +{ + for (;;) { + unsigned char a, b; + a = *p++; + b = *q++; + if (a == 0 || a != b) + return a - b; + } +} + +int strncmp(const char *p, const char *q, size_t n) +{ + while (n-- != 0) { + unsigned char a, b; + a = *p++; + b = *q++; + if (a == 0 || a != b) + return a - b; + } + return 0; +} +void *memset(void *dst, int x, size_t n) +{ + unsigned char *p; + + for (p = dst; n; n--) + *p++ = x; + + return dst; +} + +// +//void udelay(u32 d) +//{ +// // should be good to max .2% error +// u32 ticks = d * 19 / 10; +// +// if(ticks < 2) +// ticks = 2; +// +// u32 now = *(vu32*)0x0D800010; +// +// u32 then = now + ticks; +// +// if(then < now) { +// while(*(vu32*)0x0D800010 >= now); +// now = *(vu32*)0x0D800010; +// } +// +// while(now < then) { +// now = *(vu32*)0x0D800010; +// } +//} + +int memcmp(const void *s1, const void *s2, size_t n) +{ + unsigned char *us1 = (unsigned char *) s1; + unsigned char *us2 = (unsigned char *) s2; + while (n-- != 0) { + if (*us1 != *us2) + return (*us1 < *us2) ? -1 : +1; + us1++; + us2++; + } + return 0; +} + +char *strchr(const char *s, int c) +{ + do { + if(*s == c) + return (char *)s; + } while(*s++ != 0); + return NULL; +} diff --git a/string.h b/string.h new file mode 100644 index 0000000..2a45806 --- /dev/null +++ b/string.h @@ -0,0 +1,23 @@ +#ifndef _STRING_H_ +#define _STRING_H_ + +#include "global.h" + +char *strcpy(char *, const char *); +char *strncpy(char *, const char *, size_t); +int strcmp(const char *, const char *); +int strncmp(const char *p, const char *q, size_t n); +size_t strlen(const char *); +size_t strnlen(const char *, size_t); +char *strchr(const char *s, int c); +extern void *memset(void *, int, size_t); + +void udelay(u32 d); + +//void *memcpy(void *, const void *, size_t); +//void *memcpy(void *, const void *, size_t); +//void *memcpy(void *, const void *, size_t); +int memcmp(const void *s1, const void *s2, size_t n); +int sprintf( char *astr, const char *fmt, ...); + +#endif diff --git a/tiny_ehci_glue.c b/tiny_ehci_glue.c new file mode 100644 index 0000000..8dfb21f --- /dev/null +++ b/tiny_ehci_glue.c @@ -0,0 +1,92 @@ +/* + EHCI glue. A bit hacky for the moment. needs cleaning.. + + Copyright (C) 2008 kwiirk. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "tiny_ehci_glue.h" + +#define static +#define inline extern + +int verbose=0; + +void BUG(void) +{ + dbgprintf("bug\n"); + while(1); +} +#define BUG_ON(a) if(a)BUG() + +void msleep(int msec) +{ + udelay(2048*msec); +} +extern u32 __exe_start_virt__; +extern u32 __ram_start_virt__; + +extern u32 ios_thread_stack; + +void print_hex_dump_bytes(char *header,int prefix,u8 *buf,int len) +{ + int i; + if (len>0x100)len=0x100; + dbgprintf("%s %08X\n",header,(u32)buf); + for (i=0;icaps = (void*)0x0D040000; + ehci->regs = (void*)(0x0D040000 + HC_LENGTH(ehci_readl(&ehci->caps->hc_capbase))); + + ehci->num_port = 4; + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = ehci_readl( &ehci->caps->hcs_params ); + + /* data structure init */ + retval = ehci_init(); + if (retval) + return retval; + + ehci_release_ports(); //quickly release none usb2 port + + return 0; +} diff --git a/tiny_ehci_glue.h b/tiny_ehci_glue.h new file mode 100644 index 0000000..880c7d1 --- /dev/null +++ b/tiny_ehci_glue.h @@ -0,0 +1,25 @@ +#ifndef _TEGLUE_ +#define _TEGLUE_ + +#include "string.h" + +#include "ehci_types.h" +#include "utils.h" +#include "vsprintf.h" + +int tiny_ehci_init(void); + +#define readl(a) (*((volatile u32*)(a))) +#define writel(v,a) do{*((volatile u32*)(a))=(v);}while(0) +#define ehci_dbg(a...) dbgprintf(a) +#define printk(a...) dbgprintf(a) +#define get_timer() (*(((volatile u32*)0x0D800010))) + +#define cpu_to_le32(a) swab32(a) +#define le32_to_cpu(a) swab32(a) +#define cpu_to_le16(a) swab16(a) +#define le16_to_cpu(a) swab16(a) +#define cpu_to_be32(a) (a) +#define be32_to_cpu(a) (a) + +#endif \ No newline at end of file diff --git a/usb.c b/usb.c new file mode 100644 index 0000000..56df6cd --- /dev/null +++ b/usb.c @@ -0,0 +1,233 @@ +/* + This file implements libogc usb API, but with ehci direct call + + most of the file credit goes to libogc devs +*/ + +#define __usb_control_message(fd, b, c,d, e, f, g, h, i) ehci_control_message(fd,b,c,d,e,f,g) + +static s32 __usb_getdesc(struct ehci_device * fd, u8 *buffer, u8 type, u8 index, u8 size) +{ + //printk("usb_get desc %X %X %p\n",type,index,buffer); + return __usb_control_message(fd, USB_ENDPOINT_IN ,USB_REQ_GETDESCRIPTOR, (type << 8) | index, 0, size, buffer, NULL, NULL); +} + +s32 USB_GetDescriptors(struct ehci_device * fd, usb_devdesc *udd) +{ + u8 *buffer = NULL; + u8 *ptr = NULL; + usb_configurationdesc *ucd = NULL; + usb_interfacedesc *uid = NULL; + usb_endpointdesc *ued = NULL; + s32 retval = 0; + u32 iConf, iInterface, iEndpoint; + + buffer = USB_Alloc(sizeof(*udd)); + if(buffer == NULL) + { + retval = -ENOMEM; + goto free_and_error; + } + + retval = __usb_getdesc(fd, buffer, USB_DT_DEVICE, 0, USB_DT_DEVICE_SIZE); + if(retval < 0) + { + dbgprintf("__usb_getdesc():%d failed\n", retval ); + goto free_and_error; + } + + memcpy( udd, buffer, USB_DT_DEVICE_SIZE ); + USB_Free(buffer); + + u32 *_udd = (u32*)udd; + + _udd[1] = ( udd->bLength << 24 ) | ( udd->bDescriptorType << 16 ) | cpu_to_le16(udd->bcdUSB); + _udd[2] = ( cpu_to_le16(udd->idVendor) << 16 ) | cpu_to_le16(udd->idProduct); + _udd[3] = ( cpu_to_le16(udd->bcdDevice) << 16 ) | (udd->iManufacturer<<8) | udd->iProduct; + + u32 _ptr = (u32)USB_Alloc( udd->bNumConfigurations * sizeof(*udd->configurations) ); + + //udd->configurations = USB_Alloc(udd->bNumConfigurations* sizeof(*udd->configurations)); + if( _ptr == 0) + { + dbgprintf("USB_Alloc():failed:%u\n", __LINE__ ); + retval = -ENOMEM; + goto free_and_error; + } + + _udd[4] = ( udd->iSerialNumber << 24 ) | ( udd->bNumConfigurations << 16 ) | (_ptr>>16); + _udd[5] = ((_ptr & 0xFFFF) << 16) | (_udd[5]&0xFFFF); + + memset( udd->configurations, 0, udd->bNumConfigurations * sizeof(*udd->configurations) ); + + for( iConf = 0; iConf < udd->bNumConfigurations; iConf++) + { + buffer = USB_Alloc( USB_DT_CONFIG_SIZE ); + if(buffer == NULL) + { + retval = -ENOMEM; + goto free_and_error; + } + + retval = __usb_getdesc(fd, buffer, USB_DT_CONFIG, iConf, USB_DT_CONFIG_SIZE); + ucd = &udd->configurations[iConf]; + memcpy( ucd, buffer, USB_DT_CONFIG_SIZE ); + USB_Free( buffer ); + + u32 *_ucd = (u32*)ucd; + + _ucd[0] = ( ucd->bLength << 24 ) | ( ucd->bDescriptorType << 16 ) | cpu_to_le16(ucd->wTotalLength); + + //ucd->wTotalLength = cpu_to_le16(ucd->wTotalLength); + + buffer = USB_Alloc( ucd->wTotalLength); + if(buffer == NULL) + { + retval = -ENOMEM; + goto free_and_error; + } + + retval = __usb_getdesc(fd, buffer, USB_DT_CONFIG, iConf, ucd->wTotalLength); + if(retval < 0) + goto free_and_error; + + ptr = buffer; + ptr += ucd->bLength; + + retval = -ENOMEM; + //ucd->interfaces = USB_Alloc(ucd->bNumInterfaces* sizeof(*ucd->interfaces)); + //if(ucd->interfaces == NULL) + // goto free_and_error; + + _ptr = (u32)USB_Alloc(ucd->bNumInterfaces* sizeof(*ucd->interfaces)); + if( _ptr == 0 ) + goto free_and_error; + + _ucd[2] = ( ucd->bMaxPower << 24 ) | (_ptr>>8); + _ucd[3] = ((_ptr & 0xFF) << 24) | (_ucd[3]&0xFFFFFF); + + //dbgprintf("ucd->interfaces:%p\n", ucd->interfaces ); + memset( ucd->interfaces, 0, ucd->bNumInterfaces * sizeof(*ucd->interfaces) ); + + for(iInterface = 0; iInterface < ucd->bNumInterfaces; iInterface++) + { + uid = &ucd->interfaces[iInterface]; + memcpy(uid, ptr, USB_DT_INTERFACE_SIZE); + ptr += uid->bLength; + + //uid->endpoints = USB_Alloc(uid->bNumEndpoints* sizeof(*uid->endpoints)); + //if(uid->endpoints == NULL) + // goto free_and_error; + + u32 *_uid = (u32*)uid; + _ptr = (u32)USB_Alloc(uid->bNumEndpoints* sizeof(*uid->endpoints)); + + _uid[2] = (uid->iInterface<<24) | (_ptr >> 8); + _uid[3] = (_ptr<<24) | (_uid[3]&0xFFFFFF); + + memset( uid->endpoints, 0, uid->bNumEndpoints * sizeof(*uid->endpoints) ); + + for( iEndpoint = 0; iEndpoint < uid->bNumEndpoints; iEndpoint++) + { + ued = &uid->endpoints[iEndpoint]; + memcpy( ued, ptr, USB_DT_ENDPOINT_SIZE ); + ptr += ued->bLength; + + //ued->wMaxPacketSize = cpu_to_le16(ued->wMaxPacketSize); + u32 *_ued = (u32*)ued; + _ued[1] = (cpu_to_le16(ued->wMaxPacketSize) << 16 ) | (_ued[1] & 0xFFFF); + } + } + + USB_Free( buffer); + buffer = (u8*)NULL; + } + retval = 0; + +free_and_error: + if(buffer != NULL) + USB_Free(buffer); + if(retval < 0) + USB_FreeDescriptors(udd); + return retval; +} + +void USB_FreeDescriptors(usb_devdesc *udd) +{ + int iConf, iInterface; + usb_configurationdesc *ucd; + usb_interfacedesc *uid; + if(udd->configurations != NULL) + { + for(iConf = 0; iConf < udd->bNumConfigurations; iConf++) + { + ucd = &udd->configurations[iConf]; + if(ucd->interfaces != NULL) + { + for(iInterface = 0; iInterface < ucd->bNumInterfaces; iInterface++) + { + uid = &ucd->interfaces[iInterface]; + if(uid->endpoints != NULL) + USB_Free(uid->endpoints); + } + USB_Free(ucd->interfaces); + } + } + USB_Free(udd->configurations); + } +} + + +void USB_SuspendDevice(struct ehci_device *fd) +{ + return ; +} +void USB_ResumeDevice(struct ehci_device *fd) +{ + return ; +} + + +s32 USB_WriteBlkMsg(struct ehci_device *fd,u8 bEndpoint,u16 wLength,void *rpData) +{ + return ehci_bulk_message(fd,bEndpoint,wLength,rpData); +} + +s32 USB_WriteCtrlMsg(struct ehci_device *fd,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *rpData) +{ + return __usb_control_message(fd,bmRequestType,bmRequest,wValue,wIndex,wLength,rpData,NULL,NULL); +} + +s32 USB_GetConfiguration(struct ehci_device *fd, u8 *configuration) +{ + u8 *_configuration; + s32 retval; + + _configuration = USB_Alloc(1); + if(_configuration == NULL) + return -ENOMEM; + + retval = __usb_control_message(fd, (USB_CTRLTYPE_DIR_DEVICE2HOST | USB_CTRLTYPE_TYPE_STANDARD | USB_CTRLTYPE_REC_DEVICE), USB_REQ_GETCONFIG, 0, 0, 1, _configuration, NULL, NULL); + if(retval >= 0) + *configuration = *_configuration; + USB_Free( _configuration); + + return retval; +} +s32 USB_SetConfiguration(struct ehci_device *fd, u8 configuration) +{ + return __usb_control_message(fd, (USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_STANDARD | USB_CTRLTYPE_REC_DEVICE), USB_REQ_SETCONFIG, configuration, 0, 0, NULL, NULL, NULL); +} +s32 USB_SetAlternativeInterface(struct ehci_device *fd, u8 interface, u8 alternateSetting) +{ + if(alternateSetting == 0) + return -EINVAL; + return __usb_control_message(fd, (USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_STANDARD | USB_CTRLTYPE_REC_INTERFACE), + USB_REQ_SETINTERFACE, alternateSetting, interface, 0, NULL, NULL, NULL); + +} +s32 USB_ClearHalt(struct ehci_device *fd, u8 endpoint) +{ + return __usb_control_message(fd, (USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_STANDARD | USB_CTRLTYPE_REC_ENDPOINT), + USB_REQ_CLEARFEATURE, USB_FEATURE_ENDPOINT_HALT, endpoint, 0, NULL, NULL, NULL); +} diff --git a/usb.h b/usb.h new file mode 100644 index 0000000..883320f --- /dev/null +++ b/usb.h @@ -0,0 +1,176 @@ +#ifndef __USB_H__ +#define __USB_H__ + + +#define USB_MAXPATH IPC_MAXPATH_LEN + +#define USB_OK 0 +#define USB_FAILED 1 + +/* Descriptor types */ +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 + +/* Standard requests */ +#define USB_REQ_GETSTATUS 0x00 +#define USB_REQ_CLEARFEATURE 0x01 +#define USB_REQ_SETFEATURE 0x03 +#define USB_REQ_SETADDRESS 0x05 +#define USB_REQ_GETDESCRIPTOR 0x06 +#define USB_REQ_SETDESCRIPTOR 0x07 +#define USB_REQ_GETCONFIG 0x08 +#define USB_REQ_SETCONFIG 0x09 +#define USB_REQ_GETINTERFACE 0x0a +#define USB_REQ_SETINTERFACE 0x0b +#define USB_REQ_SYNCFRAME 0x0c + +/* Descriptor sizes per descriptor type */ +#define USB_DT_DEVICE_SIZE 18 +#define USB_DT_CONFIG_SIZE 9 +#define USB_DT_INTERFACE_SIZE 9 +#define USB_DT_ENDPOINT_SIZE 7 +#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ +#define USB_DT_HUB_NONVAR_SIZE 7 + +/* control message request type bitmask */ +#define USB_CTRLTYPE_DIR_HOST2DEVICE (0<<7) +#define USB_CTRLTYPE_DIR_DEVICE2HOST (1<<7) +#define USB_CTRLTYPE_TYPE_STANDARD (0<<5) +#define USB_CTRLTYPE_TYPE_CLASS (1<<5) +#define USB_CTRLTYPE_TYPE_VENDOR (2<<5) +#define USB_CTRLTYPE_TYPE_RESERVED (3<<5) +#define USB_CTRLTYPE_REC_DEVICE 0 +#define USB_CTRLTYPE_REC_INTERFACE 1 +#define USB_CTRLTYPE_REC_ENDPOINT 2 +#define USB_CTRLTYPE_REC_OTHER 3 + +#define USB_FEATURE_ENDPOINT_HALT 0 + +#define USB_ENDPOINT_IN 0x80 +#define USB_ENDPOINT_OUT 0x00 + + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ +# define ATTRIBUTE_PACKED __attribute__((packed)) + +typedef struct _usbctrlrequest { + u8 bRequestType; + u8 bRequest; + u16 wValue; + + u16 wIndex; + u16 wLength; +} ATTRIBUTE_PACKED usbctrlrequest; + +typedef struct _usbendpointdesc +{ + u8 bLength; + u8 bDescriptorType; + u8 bEndpointAddress; + u8 bmAttributes; + + u16 wMaxPacketSize; + u8 bInterval; + u8 pad; +} ATTRIBUTE_PACKED usb_endpointdesc; + +typedef struct _usbinterfacedesc +{ + u8 bLength; + u8 bDescriptorType; + u8 bInterfaceNumber; + u8 bAlternateSetting; + + u8 bNumEndpoints; + u8 bInterfaceClass; + u8 bInterfaceSubClass; + u8 bInterfaceProtocol; + + u8 iInterface; + struct _usbendpointdesc *endpoints; +} ATTRIBUTE_PACKED usb_interfacedesc; + +typedef struct _usbconfdesc +{ + u8 bLength; + u8 bDescriptorType; + u16 wTotalLength; + + u8 bNumInterfaces; + u8 bConfigurationValue; + u8 iConfiguration; + u8 bmAttributes; + + u8 bMaxPower; + struct _usbinterfacedesc *interfaces; +} ATTRIBUTE_PACKED usb_configurationdesc; + +typedef struct _usbdevdesc +{ + u8 bLength; + u8 bDescriptorType; + u16 bcdUSB; + + u8 bDeviceClass; + u8 bDeviceSubClass; + u8 bDeviceProtocol; + u8 bMaxPacketSize0; + + u16 idVendor; + u16 idProduct; + + u16 bcdDevice; + u8 iManufacturer; + u8 iProduct; + + u8 iSerialNumber; + u8 bNumConfigurations; + struct _usbconfdesc *configurations; +} ATTRIBUTE_PACKED usb_devdesc; + +struct ehci_device; + +s32 USB_OpenDevice(const char *device,u16 vid,u16 pid,struct ehci_device **fd); +s32 USB_CloseDevice(struct ehci_device **fd); + +s32 USB_GetDescriptors(struct ehci_device *fd, usb_devdesc *udd); +void USB_FreeDescriptors(usb_devdesc *udd); + +s32 USB_GetDeviceDescription(struct ehci_device *fd,usb_devdesc *devdesc); + +void USB_SuspendDevice(struct ehci_device *fd); +void USB_ResumeDevice(struct ehci_device *fd); + +s32 USB_ReadIntrMsg(struct ehci_device *fd,u8 bEndpoint,u16 wLength,void *rpData); + +s32 USB_ReadBlkMsg(struct ehci_device *fd,u8 bEndpoint,u16 wLength,void *rpData); + +s32 USB_ReadCtrlMsg(struct ehci_device *fd,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *rpData); + +s32 USB_WriteIntrMsg(struct ehci_device *fd,u8 bEndpoint,u16 wLength,void *rpData); + +s32 USB_WriteBlkMsg(struct ehci_device *fd,u8 bEndpoint,u16 wLength,void *rpData); + +s32 USB_WriteCtrlMsg(struct ehci_device *fd,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *rpData); + +s32 USB_GetConfiguration(struct ehci_device *fd, u8 *configuration); +s32 USB_SetConfiguration(struct ehci_device *fd, u8 configuration); +s32 USB_SetAlternativeInterface(struct ehci_device *fd, u8 interface, u8 alternateSetting); +s32 USB_ClearHalt(struct ehci_device *fd, u8 endpointAddress); +s32 USB_GetDeviceList(const char *devpath,void *descr_buffer,u8 num_descr,u8 b0,u8 *cnt_descr); + +/* alloc memory from the USB subsystem */ +void * USB_Alloc(int size); +void USB_Free(void *ptr); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + + +#endif diff --git a/usb_os.c b/usb_os.c new file mode 100644 index 0000000..60b8e27 --- /dev/null +++ b/usb_os.c @@ -0,0 +1,78 @@ +#include +#include "ehci_types.h" +#include "usb.h" +#include "ehci.h" +#include "alloc.h" + +void *VirtualToPhysical( void *address ) +{ + if( ((u32)address & 0xFFFF0000) == 0xFFFF0000 ) + return (void*)address; + + return (void*)((u32)address & 0x7FFFFFFF); +} +void sync_after_write( void *a, u32 v ) +{ + dc_flushrange( a, v ); +} +void sync_before_read( void *a, u32 v ) +{ + dc_invalidaterange( a, v ); +} + + +int usb_os_init(void) +{ + return 0; +} +void *ehci_maligned(int size,int alignement,int crossing) +{ + return (void*)malloca( size, alignement ); +} +dma_addr_t ehci_virt_to_dma(void *a) +{ + return (dma_addr_t)VirtualToPhysical(a); +} +dma_addr_t ehci_dma_map_to(void *buf,size_t len) +{ + sync_after_write(buf, len); + return (dma_addr_t)VirtualToPhysical(buf); +} +dma_addr_t ehci_dma_map_from(void *buf,size_t len) +{ + sync_after_write(buf, len); + return (dma_addr_t)VirtualToPhysical(buf); +} +dma_addr_t ehci_dma_map_bidir(void *buf,size_t len) +{ + sync_after_write(buf, len); + return (dma_addr_t)VirtualToPhysical(buf); +} +void ehci_dma_unmap_to(dma_addr_t buf,size_t len) +{ + sync_before_read((void*)buf, len); +} +void ehci_dma_unmap_from(dma_addr_t buf,size_t len) +{ + sync_before_read((void*)buf, len); +} +void ehci_dma_unmap_bidir(dma_addr_t buf,size_t len) +{ + sync_before_read((void*)buf, len); +} +void *USB_Alloc(int size) +{ + //u32 val; + //__asm("mov %0,lr": "=r" (val) ); + //dbgprintf("USB_Alloc(%u) LR:%08x\n", size, val ); + + //void *ptr = malloc(size); + //memset( ptr, 0, size ); + //return ptr; + + return malloc(size); +} +void USB_Free(void *ptr) +{ + return free(ptr); +} diff --git a/usbstorage.c b/usbstorage.c new file mode 100644 index 0000000..9e0876b --- /dev/null +++ b/usbstorage.c @@ -0,0 +1,745 @@ +/*------------------------------------------------------------- + +usbstorage.c -- Bulk-only USB mass storage support + +Copyright (C) 2008 +Sven Peter (svpe) + +quick port to ehci/ios: Kwiirk + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + +#define ROUNDDOWN32(v) (((u32)(v)-0x1f)&~0x1f) + +#define HEAP_SIZE (32*1024) +#define TAG_START 0x1//BADC0DE + +#define CBW_SIZE 31 +#define CBW_SIGNATURE 0x43425355 +#define CBW_IN (1 << 7) +#define CBW_OUT 0 + +#define CSW_SIZE 13 +#define CSW_SIGNATURE 0x53425355 + +#define SCSI_TEST_UNIT_READY 0x00 +#define SCSI_INQUIRY 0x12 +#define SCSI_REQUEST_SENSE 0x03 +#define SCSI_READ_CAPACITY 0x25 +#define SCSI_READ_10 0x28 +#define SCSI_WRITE_10 0x2A + +#define SCSI_SENSE_REPLY_SIZE 18 +#define SCSI_SENSE_NOT_READY 0x02 +#define SCSI_SENSE_MEDIUM_ERROR 0x03 +#define SCSI_SENSE_HARDWARE_ERROR 0x04 + +#define USB_CLASS_MASS_STORAGE 0x08 +#define MASS_STORAGE_SCSI_COMMANDS 0x06 +#define MASS_STORAGE_BULK_ONLY 0x50 + +#define USBSTORAGE_GET_MAX_LUN 0xFE +#define USBSTORAGE_RESET 0xFF + +#define USB_ENDPOINT_BULK 0x02 + +#define USBSTORAGE_CYCLE_RETRIES 3 + +#define MAX_TRANSFER_SIZE 4096 + +#define DEVLIST_MAXSIZE 8 + +static s32 __usbstorage_reset(usbstorage_handle *dev); +static s32 __usbstorage_clearerrors(usbstorage_handle *dev, u8 lun); + +// ehci driver has its own timeout. +static s32 __USB_BlkMsgTimeout(usbstorage_handle *dev, u8 bEndpoint, u16 wLength, void *rpData) +{ + return USB_WriteBlkMsg(dev->usb_fd, bEndpoint, wLength, rpData); +} + +static s32 __USB_CtrlMsgTimeout(usbstorage_handle *dev, u8 bmRequestType, u8 bmRequest, u16 wValue, u16 wIndex, u16 wLength, void *rpData) +{ + return USB_WriteCtrlMsg(dev->usb_fd, bmRequestType, bmRequest, wValue, wIndex, wLength, rpData); +} +static s32 __send_cbw(usbstorage_handle *dev, u8 lun, u32 len, u8 flags, const u8 *cb, u8 cbLen) +{ + s32 retval = USBSTORAGE_OK; + + if(cbLen == 0 || cbLen > 16) + return -EINVAL; + + memset(dev->buffer, 0, CBW_SIZE); + + ((u32*)dev->buffer)[0]=cpu_to_le32(CBW_SIGNATURE); + ((u32*)dev->buffer)[1]=cpu_to_le32(dev->tag); + ((u32*)dev->buffer)[2]=cpu_to_le32(len); + dev->buffer[12] = flags; + dev->buffer[13] = lun; + dev->buffer[14] = (cbLen > 6 ? 0x10 : 6); + + memcpy(dev->buffer + 15, (void*)cb, cbLen); + + retval = __USB_BlkMsgTimeout(dev, dev->ep_out, CBW_SIZE, (void *)dev->buffer); + + if(retval == CBW_SIZE) return USBSTORAGE_OK; + else if(retval >= 0) return USBSTORAGE_ESHORTWRITE; + + return retval; +} + +static s32 __read_csw(usbstorage_handle *dev, u8 *status, u32 *dataResidue) +{ + s32 retval = USBSTORAGE_OK; + u32 signature, tag, _dataResidue, _status; + + memset(dev->buffer, 0xff, CSW_SIZE); + + retval = __USB_BlkMsgTimeout(dev, dev->ep_in, CSW_SIZE, dev->buffer); + //print_hex_dump_bytes("csv resp:",DUMP_PREFIX_OFFSET,dev->buffer,CSW_SIZE); + + if(retval >= 0 && retval != CSW_SIZE) return USBSTORAGE_ESHORTREAD; + else if(retval < 0) return retval; + + signature = le32_to_cpu(((u32*)dev->buffer)[0]); + tag = le32_to_cpu(((u32*)dev->buffer)[1]); + _dataResidue = le32_to_cpu(((u32*)dev->buffer)[2]); + _status = dev->buffer[12]; + + if(signature != CSW_SIGNATURE) { + BUG(); + return USBSTORAGE_ESIGNATURE; + } + + if(dataResidue != NULL) + *dataResidue = _dataResidue; + if(status != NULL) + *status = _status; + + if(tag != dev->tag) return USBSTORAGE_ETAG; + dev->tag++; + + return USBSTORAGE_OK; +} +static s32 __cycle(usbstorage_handle *dev, u8 lun, u8 *buffer, u32 len, u8 *cb, u8 cbLen, u8 write, u8 *_status, u32 *_dataResidue) +{ + s32 retval = USBSTORAGE_OK; + + u8 status = 0; + u32 dataResidue = 0; + u32 thisLen; + + s8 retries = USBSTORAGE_CYCLE_RETRIES + 1; + + do + { + retries--; + if(retval == USBSTORAGE_ETIMEDOUT) + break; + + if(write) + { + retval = __send_cbw(dev, lun, len, CBW_OUT, cb, cbLen); + if(retval == USBSTORAGE_ETIMEDOUT) + break; + if(retval < 0) + { + if(__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT) + retval = USBSTORAGE_ETIMEDOUT; + continue; + } + while(len > 0) + { + thisLen=len; + retval = __USB_BlkMsgTimeout(dev, dev->ep_out, thisLen, buffer); + + if(retval == USBSTORAGE_ETIMEDOUT) + break; + + if(retval < 0) + { + retval = USBSTORAGE_EDATARESIDUE; + break; + } + + + if(retval != thisLen && len > 0) + { + retval = USBSTORAGE_EDATARESIDUE; + break; + } + len -= retval; + buffer += retval; + } + + if(retval < 0) + { + if(__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT) + retval = USBSTORAGE_ETIMEDOUT; + continue; + } + } + else + { + retval = __send_cbw(dev, lun, len, CBW_IN, cb, cbLen); + + if(retval == USBSTORAGE_ETIMEDOUT) + break; + + if(retval < 0) + { + if(__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT) + retval = USBSTORAGE_ETIMEDOUT; + continue; + } + while(len > 0) + { + thisLen=len; + retval = __USB_BlkMsgTimeout(dev, dev->ep_in, thisLen, buffer); + //print_hex_dump_bytes("usbs in:",DUMP_PREFIX_OFFSET,dev->buffer,36); + if(retval < 0) + break; + + len -= retval; + buffer += retval; + + if(retval != thisLen) + break; + } + + if(retval < 0) + { + if(__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT) + retval = USBSTORAGE_ETIMEDOUT; + continue; + } + } + + retval = __read_csw(dev, &status, &dataResidue); + + if(retval == USBSTORAGE_ETIMEDOUT) + break; + + if(retval < 0) + { + if(__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT) + retval = USBSTORAGE_ETIMEDOUT; + continue; + } + + retval = USBSTORAGE_OK; + } while(retval < 0 && retries > 0); + + if(retval < 0 && retval != USBSTORAGE_ETIMEDOUT) + { + if(__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT) + retval = USBSTORAGE_ETIMEDOUT; + } + + + if(_status != NULL) + *_status = status; + if(_dataResidue != NULL) + *_dataResidue = dataResidue; + + return retval; +} + +static s32 __usbstorage_clearerrors(usbstorage_handle *dev, u8 lun) +{ + s32 retval; + u8 cmd[16]; + u8 *sense= USB_Alloc(SCSI_SENSE_REPLY_SIZE); + u8 status = 0; + memset(cmd, 0, sizeof(cmd)); + cmd[0] = SCSI_TEST_UNIT_READY; + + retval = __cycle(dev, lun, NULL, 0, cmd, 1, 1, &status, NULL); + if(retval < 0) return retval; + + if(status != 0) + { + cmd[0] = SCSI_REQUEST_SENSE; + cmd[1] = lun << 5; + cmd[4] = SCSI_SENSE_REPLY_SIZE; + cmd[5] = 0; + memset(sense, 0, SCSI_SENSE_REPLY_SIZE); + retval = __cycle(dev, lun, sense, SCSI_SENSE_REPLY_SIZE, cmd, 6, 0, NULL, NULL); + if(retval < 0) goto error; + + status = sense[2] & 0x0F; + if(status == SCSI_SENSE_NOT_READY || status == SCSI_SENSE_MEDIUM_ERROR || status == SCSI_SENSE_HARDWARE_ERROR) + retval = USBSTORAGE_ESENSE; + } +error: + USB_Free(sense); + return retval; +} + +static s32 __usbstorage_reset(usbstorage_handle *dev) +{ + s32 retval; + + if(dev->suspended == 1) + { + USB_ResumeDevice(dev->usb_fd); + dev->suspended = 0; + } +/* + retval = ehci_reset_device(dev->usb_fd); + if(retval < 0 && retval != -7004) + goto end; + */ + //debug_printf("usbstorage reset..\n"); + retval = __USB_CtrlMsgTimeout(dev, (USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_CLASS | USB_CTRLTYPE_REC_INTERFACE), USBSTORAGE_RESET, 0, dev->interface, 0, NULL); + + /* FIXME?: some devices return -7004 here which definitely violates the usb ms protocol but they still seem to be working... */ + if(retval < 0 && retval != -7004) + goto end; + + /* gives device enough time to process the reset */ + msleep(10); + + //debug_printf("cleat halt on bulk ep..\n"); + retval = USB_ClearHalt(dev->usb_fd, dev->ep_in); + if(retval < 0) + goto end; + retval = USB_ClearHalt(dev->usb_fd, dev->ep_out); + +end: + return retval; +} + +s32 USBStorage_Open(usbstorage_handle *dev, struct ehci_device *fd) +{ + s32 retval = -1; + u8 conf,*max_lun = NULL; + u32 iConf, iInterface, iEp; + usb_devdesc udd; + usb_configurationdesc *ucd; + usb_interfacedesc *uid; + usb_endpointdesc *ued; + + max_lun = USB_Alloc(1); + if(max_lun==NULL) return -ENOMEM; + + memset(dev, 0, sizeof(*dev)); + + dev->tag = TAG_START; + dev->usb_fd = fd; + + retval = USB_GetDescriptors(dev->usb_fd, &udd); + if(retval < 0) + { + dbgprintf("USB_GetDescriptors():%d\n", retval ); + goto free_and_return; + } + + dbgprintf("udd.bNumConfigurations:%u\n", udd.bNumConfigurations ); + + for(iConf = 0; iConf < udd.bNumConfigurations; iConf++) + { + ucd = &udd.configurations[iConf]; + for(iInterface = 0; iInterface < ucd->bNumInterfaces; iInterface++) + { + uid = &ucd->interfaces[iInterface]; + if(uid->bInterfaceClass == USB_CLASS_MASS_STORAGE && + uid->bInterfaceSubClass == MASS_STORAGE_SCSI_COMMANDS && + uid->bInterfaceProtocol == MASS_STORAGE_BULK_ONLY) + { + if(uid->bNumEndpoints < 2) + continue; + + dev->ep_in = dev->ep_out = 0; + for(iEp = 0; iEp < uid->bNumEndpoints; iEp++) + { + ued = &uid->endpoints[iEp]; + if(ued->bmAttributes != USB_ENDPOINT_BULK) + continue; + + if(ued->bEndpointAddress & USB_ENDPOINT_IN) + { + dev->ep_in = ued->bEndpointAddress; + dbgprintf("%08X:%08X\n", dev->ep_in, ued->bEndpointAddress ); + } else { + dev->ep_out = ued->bEndpointAddress; + dbgprintf("%08X:%08X\n", dev->ep_out, ued->bEndpointAddress ); + } + } + + if(dev->ep_in != 0 && dev->ep_out != 0) + { + dev->configuration = ucd->bConfigurationValue; + dev->interface = uid->bInterfaceNumber; + dev->altInterface = uid->bAlternateSetting; + + dbgprintf("%08X:%08X\n", dev->configuration , ucd->bConfigurationValue ); + dbgprintf("%08X:%08X\n", dev->interface , uid->bInterfaceNumber ); + dbgprintf("%08X:%08X\n", dev->altInterface , uid->bAlternateSetting ); + + goto found; + } + } + } + } + + USB_FreeDescriptors(&udd); + retval = USBSTORAGE_ENOINTERFACE; + goto free_and_return; + +found: + USB_FreeDescriptors(&udd); + + retval = USBSTORAGE_EINIT; + if(USB_GetConfiguration(dev->usb_fd, &conf) < 0) + goto free_and_return; + if(conf != dev->configuration && USB_SetConfiguration(dev->usb_fd, dev->configuration) < 0) + goto free_and_return; + if(dev->altInterface != 0 && USB_SetAlternativeInterface(dev->usb_fd, dev->interface, dev->altInterface) < 0) + goto free_and_return; + dev->suspended = 0; + + retval = USBStorage_Reset(dev); + if(retval < 0) + goto free_and_return; + + retval = __USB_CtrlMsgTimeout(dev, (USB_CTRLTYPE_DIR_DEVICE2HOST | USB_CTRLTYPE_TYPE_CLASS | USB_CTRLTYPE_REC_INTERFACE), USBSTORAGE_GET_MAX_LUN, 0, dev->interface, 1, max_lun); + if(retval < 0) + dev->max_lun = 1; + else + dev->max_lun = *max_lun; + + + if(retval == USBSTORAGE_ETIMEDOUT) + goto free_and_return; + + retval = USBSTORAGE_OK; + + if(dev->max_lun == 0) + dev->max_lun++; + + /* taken from linux usbstorage module (drivers/usb/storage/transport.c) */ + /* + * Some devices (i.e. Iomega Zip100) need this -- apparently + * the bulk pipes get STALLed when the GetMaxLUN request is + * processed. This is, in theory, harmless to all other devices + * (regardless of if they stall or not). + */ + USB_ClearHalt(dev->usb_fd, dev->ep_in); + USB_ClearHalt(dev->usb_fd, dev->ep_out); + + dev->buffer = /*USB_Alloc(MAX_TRANSFER_SIZE)*/(u8*)0xFFFE4000; + + if(dev->buffer == NULL) retval = -ENOMEM; + else retval = USBSTORAGE_OK; + +free_and_return: + if(max_lun!=NULL) USB_Free(max_lun); + if(retval < 0) + { + if(dev->buffer != NULL) + USB_Free(dev->buffer); + memset(dev, 0, sizeof(*dev)); + return retval; + } + return 0; +} + +s32 USBStorage_Close(usbstorage_handle *dev) +{ + if(dev->buffer != NULL) + USB_Free(dev->buffer); + memset(dev, 0, sizeof(*dev)); + return 0; +} + +s32 USBStorage_Reset(usbstorage_handle *dev) +{ + s32 retval; + + retval = __usbstorage_reset(dev); + + return retval; +} + +s32 USBStorage_GetMaxLUN(usbstorage_handle *dev) +{ + + return dev->max_lun; +} + +s32 USBStorage_MountLUN(usbstorage_handle *dev, u8 lun) +{ + s32 retval; + + if(lun >= dev->max_lun) + return -EINVAL; + + retval = __usbstorage_clearerrors(dev, lun); + if(retval < 0) + return retval; + + retval = USBStorage_Inquiry(dev, lun); + + retval = USBStorage_ReadCapacity(dev, lun, &dev->sector_size[lun], &dev->n_sector[lun]); + return retval; +} + +s32 USBStorage_Inquiry(usbstorage_handle *dev, u8 lun) +{ + s32 retval; + u8 cmd[] = {SCSI_INQUIRY, lun << 5,0,0,36,0}; + u8 *response = USB_Alloc(36); + + retval = __cycle(dev, lun, response, 36, cmd, 6, 0, NULL, NULL); + //print_hex_dump_bytes("inquiry result:",DUMP_PREFIX_OFFSET,response,36); + USB_Free(response); + return retval; +} +s32 USBStorage_ReadCapacity(usbstorage_handle *dev, u8 lun, u32 *sector_size, u32 *n_sectors) +{ + s32 retval; + u8 cmd[] = {SCSI_READ_CAPACITY, lun << 5}; + u8 *response = USB_Alloc(8); + u32 val; + retval = __cycle(dev, lun, response, 8, cmd, 2, 0, NULL, NULL); + if(retval >= 0) + { + + memcpy(&val, response, 4); + if(n_sectors != NULL) + *n_sectors = be32_to_cpu(val); + memcpy(&val, response + 4, 4); + if(sector_size != NULL) + *sector_size = be32_to_cpu(val); + retval = USBSTORAGE_OK; + } + USB_Free(response); + return retval; +} + +s32 USBStorage_Read(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sectors, u8 *buffer) +{ + u8 status = 0; + s32 retval; + u8 cmd[] = { + SCSI_READ_10, + lun << 5, + sector >> 24, + sector >> 16, + sector >> 8, + sector, + 0, + n_sectors >> 8, + n_sectors, + 0 + }; + if(lun >= dev->max_lun || dev->sector_size[lun] == 0) + return -EINVAL; + retval = __cycle(dev, lun, buffer, n_sectors * dev->sector_size[lun], cmd, sizeof(cmd), 0, &status, NULL); + if(retval > 0 && status != 0) + retval = USBSTORAGE_ESTATUS; + return retval; +} + +s32 USBStorage_Write(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sectors, const u8 *buffer) +{ + u8 status = 0; + s32 retval; + u8 cmd[] = { + SCSI_WRITE_10, + lun << 5, + sector >> 24, + sector >> 16, + sector >> 8, + sector, + 0, + n_sectors >> 8, + n_sectors, + 0 + }; + if(lun >= dev->max_lun || dev->sector_size[lun] == 0) + return -EINVAL; + retval = __cycle(dev, lun, (u8 *)buffer, n_sectors * dev->sector_size[lun], cmd, sizeof(cmd), 1, &status, NULL); + if(retval > 0 && status != 0) + retval = USBSTORAGE_ESTATUS; + return retval; +} + +s32 USBStorage_Suspend(usbstorage_handle *dev) +{ + if(dev->suspended == 1) + return USBSTORAGE_OK; + + USB_SuspendDevice(dev->usb_fd); + dev->suspended = 1; + + return USBSTORAGE_OK; + +} +/* +The following is for implementing the ioctl interface inpired by the disc_io.h +as used by libfat + +This opens the first lun of the first usbstorage device found. +*/ + +static usbstorage_handle __usbfd; +static u8 __lun = 0; +static u8 __mounted = 0; +static u16 __vid = 0; +static u16 __pid = 0; + +void switchbuf(void) +{ + memcpy( (void*)0x080A0000, __usbfd.buffer, CBW_SIZE ); + __usbfd.buffer = (u8*)0x080A0000; +} +/* perform 512 time the same read */ +s32 USBStorage_Read_Stress(u32 sector, u32 numSectors, void *buffer) +{ + s32 retval; + int i; + if(__mounted != 1) + return false; + + for(i=0;i<512;i++){ + retval = USBStorage_Read(&__usbfd, __lun, sector, numSectors, buffer); + sector+=numSectors; + if(retval == USBSTORAGE_ETIMEDOUT) + { + __mounted = 0; + USBStorage_Close(&__usbfd); + } + if(retval < 0) + return false; + } + return true; + +} +// temp function before libfat is available */ +s32 USBStorage_Try_Device(struct ehci_device *fd) +{ + int maxLun,j,retval; + int ret = USBStorage_Open(&__usbfd, fd); + if( ret < 0) + { + dbgprintf("Could not open USB device:%d\n",ret); + return -EINVAL; + } + + maxLun = USBStorage_GetMaxLUN(&__usbfd); + if(maxLun == USBSTORAGE_ETIMEDOUT) + { + dbgprintf("USB device timed out\n"); + return -EINVAL; + } + + for(j = 0; j < maxLun; j++) + { + retval = USBStorage_MountLUN(&__usbfd, j); + if(retval == USBSTORAGE_ETIMEDOUT) + { + USBStorage_Reset(&__usbfd); + USBStorage_Close(&__usbfd); + break; + } + + if(retval < 0) + continue; + + __vid=fd->desc.idVendor; + __pid=fd->desc.idProduct; + __mounted = 1; + __lun = j; + return 0; + } + + dbgprintf("USB device failed to mount\n"); + return -EINVAL; +} + +static int ums_init_done = 0; +s32 USBStorage_Init(void) +{ + int i; + //debug_printf("usbstorage init %d\n", ums_init_done); + if(ums_init_done) + return 0; + ums_init_done = 1; + for(i = 0;inum_port; i++){ + struct ehci_device *dev = &ehci->devices[i]; + if(dev->id != 0){ + USBStorage_Try_Device(dev); + } + } + return 0; +} + +s32 USBStorage_Get_Capacity(u32*sector_size) +{ + if(__mounted == 1) + { + if(sector_size){ + *sector_size = __usbfd.sector_size[__lun]; + } + return __usbfd.n_sector[__lun]; + } + return 0; +} + +s32 USBStorage_Read_Sectors(u32 sector, u32 numSectors, void *buffer) +{ + s32 retval; + + if(__mounted != 1) + return false; + + retval = USBStorage_Read(&__usbfd, __lun, sector, numSectors, buffer); + if(retval == USBSTORAGE_ETIMEDOUT) + { + __mounted = 0; + USBStorage_Close(&__usbfd); + } + if(retval < 0) + return false; + return true; +} + +s32 USBStorage_Write_Sectors(u32 sector, u32 numSectors, const void *buffer) +{ + s32 retval; + + if(__mounted != 1) + return false; + + retval = USBStorage_Write(&__usbfd, __lun, sector, numSectors, buffer); + if(retval == USBSTORAGE_ETIMEDOUT) + { + __mounted = 0; + USBStorage_Close(&__usbfd); + } + if(retval < 0) + return false; + return true; +} + diff --git a/usbstorage.h b/usbstorage.h new file mode 100644 index 0000000..b03a9a8 --- /dev/null +++ b/usbstorage.h @@ -0,0 +1,74 @@ +#ifndef __USBSTORAGE_H__ +#define __USBSTORAGE_H__ + + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +#include "ehci.h" + + +#define USBSTORAGE_OK 0 +#define USBSTORAGE_ENOINTERFACE -10000 +#define USBSTORAGE_ESENSE -10001 +#define USBSTORAGE_ESHORTWRITE -10002 +#define USBSTORAGE_ESHORTREAD -10003 +#define USBSTORAGE_ESIGNATURE -10004 +#define USBSTORAGE_ETAG -10005 +#define USBSTORAGE_ESTATUS -10006 +#define USBSTORAGE_EDATARESIDUE -10007 +#define USBSTORAGE_ETIMEDOUT -ETIMEDOUT +#define USBSTORAGE_EINIT -10009 + +typedef struct +{ + u8 configuration; + u32 interface; + u32 altInterface; + + u8 ep_in; + u8 ep_out; + + u8 max_lun; + u32 sector_size[16]; + u32 n_sector[16]; + + struct ehci_device * usb_fd; + + //mutex_t lock; + //cond_t cond; + s32 retval; + + u32 tag; + u8 suspended; + + u8 *buffer; +} usbstorage_handle; + +s32 USBStorage_Initialize(void); +void switchbuf(void); + +s32 USBStorage_Open(usbstorage_handle *dev, struct ehci_device *fd); +s32 USBStorage_Close(usbstorage_handle *dev); +s32 USBStorage_Reset(usbstorage_handle *dev); + +s32 USBStorage_GetMaxLUN(usbstorage_handle *dev); +s32 USBStorage_MountLUN(usbstorage_handle *dev, u8 lun); +s32 USBStorage_Suspend(usbstorage_handle *dev); + +s32 USBStorage_ReadCapacity(usbstorage_handle *dev, u8 lun, u32 *sector_size, u32 *n_sectors); +s32 USBStorage_Read(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sectors, u8 *buffer); +s32 USBStorage_Write(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sectors, const u8 *buffer); +s32 USBStorage_Inquiry(usbstorage_handle *dev, u8 lun); + +#define DEVICE_TYPE_WII_USB (('W'<<24)|('U'<<16)|('S'<<8)|'B') + +s32 USBStorage_Try_Device(struct ehci_device *fd); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + + +#endif /* __USBSTORAGE_H__ */ diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..65e5888 --- /dev/null +++ b/utils.h @@ -0,0 +1,204 @@ +#ifndef __UTILS_H__ +#define __UTILS_H__ + +#include "global.h" + +static inline u32 read32(u32 addr) +{ + u32 data; + __asm__ volatile ("ldr\t%0, [%1]" : "=l" (data) : "l" (addr)); + return data; +} + +static inline void write32(u32 addr, u32 data) +{ + __asm__ volatile ("str\t%0, [%1]" : : "l" (data), "l" (addr)); +} + +static inline u32 set32(u32 addr, u32 set) +{ + u32 data; + __asm__ volatile ( + "ldr\t%0, [%1]\n" + "\torr\t%0, %2\n" + "\tstr\t%0, [%1]" + : "=&l" (data) + : "l" (addr), "l" (set) + ); + return data; +} + +static inline u32 clear32(u32 addr, u32 clear) +{ + u32 data; + __asm__ volatile ( + "ldr\t%0, [%1]\n" + "\tbic\t%0, %2\n" + "\tstr\t%0, [%1]" + : "=&l" (data) + : "l" (addr), "l" (clear) + ); + return data; +} + + +static inline u32 mask32(u32 addr, u32 clear, u32 set) +{ + u32 data; + __asm__ volatile ( + "ldr\t%0, [%1]\n" + "\tbic\t%0, %3\n" + "\torr\t%0, %2\n" + "\tstr\t%0, [%1]" + : "=&l" (data) + : "l" (addr), "l" (set), "l" (clear) + ); + return data; +} + +static inline u16 read16(u32 addr) +{ + u32 data; + __asm__ volatile ("ldrh\t%0, [%1]" : "=l" (data) : "l" (addr)); + return data; +} + +static inline void write16(u32 addr, u16 data) +{ + __asm__ volatile ("strh\t%0, [%1]" : : "l" (data), "l" (addr)); +} + +static inline u16 set16(u32 addr, u16 set) +{ + u16 data; + __asm__ volatile ( + "ldrh\t%0, [%1]\n" + "\torr\t%0, %2\n" + "\tstrh\t%0, [%1]" + : "=&l" (data) + : "l" (addr), "l" (set) + + ); + return data; +} + +static inline u16 clear16(u32 addr, u16 clear) +{ + u16 data; + __asm__ volatile ( + "ldrh\t%0, [%1]\n" + "\tbic\t%0, %2\n" + "\tstrh\t%0, [%1]" + : "=&l" (data) + : "l" (addr), "l" (clear) + ); + return data; +} + + +static inline u16 mask16(u32 addr, u16 clear, u16 set) +{ + u16 data; + __asm__ volatile ( + "ldrh\t%0, [%1]\n" + "\tbic\t%0, %3\n" + "\torr\t%0, %2\n" + "\tstrh\t%0, [%1]" + : "=&l" (data) + : "l" (addr), "l" (set), "l" (clear) + ); + return data; +} + +static inline u8 read8(u32 addr) +{ + u32 data; + __asm__ volatile ("ldrb\t%0, [%1]" : "=l" (data) : "l" (addr)); + return data; +} + +static inline void write8(u32 addr, u8 data) +{ + __asm__ volatile ("strb\t%0, [%1]" : : "l" (data), "l" (addr)); +} + +static inline u8 set8(u32 addr, u8 set) +{ + u8 data; + __asm__ volatile ( + "ldrb\t%0, [%1]\n" + "\torr\t%0, %2\n" + "\tstrb\t%0, [%1]" + : "=&l" (data) + : "l" (addr), "l" (set) + ); + return data; +} + +static inline u8 clear8(u32 addr, u8 clear) +{ + u8 data; + __asm__ volatile ( + "ldrb\t%0, [%1]\n" + "\tbic\t%0, %2\n" + "\tstrb\t%0, [%1]" + : "=&l" (data) + : "l" (addr), "l" (clear) + ); + return data; +} + +static inline u8 mask8(u32 addr, u8 clear, u8 set) +{ + u8 data; + __asm__ volatile ( + "ldrb\t%0, [%1]\n" + "\tbic\t%0, %3\n" + "\torr\t%0, %2\n" + "\tstrb\t%0, [%1]" + : "=&l" (data) + : "l" (addr), "l" (set), "l" (clear) + ); + return data; +} + +/* + * These functions are guaranteed to copy by reading from src and writing to dst in -bit units + * If size is not aligned, the remaining bytes are not copied + */ +extern void memset32(void *dst, u32 value, u32 size); +extern void memset16(void *dst, u16 value, u32 size); +extern void memset8(void *dst, u8 value, u32 size); + +extern void memcpy(void *dst, void *src, u32 size); + +//void udelay(u32 d); +void panic(u8 v); + +static inline u32 get_cpsr(void) +{ + u32 data; + __asm__ volatile ( "mrs\t%0, cpsr" : "=r" (data) ); + return data; +} + +#define STACK_ALIGN(type, name, cnt, alignment) \ +u8 _al__##name[((sizeof(type)*(cnt)) + (alignment) + \ +(((sizeof(type)*(cnt))%(alignment)) > 0 ? ((alignment) - \ +((sizeof(type)*(cnt))%(alignment))) : 0))]; \ +type *name = (type*)(((u32)(_al__##name)) + ((alignment) - (( \ +(u32)(_al__##name))&((alignment)-1)))) + +#define swab32(x) ((u32)( \ + (((u32)(x) & (u32)0x000000ffUL) << 24) | \ + (((u32)(x) & (u32)0x0000ff00UL) << 8) | \ + (((u32)(x) & (u32)0x00ff0000UL) >> 8) | \ + (((u32)(x) & (u32)0xff000000UL) >> 24))) +#define swab16(x) ((u16)( \ + (((u16)(x) & (u16)0x00ffU) << 8) | \ + (((u16)(x) & (u16)0xff00U) >> 8))) + + + +#endif + diff --git a/utils_asm.S b/utils_asm.S new file mode 100644 index 0000000..e640f6f --- /dev/null +++ b/utils_asm.S @@ -0,0 +1,116 @@ +/* + mini - a Free Software replacement for the Nintendo/BroadOn IOS. + random utilities + +Copyright (C) 2008, 2009 Hector Martin "marcan" + +# This code is licensed to you under the terms of the GNU GPL, version 2; +# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt +*/ + +.arm + +.globl memcpy +.globl memset32 +.globl memset16 +.globl memset8 + +.text + +memcpy: + cmp r2, #0 + bxeq lr + + stmfd sp!, {r4-r6} + orr r3, r0, r1 + tst r3, #3 + bne _unaligned + cmp r2, #0x0F + bls _dwordcopyfast + cmp r2, #0x1F + bls _fastcopy16 + stmfd sp!, {r7-r9} + +_fastcopy32: + ldmia r1!, {r3-r9,r12} + stmia r0!, {r3-r9,r12} + sub r2, r2, #0x20 + cmp r2, #0x1F + bls _done + b _fastcopy32 +_done: + ldmfd sp!, {r7-r9} + +_fastcopy16: + cmp r2, #0xf + bls _dwordcopyfast + ldmia r1!, {r3-r6} + stmia r0!, {r3-r6} + sub r2, r2, #0x10 + b _fastcopy16 + +_dwordcopyfast: + cmp r2, #3 + bls _unaligned + ldr r3, [r1], #4 + str r3, [r0], #4 + sub r2, r2, #4 + b _dwordcopyfast + +_unaligned: + cmp r0, #0x1800000 + bcs _bytecopy + mov r12, #0xff +_dwordcopy: + cmp r2, #0 + beq _done2 + mov r6, r0 + and r3, r6, #3 + ldr r4, [r6,-r3]! + mov r3, r3, lsl #3 + rsb r3, r3, #0x18 + bic r5, r4, r12, lsl r3 + ldrb r4, [r1],#1 + orr r4, r5, r4, lsl r3 + str r4, [r6] + add r0, r0, #1 + sub r2, r2, #1 + b _dwordcopy + +_bytecopy: + cmp r2, #0 + beq _done2 + ldrb r3, [r1], #1 + strb r3, [r0], #1 + sub r2, r2, #1 + b _bytecopy +_done2: + ldmfd sp!, {r4-r6} + bx lr + +memset32: + bics r2, #3 + bxeq lr +1: str r1, [r0] ,#4 + subs r2, #4 + bne 1b + bx lr + + +memset16: + bics r2, #1 + bxeq lr +1: strh r1, [r0], #2 + subs r2, #2 + bne 1b + bx lr + +memset8: + cmp r2, #0 + bxeq lr +1: strb r1, [r0], #1 + subs r2, #1 + bne 1b + bx lr + + diff --git a/vsprintf.c b/vsprintf.c new file mode 100644 index 0000000..b489bf9 --- /dev/null +++ b/vsprintf.c @@ -0,0 +1,442 @@ +/* + * linux/lib/vsprintf.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */ +/* + * Wirzenius wrote this portably, Torvalds fucked it up :-) + */ + +#include +#include "global.h" +#include "string.h" +#include "ff.h" +#include "memory.h" + +char * strstr ( const char *str1, const char *str2) +{ + char *cp = (char *) str1; + char *s1, *s2; + + if ( !*str2 ) + return((char *)str1); + + while (*cp) + { + s1 = cp; + s2 = (char *) str2; + while ( *s1 && *s2 && !(*s1-*s2) ) + s1++, s2++; + + if (!*s2) + return(cp); + cp++; + + } + + return(NULL); +} + +static inline int isdigit(int c) +{ + return c >= '0' && c <= '9'; +} + +static inline int isxdigit(int c) +{ + return (c >= '0' && c <= '9') + || (c >= 'a' && c <= 'f') + || (c >= 'A' && c <= 'F'); +} + +static inline int islower(int c) +{ + return c >= 'a' && c <= 'z'; +} + +static inline int toupper(int c) +{ + if (islower(c)) + c -= 'a'-'A'; + return c; +} + +static int skip_atoi(const char **s) +{ + int i=0; + + while (isdigit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} + +#define ZEROPAD 1 /* pad with zero */ +#define SIGN 2 /* unsigned/signed long */ +#define PLUS 4 /* show plus */ +#define SPACE 8 /* space if plus */ +#define LEFT 16 /* left justified */ +#define SPECIAL 32 /* 0x */ +#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ + +#define do_div(n,base) ({ \ +int __res; \ +__res = ((unsigned long) n) % (unsigned) base; \ +n = ((unsigned long) n) / (unsigned) base; \ +__res; }) + +static char * number(char * str, long num, int base, int size, int precision + ,int type) +{ + char c,sign,tmp[66]; + const char *digits="0123456789abcdefghijklmnopqrstuvwxyz"; + int i; + + if (type & LARGE) + digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + if (type & LEFT) + type &= ~ZEROPAD; + if (base < 2 || base > 36) + return 0; + c = (type & ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & SIGN) { + if (num < 0) { + sign = '-'; + num = -num; + size--; + } else if (type & PLUS) { + sign = '+'; + size--; + } else if (type & SPACE) { + sign = ' '; + size--; + } + } + if (type & SPECIAL) { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } + i = 0; + if (num == 0) + tmp[i++]='0'; + else while (num != 0) + tmp[i++] = digits[do_div(num,base)]; + if (i > precision) + precision = i; + size -= precision; + if (!(type&(ZEROPAD+LEFT))) + while(size-->0) + *str++ = ' '; + if (sign) + *str++ = sign; + if (type & SPECIAL) { + if (base==8) + *str++ = '0'; + else if (base==16) { + *str++ = '0'; + *str++ = digits[33]; + } + } + if (!(type & LEFT)) + while (size-- > 0) + *str++ = c; + while (i < precision--) + *str++ = '0'; + while (i-- > 0) + *str++ = tmp[i]; + while (size-- > 0) + *str++ = ' '; + return str; +} + +int vsprintf(char *buf, const char *fmt, va_list args) +{ + int len; + unsigned long num; + int i, base; + char * str; + const char *s; + + int flags; /* flags to number() */ + + int field_width; /* width of output field */ + int precision; /* min. # of digits for integers; max + number of chars for from string */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + + for(str=buf ; *fmt ; ++fmt) + { + if (*fmt != '%') { + *str++ = *fmt; + continue; + } + + /* process flags */ + flags = 0; + repeat: + ++fmt; /* this also skips first '%' */ + switch (*fmt) { + case '-': flags |= LEFT; goto repeat; + case '+': flags |= PLUS; goto repeat; + case ' ': flags |= SPACE; goto repeat; + case '#': flags |= SPECIAL; goto repeat; + case '0': flags |= ZEROPAD; goto repeat; + } + + /* get field width */ + field_width = -1; + if (isdigit(*fmt)) + field_width = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) { + field_width = -field_width; + flags |= LEFT; + } + } + + /* get the precision */ + precision = -1; + if (*fmt == '.') { + ++fmt; + if (isdigit(*fmt)) + precision = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) + precision = 0; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { + qualifier = *fmt; + ++fmt; + } + + /* default base */ + base = 10; + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) + while (--field_width > 0) + *str++ = ' '; + *str++ = (unsigned char) va_arg(args, int); + while (--field_width > 0) + *str++ = ' '; + continue; + + case 's': + s = va_arg(args, char *); + //if (!s) + // s = ""; + + len = strnlen(s, precision); + + if (!(flags & LEFT)) + while (len < field_width--) + *str++ = ' '; + for (i = 0; i < len; ++i) + *str++ = *s++; + while (len < field_width--) + *str++ = ' '; + continue; + + case 'p': + if (field_width == -1) { + field_width = 2*sizeof(void *); + flags |= ZEROPAD; + } + str = number(str, + (unsigned long) va_arg(args, void *), 16, + field_width, precision, flags); + continue; + + + case 'n': + if (qualifier == 'l') { + long * ip = va_arg(args, long *); + *ip = (str - buf); + } else { + int * ip = va_arg(args, int *); + *ip = (str - buf); + } + continue; + + case '%': + *str++ = '%'; + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base = 8; + break; + + case 'X': + flags |= LARGE; + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + case 'u': + break; + + default: + *str++ = '%'; + if (*fmt) + *str++ = *fmt; + else + --fmt; + continue; + } + if (qualifier == 'l') + num = va_arg(args, unsigned long); + else if (qualifier == 'h') { + num = (unsigned short) va_arg(args, int); + if (flags & SIGN) + num = (short) num; + } else if (flags & SIGN) + num = va_arg(args, int); + else + num = va_arg(args, unsigned int); + str = number(str, num, base, field_width, precision, flags); + } + *str = '\0'; + return str-buf; +} +int sprintf( char *astr, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i = vsprintf(astr, fmt, args); + va_end(args); + + return i; +} + +#define EXI 0xD806814 + +void EXISendByte( char byte ) +{ + +loop: + *(vu32*)EXI = 0xD0; + *(vu32*)(EXI+0x10) = 0xB0000000 | (byte<<20); + *(vu32*)(EXI+0x0C) = 0x19; + + while( *(vu32*)(EXI+0x0C)&1 ); + + u32 loop = *(vu32*)(EXI+0x10)&0x4000000; + + *(vu32*)EXI = 0; + + if( !loop ) + goto loop; + + return; +} +void GeckoSendBuffer( char *buffer ) +{ + int i = 0; + while( buffer[i] != '\0' ) + { + EXISendByte( buffer[i] ); + ++i; + } + + return; +} + +extern FIL Log; + +static char buffer[128] ALIGNED(32); +int dbgprintf( const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vsprintf(buffer, fmt, args); + va_end(args); + + //u32 cokie = read32( 0x0D800070 ); + //set32(0x0D800070,1); + + if( read32( 0x0D800070 ) & 1 ) + { + GeckoSendBuffer( buffer ); + }/* else { + u32 read; + u32 fres = f_open( &Log, "/dm.log", FA_READ|FA_WRITE|FA_OPEN_ALWAYS ); + if( fres != FR_OK ) + { + write32( 0x0D800070, 1 ); + dbgprintf("f_open():%d\n", fres ); + } + + f_lseek( &Log, Log.fsize ); + f_write( &Log, buffer, strlen(buffer), &read ); + f_close( &Log ); + }*/ + + //write32( 0x0D800070, cokie ); + + return 1; +} + + +static char ascii(char s) +{ + if(s < 0x20) return '.'; + if(s > 0x7E) return '.'; + return s; +} + +void hexdump(void *d, int len) +{ + u8 *data; + int i, off; + data = (u8*)d; + for (off=0; off=len) dbgprintf(" "); + else dbgprintf("%02x ",data[off+i]); + + dbgprintf(" "); + for(i=0; i<16; i++) + if((i+off)>=len) dbgprintf(" "); + else dbgprintf("%c",ascii(data[off+i])); + dbgprintf("\n"); + } +} + +//void fatal(const char *fmt, ...) +//{ +// va_list args; +// char buffer[1024]; +// int i; +// +// va_start(args, fmt); +// i = vsprintf(buffer, fmt, args); +// va_end(args); +// OSReport(buffer); +// for (;;); +//} +// + + diff --git a/vsprintf.h b/vsprintf.h new file mode 100644 index 0000000..78e8b2d --- /dev/null +++ b/vsprintf.h @@ -0,0 +1,8 @@ +#include +#include "string.h" + +char * strstr ( const char *str1, const char *str2); +int vsprintf(char *buf, const char *fmt, va_list args); +int _sprintf( char *buf, const char *fmt, ... ); +int dbgprintf( const char *fmt, ...); +void hexdump(void *d, int len);