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);