diff --git a/arm9/source/gamecart/card_spi.c b/arm9/source/gamecart/card_spi.c new file mode 100644 index 0000000..ffb30a3 --- /dev/null +++ b/arm9/source/gamecart/card_spi.c @@ -0,0 +1,522 @@ +/* + * This file is based on SPI.cpp from TWLSaveTool. Its copyright notice is + * reproduced below. + * + * Copyright (C) 2015-2016 TuxSH + * + * TWLSaveTool 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, see + */ + +#include "card_spi.h" +#include +#include "timer.h" + +#define SPI_CMD_RDSR 5 +#define SPI_CMD_WREN 6 + +#define SPI_512B_EEPROM_CMD_WRLO 2 +#define SPI_512B_EEPROM_CMD_WRHI 10 +#define SPI_512B_EEPROM_CMD_RDLO 3 +#define SPI_512B_EEPROM_CMD_RDHI 11 + +#define SPI_EEPROM_CMD_WRITE 2 + +#define SPI_CMD_READ 3 + +#define SPI_CMD_PP 2 +#define SPI_FLASH_CMD_PW 10 +#define SPI_FLASH_CMD_RDID 0x9f +#define SPI_FLASH_CMD_SE 0xd8 +#define SPI_FLASH_CMD_PE 0xdb +#define SPI_FLASH_CMD_MXIC_SE 0x20 + +#define SPI_FLG_WIP 1 +#define SPI_FLG_WEL 2 + +// declarations for actual implementations +int CardSPIEnableWriting_512B(CardSPIType type); +int CardSPIEnableWriting_regular(CardSPIType type); +int CardSPIReadSaveData_9bit(CardSPIType type, u32 offset, void* data, u32 size); +int CardSPIReadSaveData_16bit(CardSPIType type, u32 offset, void* data, u32 size); +int CardSPIReadSaveData_24bit(CardSPIType type, u32 offset, void* data, u32 size); +int CardSPIWriteSaveData_9bit(CardSPIType type, u32 offset, const void* data, u32 size); +int CardSPIWriteSaveData_16bit(CardSPIType type, u32 offset, const void* data, u32 size); +int CardSPIWriteSaveData_24bit_write(CardSPIType type, u32 offset, const void* data, u32 size); +int CardSPIWriteSaveData_24bit_erase_program(CardSPIType type, u32 offset, const void* data, u32 size); +int CardSPIEraseSector_emulated(CardSPIType type, u32 offset); +int CardSPIEraseSector_real(CardSPIType type, u32 offset); + +const CardSPITypeData EEPROM_512B_ = { CardSPIEnableWriting_512B, CardSPIReadSaveData_9bit, CardSPIWriteSaveData_9bit, CardSPIEraseSector_emulated, 0xffffff, 1 << 9, 16, 16, 16, false, 0, 0, 0 }; + +const CardSPITypeData EEPROM_STD_DUMMY = { CardSPIEnableWriting_regular, CardSPIReadSaveData_16bit, CardSPIWriteSaveData_16bit, CardSPIEraseSector_emulated, 0xffffff, UINT32_MAX, 1, 1, 1, false, SPI_EEPROM_CMD_WRITE, 0, 0 }; +const CardSPITypeData EEPROMTypes[] = { + { CardSPIEnableWriting_regular, CardSPIReadSaveData_16bit, CardSPIWriteSaveData_16bit, CardSPIEraseSector_emulated, 0xffffff, 1 << 13, 32, 32, 32, false, SPI_EEPROM_CMD_WRITE, 0, 0}, // EEPROM 8 KB + { CardSPIEnableWriting_regular, CardSPIReadSaveData_16bit, CardSPIWriteSaveData_16bit, CardSPIEraseSector_emulated, 0xffffff, 1 << 16, 128, 128, 128, false, SPI_EEPROM_CMD_WRITE, 0, 0}, // EEPROM 64 KB + { CardSPIEnableWriting_regular, CardSPIReadSaveData_24bit, CardSPIWriteSaveData_24bit_write, CardSPIEraseSector_emulated, 0xffffff, 1 << 17, 256, 256, 256, false, SPI_EEPROM_CMD_WRITE, 0, 0}, // EEPROM 128 KB +}; + +const CardSPITypeData FLASH_STD_DUMMY = { NULL, CardSPIReadSaveData_24bit, NULL, NULL, 0x0, 0, 0, 0, 0, false, 0, 0, 0 }; +const CardSPITypeData FlashStdTypes[] = { + // NTR/TWL + { CardSPIEnableWriting_regular, CardSPIReadSaveData_24bit, CardSPIWriteSaveData_24bit_write, CardSPIEraseSector_real, 0x204012, 1 << 18, 65536, 256, 256, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_SE }, + { CardSPIEnableWriting_regular, CardSPIReadSaveData_24bit, CardSPIWriteSaveData_24bit_erase_program, CardSPIEraseSector_real, 0x621600, 1 << 18, 65536, 256, 65536, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_SE }, + { CardSPIEnableWriting_regular, CardSPIReadSaveData_24bit, CardSPIWriteSaveData_24bit_write, CardSPIEraseSector_real, 0x204013, 1 << 19, 65536, 256, 256, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_SE }, + { CardSPIEnableWriting_regular, CardSPIReadSaveData_24bit, CardSPIWriteSaveData_24bit_write, CardSPIEraseSector_real, 0x621100, 1 << 19, 65536, 256, 256, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_SE }, + { CardSPIEnableWriting_regular, CardSPIReadSaveData_24bit, CardSPIWriteSaveData_24bit_write, CardSPIEraseSector_real, 0x204014, 1 << 20, 65536, 256, 256, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_SE }, + // Untested (but pretty safe bet), for Art Academy + { CardSPIEnableWriting_regular, CardSPIReadSaveData_24bit, CardSPIWriteSaveData_24bit_erase_program, CardSPIEraseSector_real, 0x202017, 1 << 23, 65536, 32, 65536, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_SE }, + { CardSPIEnableWriting_regular, CardSPIReadSaveData_24bit, CardSPIWriteSaveData_24bit_erase_program, CardSPIEraseSector_real, 0x204017, 1 << 23, 65536, 32, 65536, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_SE }, + // CTR + { CardSPIEnableWriting_regular, CardSPIReadSaveData_24bit, CardSPIWriteSaveData_24bit_erase_program, CardSPIEraseSector_real, 0xC22210, 1 << 16, 4096, 32, 4096, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_MXIC_SE }, + { CardSPIEnableWriting_regular, CardSPIReadSaveData_24bit, CardSPIWriteSaveData_24bit_erase_program, CardSPIEraseSector_real, 0xC22211, 1 << 17, 4096, 32, 4096, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_MXIC_SE }, + { CardSPIEnableWriting_regular, CardSPIReadSaveData_24bit, CardSPIWriteSaveData_24bit_erase_program, CardSPIEraseSector_real, 0xC22212, 1 << 18, 4096, 32, 4096, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_MXIC_SE }, + { CardSPIEnableWriting_regular, CardSPIReadSaveData_24bit, CardSPIWriteSaveData_24bit_erase_program, CardSPIEraseSector_real, 0xC22213, 1 << 19, 4096, 32, 4096, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_MXIC_SE }, + { CardSPIEnableWriting_regular, CardSPIReadSaveData_24bit, CardSPIWriteSaveData_24bit_erase_program, CardSPIEraseSector_real, 0xC22214, 1 << 20, 4096, 32, 4096, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_MXIC_SE }, + { CardSPIEnableWriting_regular, CardSPIReadSaveData_24bit, CardSPIWriteSaveData_24bit_erase_program, CardSPIEraseSector_real, 0xC22215, 1 << 21, 4096, 32, 4096, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_MXIC_SE }, + { CardSPIEnableWriting_regular, CardSPIReadSaveData_24bit, CardSPIWriteSaveData_24bit_erase_program, CardSPIEraseSector_real, 0xC22216, 1 << 22, 4096, 32, 4096, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_MXIC_SE }, + { CardSPIEnableWriting_regular, CardSPIReadSaveData_24bit, CardSPIWriteSaveData_24bit_erase_program, CardSPIEraseSector_real, 0xC22217, 1 << 23, 4096, 32, 4096, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_MXIC_SE }, +}; + +const CardSPITypeData FLASH_INFRARED_DUMMY = { NULL, CardSPIReadSaveData_24bit, NULL, NULL, 0x0, 0, 0, 0, 0, true, 0, 0, 0 }; +const CardSPITypeData FlashInfraredTypes[] = { + // NTR/TWL + { CardSPIEnableWriting_regular, CardSPIReadSaveData_24bit, CardSPIWriteSaveData_24bit_write, CardSPIEraseSector_real, 0x204012, 1 << 18, 65536, 256, 256, true, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_SE }, + { CardSPIEnableWriting_regular, CardSPIReadSaveData_24bit, CardSPIWriteSaveData_24bit_erase_program, CardSPIEraseSector_real, 0x621600, 1 << 18, 65536, 256, 65536, true, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_SE }, + { CardSPIEnableWriting_regular, CardSPIReadSaveData_24bit, CardSPIWriteSaveData_24bit_write, CardSPIEraseSector_real, 0x204013, 1 << 19, 65536, 256, 256, true, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_SE }, + { CardSPIEnableWriting_regular, CardSPIReadSaveData_24bit, CardSPIWriteSaveData_24bit_write, CardSPIEraseSector_real, 0x621100, 1 << 19, 65536, 256, 256, true, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_SE }, +}; + +const CardSPIType EEPROM_512B = &EEPROM_512B_; + +const CardSPIType EEPROM_8KB = EEPROMTypes + 0; +const CardSPIType EEPROM_64KB = EEPROMTypes + 1; +const CardSPIType EEPROM_128KB = EEPROMTypes + 2; + +const CardSPIType FLASH_256KB_1 = FlashStdTypes + 0; +const CardSPIType FLASH_256KB_2 = FlashStdTypes + 1; +const CardSPIType FLASH_512KB_1 = FlashStdTypes + 2; +const CardSPIType FLASH_512KB_2 = FlashStdTypes + 3; +const CardSPIType FLASH_1MB = FlashStdTypes + 4; +const CardSPIType FLASH_8MB_1 = FlashStdTypes + 5; +const CardSPIType FLASH_8MB_2 = FlashStdTypes + 6; + +const CardSPIType FLASH_64KB_CTR = FlashStdTypes + 7; +const CardSPIType FLASH_128KB_CTR = FlashStdTypes + 8; +const CardSPIType FLASH_256KB_CTR = FlashStdTypes + 9; +const CardSPIType FLASH_512KB_CTR = FlashStdTypes + 10; +const CardSPIType FLASH_1MB_CTR = FlashStdTypes + 11; +const CardSPIType FLASH_2MB_CTR = FlashStdTypes + 12; +const CardSPIType FLASH_4MB_CTR = FlashStdTypes + 13; +const CardSPIType FLASH_8MB_CTR = FlashStdTypes + 14; + +const CardSPIType FLASH_256KB_1_INFRARED = FlashInfraredTypes + 0; +const CardSPIType FLASH_256KB_2_INFRARED = FlashInfraredTypes + 1; +const CardSPIType FLASH_512KB_1_INFRARED = FlashInfraredTypes + 2; +const CardSPIType FLASH_512KB_2_INFRARED = FlashInfraredTypes + 3; + +#define REG_CFG9_CARDCTL *((vu16*)0x1000000C) +#define CARDCTL_SPICARD (1u<<8) + +int CardSPIWriteRead(CardSPIType type, const void* cmd, u32 cmdSize, void* answer, u32 answerSize, const void* data, u32 dataSize) { + u32 headerFooterVal = 0; + + REG_CFG9_CARDCTL |= CARDCTL_SPICARD; + + if (type->infrared) { + SPI_XferInfo irXfer = { &headerFooterVal, 1, false }; + SPI_DoXfer(SPI_DEV_CART_IR, &irXfer, 1, false); + } + + SPI_XferInfo transfers[3] = { + { (u8*) cmd, cmdSize, false }, + { answer, answerSize, true }, + { (u8*) data, dataSize, false }, + }; + SPI_DoXfer(SPI_DEV_CART_FLASH, transfers, 3, true); + + REG_CFG9_CARDCTL &= ~CARDCTL_SPICARD; + + return 0; +} + +int CardSPIWaitWriteEnd(CardSPIType type) { + u8 cmd = SPI_CMD_RDSR, statusReg = 0; + int res = 0; + u64 time_start = timer_start(); + + do{ + res = CardSPIWriteRead(type, &cmd, 1, &statusReg, 1, 0, 0); + if(res) return res; + if(timer_msec(time_start) > 1000) return 1; + } while(statusReg & SPI_FLG_WIP); + + return 0; +} + +int CardSPIEnableWriting_512B(CardSPIType type) { + u8 cmd = SPI_CMD_WREN; + return CardSPIWriteRead(type, &cmd, 1, NULL, 0, 0, 0); +} + +int CardSPIEnableWriting_regular(CardSPIType type) { + u8 cmd = SPI_CMD_WREN, statusReg = 0; + int res = CardSPIWriteRead(type, &cmd, 1, NULL, 0, 0, 0); + + if(res) return res; + cmd = SPI_CMD_RDSR; + + do{ + res = CardSPIWriteRead(type, &cmd, 1, &statusReg, 1, 0, 0); + if(res) return res; + } while(statusReg & ~SPI_FLG_WEL); + + return 0; +} + +int CardSPIEnableWriting(CardSPIType type) { + if(type == NO_CHIP) return 1; + return type->enableWriting(type); +} + +int _SPIWriteTransaction(CardSPIType type, void* cmd, u32 cmdSize, const void* data, u32 dataSize) { + int res; + if( (res = CardSPIEnableWriting(type)) ) return res; + if( (res = CardSPIWriteRead(type, cmd, cmdSize, NULL, 0, (void*) ((u8*) data), dataSize)) ) return res; + return CardSPIWaitWriteEnd(type); +} + +int CardSPIReadJEDECIDAndStatusReg(CardSPIType type, u32* id, u8* statusReg) { + u8 cmd = SPI_FLASH_CMD_RDID; + u8 reg = 0; + u8 idbuf[3] = { 0 }; + u32 id_ = 0; + int res = CardSPIWaitWriteEnd(type); + if(res) return res; + + if((res = CardSPIWriteRead(type, &cmd, 1, idbuf, 3, 0, 0))) return res; + + id_ = (idbuf[0] << 16) | (idbuf[1] << 8) | idbuf[2]; + cmd = SPI_CMD_RDSR; + + if((res = CardSPIWriteRead(type, &cmd, 1, ®, 1, 0, 0))) return res; + + if(id) *id = id_; + if(statusReg) *statusReg = reg; + + return 0; +} + +u32 CardSPIGetPageSize(CardSPIType type) { + if(type == NO_CHIP) return 0; + return type->pageSize; +} + +u32 CardSPIGetEraseSize(CardSPIType type) { + if(type == NO_CHIP) return 0; + return type->eraseSize; +} + +u32 CardSPIGetCapacity(CardSPIType type) { + if(type == NO_CHIP) return 0; + return type->capacity; +} + +int CardSPIWriteSaveData_9bit(CardSPIType type, u32 offset, const void* data, u32 size) { + u8 cmd[2] = { (offset >= 0x100) ? SPI_512B_EEPROM_CMD_WRHI : SPI_512B_EEPROM_CMD_WRLO, (u8) offset }; + + return _SPIWriteTransaction(type, cmd, 2, (void*) ((u8*) data), size); +} + +int CardSPIWriteSaveData_16bit(CardSPIType type, u32 offset, const void* data, u32 size) { + u8 cmd[3] = { type->writeCommand, (u8)(offset >> 8), (u8) offset }; + + return _SPIWriteTransaction(type, cmd, 3, (void*) ((u8*) data), size); +} + +int CardSPIWriteSaveData_24bit_write(CardSPIType type, u32 offset, const void* data, u32 size) { + u8 cmd[4] = { type->writeCommand, (u8)(offset >> 16), (u8)(offset >> 8), (u8) offset }; + + return _SPIWriteTransaction(type, cmd, 4, (void*) ((u8*) data), size); +} + +int CardSPIWriteSaveData_24bit_erase_program(CardSPIType type, u32 offset, const void* data, u32 size) { + u8 cmd[4] = { type->programCommand }; + const u32 pageSize = CardSPIGetPageSize(type); + const u32 eraseSize = CardSPIGetEraseSize(type); + int res; + + u8 *newData = NULL; + if(offset % eraseSize || size < eraseSize) { + u32 sectorStart = (offset / eraseSize) * eraseSize; + newData = malloc(eraseSize); + if(!newData) return 1; + if( (res = CardSPIReadSaveData(type, sectorStart, newData, eraseSize)) ) { + free(newData); + return res; + } + memcpy(newData + (offset % eraseSize), data, size); + data = newData; + offset = sectorStart; + } + + if( (res = CardSPIEraseSector(type, offset)) ) { + free(newData); + return res; + } + + for(u32 pos = offset; pos < offset + eraseSize; pos += pageSize) { + cmd[1] = (u8)(pos >> 16); + cmd[2] = (u8)(pos >> 8); + cmd[3] = (u8) pos; + if( (res = _SPIWriteTransaction(type, cmd, 4, (void*) ((u8*) data - offset + pos), pageSize)) ) { + free(newData); + return res; + } + } + + free(newData); + return 0; +} + +int CardSPIWriteSaveData(CardSPIType type, u32 offset, const void* data, u32 size) { + if(type == NO_CHIP) return 1; + + if(size == 0) return 0; + size = min(size, CardSPIGetCapacity(type) - offset); + u32 end = offset + size; + u32 pos = offset; + u32 writeSize = type->writeSize; + if(writeSize == 0) return 0xC8E13404; + + int res = CardSPIWaitWriteEnd(type); + if(res) return res; + + while(pos < end) { + u32 remaining = end - pos; + u32 nb = writeSize - (pos % writeSize); + + u32 dataSize = (remaining < nb) ? remaining : nb; + + if( (res = type->writeSaveData(type, pos, (void*) ((u8*) data - offset + pos), dataSize)) ) return res; + + pos = ((pos / writeSize) + 1) * writeSize; // truncate + } + + return 0; +} + +int CardSPIReadSaveData_9bit(CardSPIType type, u32 pos, void* data, u32 size) { + u8 cmd[4]; + u32 cmdSize = 2; + + u32 end = pos + size; + + u32 read = 0; + if(pos < 0x100) { + u32 len = 0x100 - pos; + cmd[0] = SPI_512B_EEPROM_CMD_RDLO; + cmd[1] = (u8) pos; + + int res = CardSPIWriteRead(type, cmd, cmdSize, data, len, NULL, 0); + if(res) return res; + + read += len; + } + + if(end >= 0x100) { + u32 len = end - 0x100; + + cmd[0] = SPI_512B_EEPROM_CMD_RDHI; + cmd[1] = (u8)(pos + read); + + int res = CardSPIWriteRead(type, cmd, cmdSize, (void*)((u8*)data + read), len, NULL, 0); + + if(res) return res; + } + + return 0; +} + +int CardSPIReadSaveData_16bit(CardSPIType type, u32 offset, void* data, u32 size) { + u8 cmd[3] = { SPI_CMD_READ, (u8)(offset >> 8), (u8) offset }; + + return CardSPIWriteRead(type, cmd, 3, data, size, NULL, 0); +} + +int CardSPIReadSaveData_24bit(CardSPIType type, u32 offset, void* data, u32 size) { + u8 cmd[4] = { SPI_CMD_READ, (u8)(offset >> 16), (u8)(offset >> 8), (u8) offset }; + + return CardSPIWriteRead(type, cmd, 4, data, size, NULL, 0); +} + +int CardSPIReadSaveData(CardSPIType type, u32 offset, void* data, u32 size) { + if(type == NO_CHIP) return 1; + + if(size == 0) return 0; + + int res = CardSPIWaitWriteEnd(type); + if(res) return res; + + size = (size <= CardSPIGetCapacity(type) - offset) ? size : CardSPIGetCapacity(type) - offset; + + return type->readSaveData(type, offset, data, size); +} + +int CardSPIEraseSector_emulated(CardSPIType type, u32 offset) { + u32 blockSize = CardSPIGetEraseSize(type); + u8 *fill_buf = malloc(CardSPIGetEraseSize(type)); + if (!fill_buf) return 1; + memset(fill_buf, 0xff, blockSize); + offset = (offset / blockSize) * blockSize; + + int res = CardSPIWriteSaveData(type, offset, fill_buf, blockSize); + free(fill_buf); + return res; +} + +int CardSPIEraseSector_real(CardSPIType type, u32 offset) { + u8 cmd[4] = { type->eraseCommand, (u8)(offset >> 16), (u8)(offset >> 8), (u8) offset }; + + int res = CardSPIWaitWriteEnd(type); + if(res) return res; + + return _SPIWriteTransaction(type, cmd, 4, NULL, 0); +} + + +int CardSPIEraseSector(CardSPIType type, u32 offset) { + if(type == NO_CHIP) return 1; + return type->eraseSector(type, offset); +} + + +// The following routine use code from savegame-manager: + +/* + * savegame_manager: a tool to backup and restore savegames from Nintendo + * DS cartridges. Nintendo DS and all derivative names are trademarks + * by Nintendo. EZFlash 3-in-1 is a trademark by EZFlash. + * + * auxspi.cpp: A thin reimplementation of the AUXSPI protocol + * (high level functions) + * + * Copyright (C) Pokedoc (2010) + */ +/* + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +int _SPIIsDataMirrored(CardSPIType type, int size, bool* mirrored) { + u32 offset0 = (size-1); // n KB + u32 offset1 = (2*size-1); // 2n KB + + u8 buf1; // +0k data read -> write + u8 buf2; // +n k data read -> read + u8 buf3; // +0k ~data write + u8 buf4; // +n k data new comp buf2 + + int res; + + if( (res = CardSPIReadSaveData(type, offset0, &buf1, 1)) ) return res; + if( (res = CardSPIReadSaveData(type, offset1, &buf2, 1)) ) return res; + buf3=~buf1; + if( (res = CardSPIWriteSaveData(type, offset0, &buf3, 1)) ) return res; + if( (res = CardSPIReadSaveData(type, offset1, &buf4, 1)) ) return res; + if( (res = CardSPIWriteSaveData(type, offset0, &buf1, 1)) ) return res; + + *mirrored = buf2 != buf4; + return 0; +} + +int CardSPIGetCardSPIType(CardSPIType* type, int infrared) { + u8 sr = 0; + u32 jedec = 0; + u32 tries = 0; + CardSPIType t = (infrared == 1) ? &FLASH_INFRARED_DUMMY : &FLASH_STD_DUMMY; + int res; + + u32 maxTries = (infrared == -1) ? 2 : 1; // note: infrared = -1 fails 1/3 of the time + while(tries < maxTries){ + res = CardSPIReadJEDECIDAndStatusReg(t, &jedec, &sr); // dummy + if(res) return res; + + if ((sr & 0xfd) == 0x00 && (jedec != 0x00ffffff)) { break; } + if ((sr & 0xfd) == 0xF0 && (jedec == 0x00ffffff)) { t = EEPROM_512B; break; } + if ((sr & 0xfd) == 0x00 && (jedec == 0x00ffffff)) { t = &EEPROM_STD_DUMMY; break; } + + ++tries; + t = &FLASH_INFRARED_DUMMY; + } + + if(t == EEPROM_512B) { *type = t; return 0; } + else if(t == &EEPROM_STD_DUMMY) { + bool mirrored = false; + size_t i; + + for(i = 0; i < sizeof(EEPROMTypes) / sizeof(CardSPITypeData) - 1; i++) { + if( (res = _SPIIsDataMirrored(t, CardSPIGetCapacity(EEPROMTypes + i), &mirrored)) ) return res; + if(mirrored) { + *type = EEPROMTypes + i; + return 0; + } + } + *type = EEPROMTypes + i; + return 0; + } + + else if(t == &FLASH_INFRARED_DUMMY) { + size_t i; + + if(infrared == 0) *type = NO_CHIP; // did anything go wrong? + + for(i = 0; i < sizeof(FlashInfraredTypes) / sizeof(CardSPITypeData); i++) { + if(FlashInfraredTypes[i].jedecId == jedec) { + *type = FlashInfraredTypes + i; + return 0; + } + } + + *type = NO_CHIP; + return 0; + } + + else { + size_t i; + + if(infrared == 1) *type = NO_CHIP; // did anything go wrong? + + for(i = 0; i < sizeof(FlashStdTypes) / sizeof(CardSPITypeData); i++) { + if(FlashStdTypes[i].jedecId == jedec) { + *type = FlashStdTypes + i; + return 0; + } + } + + *type = NO_CHIP; + return 0; + } +} diff --git a/arm9/source/gamecart/card_spi.h b/arm9/source/gamecart/card_spi.h new file mode 100644 index 0000000..496f47d --- /dev/null +++ b/arm9/source/gamecart/card_spi.h @@ -0,0 +1,95 @@ +/* + * This file is based on SPI.h from TWLSaveTool. Its copyright notice is + * reproduced below. + * + * Copyright (C) 2015-2016 TuxSH + * + * TWLSaveTool 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, see + */ + +#pragma once +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct CardSPITypeData CardSPITypeData; + +typedef const CardSPITypeData * CardSPIType; + +struct CardSPITypeData { + int (*enableWriting) (CardSPIType type); + int (*readSaveData) (CardSPIType type, u32 offset, void* data, u32 size); + int (*writeSaveData) (CardSPIType type, u32 offset, const void* data, u32 size); + int (*eraseSector) (CardSPIType type, u32 offset); + u32 jedecId; + u32 capacity; + u32 eraseSize; + u32 pageSize; + u32 writeSize; + bool infrared; + u8 writeCommand; + u8 programCommand; + u8 eraseCommand; +}; + +#define NO_CHIP NULL + +const CardSPIType EEPROM_512B; + +const CardSPIType EEPROM_8KB; +const CardSPIType EEPROM_64KB; +const CardSPIType EEPROM_128KB; + +const CardSPIType FLASH_256KB_1; +const CardSPIType FLASH_256KB_2; +const CardSPIType FLASH_512KB_1; +const CardSPIType FLASH_512KB_2; +const CardSPIType FLASH_1MB; +const CardSPIType FLASH_8MB_1; // <- can't restore savegames, and maybe not read them atm +const CardSPIType FLASH_8MB_2; // we are also unsure about the ID for this + +const CardSPIType FLASH_64KB_CTR; // I am extrapolating from the dataheets, only a few of these have been observed in the wild +const CardSPIType FLASH_128KB_CTR; // Most common, including Ocarina of time 3D +const CardSPIType FLASH_256KB_CTR; +const CardSPIType FLASH_512KB_CTR; // Also common, including Detective Pikachu +const CardSPIType FLASH_1MB_CTR; // For example Pokemon Ultra Sun +const CardSPIType FLASH_2MB_CTR; +const CardSPIType FLASH_4MB_CTR; +const CardSPIType FLASH_8MB_CTR; + +const CardSPIType FLASH_256KB_1_INFRARED; // AFAIK, only "Active Health with Carol Vorderman" has such a flash save memory +const CardSPIType FLASH_256KB_2_INFRARED; +const CardSPIType FLASH_512KB_1_INFRARED; +const CardSPIType FLASH_512KB_2_INFRARED; + +int CardSPIWriteRead(CardSPIType type, const void* cmd, u32 cmdSize, void* answer, u32 answerSize, const void* data, u32 dataSize); +int CardSPIWaitWriteEnd(CardSPIType type); +int CardSPIEnableWriting(CardSPIType type); +int CardSPIReadJEDECIDAndStatusReg(CardSPIType type, u32* id, u8* statusReg); +int CardSPIGetCardSPIType(CardSPIType* type, int infrared); +u32 CardSPIGetPageSize(CardSPIType type); +u32 CardSPIGetCapacity(CardSPIType type); +u32 CardSPIGetEraseSize(CardSPIType type); + +int CardSPIWriteSaveData(CardSPIType type, u32 offset, const void* data, u32 size); +int CardSPIReadSaveData(CardSPIType type, u32 offset, void* data, u32 size); + +int CardSPIEraseSector(CardSPIType type, u32 offset); +int CardSPIErase(CardSPIType type); + +#ifdef __cplusplus +} +#endif diff --git a/arm9/source/gamecart/gamecart.c b/arm9/source/gamecart/gamecart.c index 293a650..139a810 100644 --- a/arm9/source/gamecart/gamecart.c +++ b/arm9/source/gamecart/gamecart.c @@ -2,8 +2,7 @@ #include "protocol.h" #include "command_ctr.h" #include "command_ntr.h" -#include "card_eeprom.h" -#include "spi.h" +#include "card_spi.h" #include "nds.h" #include "ncch.h" #include "ncsd.h" @@ -25,7 +24,7 @@ typedef struct { u64 cart_size; u64 data_size; u32 save_size; - CardType save_type; + CardSPIType save_type; u32 unused_offset; } PACKED_ALIGN(16) CartDataCtr; @@ -41,7 +40,7 @@ typedef struct { u64 cart_size; u64 data_size; u32 save_size; - CardType save_type; + CardSPIType save_type; u32 arm9i_rom_offset; } PACKED_ALIGN(16) CartDataNtrTwl; @@ -101,10 +100,10 @@ u32 InitCartRead(CartData* cdata) { // save data u32 card2_offset = getle32(cdata->header + 0x200); - if ((card2_offset != 0xFFFFFFFF) || (SPIGetCardType(&(cdata->save_type), 0) != 0)) { + if ((card2_offset != 0xFFFFFFFF) || (CardSPIGetCardSPIType(&(cdata->save_type), 0) != 0)) { cdata->save_type = NO_CHIP; } - cdata->save_size = SPIGetCapacity(cdata->save_type); + cdata->save_size = CardSPIGetCapacity(cdata->save_type); } else { // NTR/TWL cartridges // NTR header TwlHeader* nds_header = (void*)cdata->header; @@ -136,10 +135,10 @@ u32 InitCartRead(CartData* cdata) { // save data u32 infrared = (*(nds_header->game_code) == 'I') ? 1 : 0; - if (SPIGetCardType(&(cdata->save_type), infrared) != 0) { + if (CardSPIGetCardSPIType(&(cdata->save_type), infrared) != 0) { cdata->save_type = NO_CHIP; } - cdata->save_size = SPIGetCapacity(cdata->save_type); + cdata->save_size = CardSPIGetCapacity(cdata->save_type); } return 0; } @@ -244,13 +243,13 @@ u32 ReadCartPrivateHeader(void* buffer, u64 offset, u64 count, CartData* cdata) u32 ReadCartSave(u8* buffer, u64 offset, u64 count, CartData* cdata) { if (offset >= cdata->save_size) return 1; if (offset + count > cdata->save_size) count = cdata->save_size - offset; - return (SPIReadSaveData((CardType) cdata->save_type, offset, buffer, count) == 0) ? 0 : 1; + return (CardSPIReadSaveData((CardSPIType) cdata->save_type, offset, buffer, count) == 0) ? 0 : 1; } u32 WriteCartSave(const u8* buffer, u64 offset, u64 count, CartData* cdata) { if (offset >= cdata->save_size) return 1; if (offset + count > cdata->save_size) count = cdata->save_size - offset; - return (SPIWriteSaveData((CardType) cdata->save_type, offset, buffer, count) == 0) ? 0 : 1; + return (CardSPIWriteSaveData((CardSPIType) cdata->save_type, offset, buffer, count) == 0) ? 0 : 1; } u32 ReadCartSaveJedecId(u8* buffer, u64 offset, u64 count, CartData* cdata) { @@ -259,7 +258,7 @@ u32 ReadCartSaveJedecId(u8* buffer, u64 offset, u64 count, CartData* cdata) { u8 sReg; if (offset >= JEDECID_AND_SREG_SIZE) return 1; if (offset + count > JEDECID_AND_SREG_SIZE) count = JEDECID_AND_SREG_SIZE - offset; - SPIReadJEDECIDAndStatusReg((CardType) cdata->save_type, &id, &sReg); + CardSPIReadJEDECIDAndStatusReg((CardSPIType) cdata->save_type, &id, &sReg); ownBuf[0] = (id >> 16) & 0xff; ownBuf[1] = (id >> 8) & 0xff; ownBuf[2] = id & 0xff; diff --git a/arm9/source/gamecart/gamecart.h b/arm9/source/gamecart/gamecart.h index 9942e0e..5569c6a 100644 --- a/arm9/source/gamecart/gamecart.h +++ b/arm9/source/gamecart/gamecart.h @@ -1,7 +1,7 @@ #pragma once #include "common.h" -#include "spi.h" +#include "card_spi.h" #define CART_NONE 0 #define CART_CTR (1<<0) @@ -20,7 +20,7 @@ typedef struct { u64 cart_size; u64 data_size; u32 save_size; - CardType save_type; + CardSPIType save_type; u32 arm9i_rom_offset; // TWL specific } PACKED_ALIGN(16) CartData; diff --git a/arm9/source/gamecart/spi.c b/arm9/source/gamecart/spi.c deleted file mode 100644 index b7c14b9..0000000 --- a/arm9/source/gamecart/spi.c +++ /dev/null @@ -1,500 +0,0 @@ -/* - * This file is based on SPI.cpp from TWLSaveTool. Its copyright notice is - * reproduced below. - * - * Copyright (C) 2015-2016 TuxSH - * - * TWLSaveTool 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, see - */ - -#include "spi.h" -#include -#include "timer.h" - -// declarations for actual implementations -int SPIEnableWriting_512B(CardType type); -int SPIEnableWriting_regular(CardType type); -int SPIReadSaveData_9bit(CardType type, u32 offset, void* data, u32 size); -int SPIReadSaveData_16bit(CardType type, u32 offset, void* data, u32 size); -int SPIReadSaveData_24bit(CardType type, u32 offset, void* data, u32 size); -int SPIWriteSaveData_9bit(CardType type, u32 offset, const void* data, u32 size); -int SPIWriteSaveData_16bit(CardType type, u32 offset, const void* data, u32 size); -int SPIWriteSaveData_24bit_write(CardType type, u32 offset, const void* data, u32 size); -int SPIWriteSaveData_24bit_erase_program(CardType type, u32 offset, const void* data, u32 size); -int SPIEraseSector_emulated(CardType type, u32 offset); -int SPIEraseSector_real(CardType type, u32 offset); - -const CardTypeData EEPROM_512B_ = { SPIEnableWriting_512B, SPIReadSaveData_9bit, SPIWriteSaveData_9bit, SPIEraseSector_emulated, 0xffffff, 1 << 9, 16, 16, 16, false, 0, 0, 0 }; - -const CardTypeData EEPROM_STD_DUMMY = { SPIEnableWriting_regular, SPIReadSaveData_16bit, SPIWriteSaveData_16bit, SPIEraseSector_emulated, 0xffffff, UINT32_MAX, 1, 1, 1, false, SPI_EEPROM_CMD_WRITE, 0, 0 }; -const CardTypeData EEPROMTypes[] = { - { SPIEnableWriting_regular, SPIReadSaveData_16bit, SPIWriteSaveData_16bit, SPIEraseSector_emulated, 0xffffff, 1 << 13, 32, 32, 32, false, SPI_EEPROM_CMD_WRITE, 0, 0}, // EEPROM 8 KB - { SPIEnableWriting_regular, SPIReadSaveData_16bit, SPIWriteSaveData_16bit, SPIEraseSector_emulated, 0xffffff, 1 << 16, 128, 128, 128, false, SPI_EEPROM_CMD_WRITE, 0, 0}, // EEPROM 64 KB - { SPIEnableWriting_regular, SPIReadSaveData_24bit, SPIWriteSaveData_24bit_write, SPIEraseSector_emulated, 0xffffff, 1 << 17, 256, 256, 256, false, SPI_EEPROM_CMD_WRITE, 0, 0}, // EEPROM 128 KB -}; - -const CardTypeData FLASH_STD_DUMMY = { NULL, SPIReadSaveData_24bit, NULL, NULL, 0x0, 0, 0, 0, 0, false, 0, 0, 0 }; -const CardTypeData FlashStdTypes[] = { - // NTR/TWL - { SPIEnableWriting_regular, SPIReadSaveData_24bit, SPIWriteSaveData_24bit_write, SPIEraseSector_real, 0x204012, 1 << 18, 65536, 256, 256, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_SE }, - { SPIEnableWriting_regular, SPIReadSaveData_24bit, SPIWriteSaveData_24bit_erase_program, SPIEraseSector_real, 0x621600, 1 << 18, 65536, 256, 65536, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_SE }, - { SPIEnableWriting_regular, SPIReadSaveData_24bit, SPIWriteSaveData_24bit_write, SPIEraseSector_real, 0x204013, 1 << 19, 65536, 256, 256, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_SE }, - { SPIEnableWriting_regular, SPIReadSaveData_24bit, SPIWriteSaveData_24bit_write, SPIEraseSector_real, 0x621100, 1 << 19, 65536, 256, 256, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_SE }, - { SPIEnableWriting_regular, SPIReadSaveData_24bit, SPIWriteSaveData_24bit_write, SPIEraseSector_real, 0x204014, 1 << 20, 65536, 256, 256, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_SE }, - // Untested (but pretty safe bet), for Art Academy - { SPIEnableWriting_regular, SPIReadSaveData_24bit, SPIWriteSaveData_24bit_erase_program, SPIEraseSector_real, 0x202017, 1 << 23, 65536, 32, 65536, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_SE }, - { SPIEnableWriting_regular, SPIReadSaveData_24bit, SPIWriteSaveData_24bit_erase_program, SPIEraseSector_real, 0x204017, 1 << 23, 65536, 32, 65536, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_SE }, - // CTR - { SPIEnableWriting_regular, SPIReadSaveData_24bit, SPIWriteSaveData_24bit_erase_program, SPIEraseSector_real, 0xC22210, 1 << 16, 4096, 32, 4096, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_MXIC_SE }, - { SPIEnableWriting_regular, SPIReadSaveData_24bit, SPIWriteSaveData_24bit_erase_program, SPIEraseSector_real, 0xC22211, 1 << 17, 4096, 32, 4096, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_MXIC_SE }, - { SPIEnableWriting_regular, SPIReadSaveData_24bit, SPIWriteSaveData_24bit_erase_program, SPIEraseSector_real, 0xC22212, 1 << 18, 4096, 32, 4096, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_MXIC_SE }, - { SPIEnableWriting_regular, SPIReadSaveData_24bit, SPIWriteSaveData_24bit_erase_program, SPIEraseSector_real, 0xC22213, 1 << 19, 4096, 32, 4096, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_MXIC_SE }, - { SPIEnableWriting_regular, SPIReadSaveData_24bit, SPIWriteSaveData_24bit_erase_program, SPIEraseSector_real, 0xC22214, 1 << 20, 4096, 32, 4096, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_MXIC_SE }, - { SPIEnableWriting_regular, SPIReadSaveData_24bit, SPIWriteSaveData_24bit_erase_program, SPIEraseSector_real, 0xC22215, 1 << 21, 4096, 32, 4096, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_MXIC_SE }, - { SPIEnableWriting_regular, SPIReadSaveData_24bit, SPIWriteSaveData_24bit_erase_program, SPIEraseSector_real, 0xC22216, 1 << 22, 4096, 32, 4096, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_MXIC_SE }, - { SPIEnableWriting_regular, SPIReadSaveData_24bit, SPIWriteSaveData_24bit_erase_program, SPIEraseSector_real, 0xC22217, 1 << 23, 4096, 32, 4096, false, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_MXIC_SE }, -}; - -const CardTypeData FLASH_INFRARED_DUMMY = { NULL, SPIReadSaveData_24bit, NULL, NULL, 0x0, 0, 0, 0, 0, true, 0, 0, 0 }; -const CardTypeData FlashInfraredTypes[] = { - // NTR/TWL - { SPIEnableWriting_regular, SPIReadSaveData_24bit, SPIWriteSaveData_24bit_write, SPIEraseSector_real, 0x204012, 1 << 18, 65536, 256, 256, true, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_SE }, - { SPIEnableWriting_regular, SPIReadSaveData_24bit, SPIWriteSaveData_24bit_erase_program, SPIEraseSector_real, 0x621600, 1 << 18, 65536, 256, 65536, true, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_SE }, - { SPIEnableWriting_regular, SPIReadSaveData_24bit, SPIWriteSaveData_24bit_write, SPIEraseSector_real, 0x204013, 1 << 19, 65536, 256, 256, true, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_SE }, - { SPIEnableWriting_regular, SPIReadSaveData_24bit, SPIWriteSaveData_24bit_write, SPIEraseSector_real, 0x621100, 1 << 19, 65536, 256, 256, true, SPI_FLASH_CMD_PW, SPI_CMD_PP, SPI_FLASH_CMD_SE }, -}; - -const CardType EEPROM_512B = &EEPROM_512B_; - -const CardType EEPROM_8KB = EEPROMTypes + 0; -const CardType EEPROM_64KB = EEPROMTypes + 1; -const CardType EEPROM_128KB = EEPROMTypes + 2; - -const CardType FLASH_256KB_1 = FlashStdTypes + 0; -const CardType FLASH_256KB_2 = FlashStdTypes + 1; -const CardType FLASH_512KB_1 = FlashStdTypes + 2; -const CardType FLASH_512KB_2 = FlashStdTypes + 3; -const CardType FLASH_1MB = FlashStdTypes + 4; -const CardType FLASH_8MB_1 = FlashStdTypes + 5; -const CardType FLASH_8MB_2 = FlashStdTypes + 6; - -const CardType FLASH_64KB_CTR = FlashStdTypes + 7; -const CardType FLASH_128KB_CTR = FlashStdTypes + 8; -const CardType FLASH_256KB_CTR = FlashStdTypes + 9; -const CardType FLASH_512KB_CTR = FlashStdTypes + 10; -const CardType FLASH_1MB_CTR = FlashStdTypes + 11; -const CardType FLASH_2MB_CTR = FlashStdTypes + 12; -const CardType FLASH_4MB_CTR = FlashStdTypes + 13; -const CardType FLASH_8MB_CTR = FlashStdTypes + 14; - -const CardType FLASH_256KB_1_INFRARED = FlashInfraredTypes + 0; -const CardType FLASH_256KB_2_INFRARED = FlashInfraredTypes + 1; -const CardType FLASH_512KB_1_INFRARED = FlashInfraredTypes + 2; -const CardType FLASH_512KB_2_INFRARED = FlashInfraredTypes + 3; - -#define REG_CFG9_CARDCTL *((vu16*)0x1000000C) -#define CARDCTL_SPICARD (1u<<8) - -int SPIWriteRead(CardType type, const void* cmd, u32 cmdSize, void* answer, u32 answerSize, const void* data, u32 dataSize) { - u32 headerFooterVal = 0; - - REG_CFG9_CARDCTL |= CARDCTL_SPICARD; - - if (type->infrared) { - SPI_XferInfo irXfer = { &headerFooterVal, 1, false }; - SPI_DoXfer(SPI_DEV_CART_IR, &irXfer, 1, false); - } - - SPI_XferInfo transfers[3] = { - { (u8*) cmd, cmdSize, false }, - { answer, answerSize, true }, - { (u8*) data, dataSize, false }, - }; - SPI_DoXfer(SPI_DEV_CART_FLASH, transfers, 3, true); - - REG_CFG9_CARDCTL &= ~CARDCTL_SPICARD; - - return 0; -} - -int SPIWaitWriteEnd(CardType type) { - u8 cmd = SPI_CMD_RDSR, statusReg = 0; - int res = 0; - u64 time_start = timer_start(); - - do{ - res = SPIWriteRead(type, &cmd, 1, &statusReg, 1, 0, 0); - if(res) return res; - if(timer_msec(time_start) > 1000) return 1; - } while(statusReg & SPI_FLG_WIP); - - return 0; -} - -int SPIEnableWriting_512B(CardType type) { - u8 cmd = SPI_CMD_WREN; - return SPIWriteRead(type, &cmd, 1, NULL, 0, 0, 0); -} - -int SPIEnableWriting_regular(CardType type) { - u8 cmd = SPI_CMD_WREN, statusReg = 0; - int res = SPIWriteRead(type, &cmd, 1, NULL, 0, 0, 0); - - if(res) return res; - cmd = SPI_CMD_RDSR; - - do{ - res = SPIWriteRead(type, &cmd, 1, &statusReg, 1, 0, 0); - if(res) return res; - } while(statusReg & ~SPI_FLG_WEL); - - return 0; -} - -int SPIEnableWriting(CardType type) { - if(type == NO_CHIP) return 1; - return type->enableWriting(type); -} - -int _SPIWriteTransaction(CardType type, void* cmd, u32 cmdSize, const void* data, u32 dataSize) { - int res; - if( (res = SPIEnableWriting(type)) ) return res; - if( (res = SPIWriteRead(type, cmd, cmdSize, NULL, 0, (void*) ((u8*) data), dataSize)) ) return res; - return SPIWaitWriteEnd(type); -} - -int SPIReadJEDECIDAndStatusReg(CardType type, u32* id, u8* statusReg) { - u8 cmd = SPI_FLASH_CMD_RDID; - u8 reg = 0; - u8 idbuf[3] = { 0 }; - u32 id_ = 0; - int res = SPIWaitWriteEnd(type); - if(res) return res; - - if((res = SPIWriteRead(type, &cmd, 1, idbuf, 3, 0, 0))) return res; - - id_ = (idbuf[0] << 16) | (idbuf[1] << 8) | idbuf[2]; - cmd = SPI_CMD_RDSR; - - if((res = SPIWriteRead(type, &cmd, 1, ®, 1, 0, 0))) return res; - - if(id) *id = id_; - if(statusReg) *statusReg = reg; - - return 0; -} - -u32 SPIGetPageSize(CardType type) { - if(type == NO_CHIP) return 0; - return type->pageSize; -} - -u32 SPIGetEraseSize(CardType type) { - if(type == NO_CHIP) return 0; - return type->eraseSize; -} - -u32 SPIGetCapacity(CardType type) { - if(type == NO_CHIP) return 0; - return type->capacity; -} - -int SPIWriteSaveData_9bit(CardType type, u32 offset, const void* data, u32 size) { - u8 cmd[2] = { (offset >= 0x100) ? SPI_512B_EEPROM_CMD_WRHI : SPI_512B_EEPROM_CMD_WRLO, (u8) offset }; - - return _SPIWriteTransaction(type, cmd, 2, (void*) ((u8*) data), size); -} - -int SPIWriteSaveData_16bit(CardType type, u32 offset, const void* data, u32 size) { - u8 cmd[3] = { type->writeCommand, (u8)(offset >> 8), (u8) offset }; - - return _SPIWriteTransaction(type, cmd, 3, (void*) ((u8*) data), size); -} - -int SPIWriteSaveData_24bit_write(CardType type, u32 offset, const void* data, u32 size) { - u8 cmd[4] = { type->writeCommand, (u8)(offset >> 16), (u8)(offset >> 8), (u8) offset }; - - return _SPIWriteTransaction(type, cmd, 4, (void*) ((u8*) data), size); -} - -int SPIWriteSaveData_24bit_erase_program(CardType type, u32 offset, const void* data, u32 size) { - u8 cmd[4] = { type->programCommand }; - const u32 pageSize = SPIGetPageSize(type); - const u32 eraseSize = SPIGetEraseSize(type); - int res; - - u8 *newData = NULL; - if(offset % eraseSize || size < eraseSize) { - u32 sectorStart = (offset / eraseSize) * eraseSize; - newData = malloc(eraseSize); - if(!newData) return 1; - if( (res = SPIReadSaveData(type, sectorStart, newData, eraseSize)) ) { - free(newData); - return res; - } - memcpy(newData + (offset % eraseSize), data, size); - data = newData; - offset = sectorStart; - } - - if( (res = SPIEraseSector(type, offset)) ) { - free(newData); - return res; - } - - for(u32 pos = offset; pos < offset + eraseSize; pos += pageSize) { - cmd[1] = (u8)(pos >> 16); - cmd[2] = (u8)(pos >> 8); - cmd[3] = (u8) pos; - if( (res = _SPIWriteTransaction(type, cmd, 4, (void*) ((u8*) data - offset + pos), pageSize)) ) { - free(newData); - return res; - } - } - - free(newData); - return 0; -} - -int SPIWriteSaveData(CardType type, u32 offset, const void* data, u32 size) { - if(type == NO_CHIP) return 1; - - if(size == 0) return 0; - size = min(size, SPIGetCapacity(type) - offset); - u32 end = offset + size; - u32 pos = offset; - u32 writeSize = type->writeSize; - if(writeSize == 0) return 0xC8E13404; - - int res = SPIWaitWriteEnd(type); - if(res) return res; - - while(pos < end) { - u32 remaining = end - pos; - u32 nb = writeSize - (pos % writeSize); - - u32 dataSize = (remaining < nb) ? remaining : nb; - - if( (res = type->writeSaveData(type, pos, (void*) ((u8*) data - offset + pos), dataSize)) ) return res; - - pos = ((pos / writeSize) + 1) * writeSize; // truncate - } - - return 0; -} - -int SPIReadSaveData_9bit(CardType type, u32 pos, void* data, u32 size) { - u8 cmd[4]; - u32 cmdSize = 2; - - u32 end = pos + size; - - u32 read = 0; - if(pos < 0x100) { - u32 len = 0x100 - pos; - cmd[0] = SPI_512B_EEPROM_CMD_RDLO; - cmd[1] = (u8) pos; - - int res = SPIWriteRead(type, cmd, cmdSize, data, len, NULL, 0); - if(res) return res; - - read += len; - } - - if(end >= 0x100) { - u32 len = end - 0x100; - - cmd[0] = SPI_512B_EEPROM_CMD_RDHI; - cmd[1] = (u8)(pos + read); - - int res = SPIWriteRead(type, cmd, cmdSize, (void*)((u8*)data + read), len, NULL, 0); - - if(res) return res; - } - - return 0; -} - -int SPIReadSaveData_16bit(CardType type, u32 offset, void* data, u32 size) { - u8 cmd[3] = { SPI_CMD_READ, (u8)(offset >> 8), (u8) offset }; - - return SPIWriteRead(type, cmd, 3, data, size, NULL, 0); -} - -int SPIReadSaveData_24bit(CardType type, u32 offset, void* data, u32 size) { - u8 cmd[4] = { SPI_CMD_READ, (u8)(offset >> 16), (u8)(offset >> 8), (u8) offset }; - - return SPIWriteRead(type, cmd, 4, data, size, NULL, 0); -} - -int SPIReadSaveData(CardType type, u32 offset, void* data, u32 size) { - if(type == NO_CHIP) return 1; - - if(size == 0) return 0; - - int res = SPIWaitWriteEnd(type); - if(res) return res; - - size = (size <= SPIGetCapacity(type) - offset) ? size : SPIGetCapacity(type) - offset; - - return type->readSaveData(type, offset, data, size); -} - -int SPIEraseSector_emulated(CardType type, u32 offset) { - u32 blockSize = SPIGetEraseSize(type); - u8 *fill_buf = malloc(SPIGetEraseSize(type)); - if (!fill_buf) return 1; - memset(fill_buf, 0xff, blockSize); - offset = (offset / blockSize) * blockSize; - - int res = SPIWriteSaveData(type, offset, fill_buf, blockSize); - free(fill_buf); - return res; -} - -int SPIEraseSector_real(CardType type, u32 offset) { - u8 cmd[4] = { type->eraseCommand, (u8)(offset >> 16), (u8)(offset >> 8), (u8) offset }; - - int res = SPIWaitWriteEnd(type); - if(res) return res; - - return _SPIWriteTransaction(type, cmd, 4, NULL, 0); -} - - -int SPIEraseSector(CardType type, u32 offset) { - if(type == NO_CHIP) return 1; - return type->eraseSector(type, offset); -} - - -// The following routine use code from savegame-manager: - -/* - * savegame_manager: a tool to backup and restore savegames from Nintendo - * DS cartridges. Nintendo DS and all derivative names are trademarks - * by Nintendo. EZFlash 3-in-1 is a trademark by EZFlash. - * - * auxspi.cpp: A thin reimplementation of the AUXSPI protocol - * (high level functions) - * - * Copyright (C) Pokedoc (2010) - */ -/* - * 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., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -int _SPIIsDataMirrored(CardType type, int size, bool* mirrored) { - u32 offset0 = (size-1); // n KB - u32 offset1 = (2*size-1); // 2n KB - - u8 buf1; // +0k data read -> write - u8 buf2; // +n k data read -> read - u8 buf3; // +0k ~data write - u8 buf4; // +n k data new comp buf2 - - int res; - - if( (res = SPIReadSaveData(type, offset0, &buf1, 1)) ) return res; - if( (res = SPIReadSaveData(type, offset1, &buf2, 1)) ) return res; - buf3=~buf1; - if( (res = SPIWriteSaveData(type, offset0, &buf3, 1)) ) return res; - if( (res = SPIReadSaveData(type, offset1, &buf4, 1)) ) return res; - if( (res = SPIWriteSaveData(type, offset0, &buf1, 1)) ) return res; - - *mirrored = buf2 != buf4; - return 0; -} - -int SPIGetCardType(CardType* type, int infrared) { - u8 sr = 0; - u32 jedec = 0; - u32 tries = 0; - CardType t = (infrared == 1) ? &FLASH_INFRARED_DUMMY : &FLASH_STD_DUMMY; - int res; - - u32 maxTries = (infrared == -1) ? 2 : 1; // note: infrared = -1 fails 1/3 of the time - while(tries < maxTries){ - res = SPIReadJEDECIDAndStatusReg(t, &jedec, &sr); // dummy - if(res) return res; - - if ((sr & 0xfd) == 0x00 && (jedec != 0x00ffffff)) { break; } - if ((sr & 0xfd) == 0xF0 && (jedec == 0x00ffffff)) { t = EEPROM_512B; break; } - if ((sr & 0xfd) == 0x00 && (jedec == 0x00ffffff)) { t = &EEPROM_STD_DUMMY; break; } - - ++tries; - t = &FLASH_INFRARED_DUMMY; - } - - if(t == EEPROM_512B) { *type = t; return 0; } - else if(t == &EEPROM_STD_DUMMY) { - bool mirrored = false; - size_t i; - - for(i = 0; i < sizeof(EEPROMTypes) / sizeof(CardTypeData) - 1; i++) { - if( (res = _SPIIsDataMirrored(t, SPIGetCapacity(EEPROMTypes + i), &mirrored)) ) return res; - if(mirrored) { - *type = EEPROMTypes + i; - return 0; - } - } - *type = EEPROMTypes + i; - return 0; - } - - else if(t == &FLASH_INFRARED_DUMMY) { - size_t i; - - if(infrared == 0) *type = NO_CHIP; // did anything go wrong? - - for(i = 0; i < sizeof(FlashInfraredTypes) / sizeof(CardTypeData); i++) { - if(FlashInfraredTypes[i].jedecId == jedec) { - *type = FlashInfraredTypes + i; - return 0; - } - } - - *type = NO_CHIP; - return 0; - } - - else { - size_t i; - - if(infrared == 1) *type = NO_CHIP; // did anything go wrong? - - for(i = 0; i < sizeof(FlashStdTypes) / sizeof(CardTypeData); i++) { - if(FlashStdTypes[i].jedecId == jedec) { - *type = FlashStdTypes + i; - return 0; - } - } - - *type = NO_CHIP; - return 0; - } -} diff --git a/arm9/source/gamecart/spi.h b/arm9/source/gamecart/spi.h deleted file mode 100644 index cfc7e91..0000000 --- a/arm9/source/gamecart/spi.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * This file is based on SPI.h from TWLSaveTool. Its copyright notice is - * reproduced below. - * - * Copyright (C) 2015-2016 TuxSH - * - * TWLSaveTool 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, see - */ - -#pragma once -#include "common.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define SPI_CMD_RDSR 5 -#define SPI_CMD_WREN 6 - -#define SPI_512B_EEPROM_CMD_WRLO 2 -#define SPI_512B_EEPROM_CMD_WRHI 10 -#define SPI_512B_EEPROM_CMD_RDLO 3 -#define SPI_512B_EEPROM_CMD_RDHI 11 - -#define SPI_EEPROM_CMD_WRITE 2 - -#define SPI_CMD_READ 3 - -#define SPI_CMD_PP 2 -#define SPI_FLASH_CMD_PW 10 -#define SPI_FLASH_CMD_RDID 0x9f -#define SPI_FLASH_CMD_SE 0xd8 -#define SPI_FLASH_CMD_PE 0xdb -#define SPI_FLASH_CMD_MXIC_SE 0x20 - -#define SPI_FLG_WIP 1 -#define SPI_FLG_WEL 2 - -typedef struct CardTypeData CardTypeData; - -typedef const CardTypeData * CardType; - -struct CardTypeData { - int (*enableWriting) (CardType type); - int (*readSaveData) (CardType type, u32 offset, void* data, u32 size); - int (*writeSaveData) (CardType type, u32 offset, const void* data, u32 size); - int (*eraseSector) (CardType type, u32 offset); - u32 jedecId; - u32 capacity; - u32 eraseSize; - u32 pageSize; - u32 writeSize; - bool infrared; - u8 writeCommand; - u8 programCommand; - u8 eraseCommand; -}; - -#define NO_CHIP NULL - -const CardType EEPROM_512B; - -const CardType EEPROM_8KB; -const CardType EEPROM_64KB; -const CardType EEPROM_128KB; - -const CardType FLASH_256KB_1; -const CardType FLASH_256KB_2; -const CardType FLASH_512KB_1; -const CardType FLASH_512KB_2; -const CardType FLASH_1MB; -const CardType FLASH_8MB_1; // <- can't restore savegames, and maybe not read them atm -const CardType FLASH_8MB_2; // we are also unsure about the ID for this - -const CardType FLASH_64KB_CTR; // I am extrapolating from the dataheets, only a few of these have been observed in the wild -const CardType FLASH_128KB_CTR; // Most common, including Ocarina of time 3D -const CardType FLASH_256KB_CTR; -const CardType FLASH_512KB_CTR; // Also common, including Detective Pikachu -const CardType FLASH_1MB_CTR; // For example Pokemon Ultra Sun -const CardType FLASH_2MB_CTR; -const CardType FLASH_4MB_CTR; -const CardType FLASH_8MB_CTR; - -const CardType FLASH_256KB_1_INFRARED; // AFAIK, only "Active Health with Carol Vorderman" has such a flash save memory -const CardType FLASH_256KB_2_INFRARED; -const CardType FLASH_512KB_1_INFRARED; -const CardType FLASH_512KB_2_INFRARED; - -int SPIWriteRead(CardType type, const void* cmd, u32 cmdSize, void* answer, u32 answerSize, const void* data, u32 dataSize); -int SPIWaitWriteEnd(CardType type); -int SPIEnableWriting(CardType type); -int SPIReadJEDECIDAndStatusReg(CardType type, u32* id, u8* statusReg); -int SPIGetCardType(CardType* type, int infrared); -u32 SPIGetPageSize(CardType type); -u32 SPIGetCapacity(CardType type); -u32 SPIGetEraseSize(CardType type); - -int SPIWriteSaveData(CardType type, u32 offset, const void* data, u32 size); -int SPIReadSaveData(CardType type, u32 offset, void* data, u32 size); - -int SPIEraseSector(CardType type, u32 offset); -int SPIErase(CardType type); - -#ifdef __cplusplus -} -#endif diff --git a/arm9/source/gamecart/spi_test.c b/arm9/source/gamecart/spi_test.c deleted file mode 100644 index 2e7a425..0000000 --- a/arm9/source/gamecart/spi_test.c +++ /dev/null @@ -1,116 +0,0 @@ -#include "spi_test.h" -#include "timer.h" -#include "ui.h" -#include "spi.h" - -void SPITestWaitWriteEnd(CardType type) { - while (SPIWaitWriteEnd(type)); -} - -int SPITestEraseSector(CardType type, u32 offset, u8 actualCmd) { - u8 cmd[4] = { actualCmd, (u8)(offset >> 16), (u8)(offset >> 8), (u8) offset }; - if(type == NO_CHIP) return 0xC8E13404; - - int res = SPIWaitWriteEnd(type); - - if( (res = SPIEnableWriting(type)) ) return res; - if( (res = SPIWriteRead(type, cmd, 4, NULL, 0, NULL, 0)) ) return res; - SPITestWaitWriteEnd(type); - return 0; -} - -u64 SPITestEraseAll(CardType type, u32 size, u8 actualCmd, u32 eraseSize) { - u32 offset; - u64 timer = timer_start(); - for(offset = 0; offset < size; offset += eraseSize) { - SPITestEraseSector(type, offset, actualCmd); - } - return timer_ticks(timer); -} - -u64 SPITestWriteAll(CardType type, u32 size, u8 actualCmd, u32 pageSize, u8 *buf) { - memset(buf, 0, size); - u8 cmd[4] = { actualCmd, 0, 0, 0 }; - u32 offset = 0; - u64 timer = timer_start(); - for(offset = 0; offset < size; offset += pageSize) { - cmd[1] = (u8)(offset >> 16); - cmd[2] = (u8)(offset >> 8); - cmd[3] = (u8) offset; - SPIEnableWriting(type); - SPIWriteRead(type, cmd, 4, NULL, 0, buf, pageSize); - SPIWaitWriteEnd(type); - } - return timer_ticks(timer); -} - -int SPITestBytes(CardType t, u32 id, u8 *buf, u8 byte, const char *word, u32 size) { - u32 offset; - SPIReadSaveData(t, 0, buf, size); - for(offset = 0; offset < size; offset++) { - if(buf[offset] != byte) { - if(word && !ShowPrompt(true, "ID: 0x%06lX\n1: Could not %s\n*0x%06lX = 0x%02hhX", id, word, offset, buf[offset])) return -1; - return 1; - } - } - return 0; -} - -void SPIFlashTest(void) { - CardType t = ShowPrompt(true, "Does the cart have IR?") ? FLASH_512KB_1_INFRARED : FLASH_256KB_1; - u32 size; - u32 pageSize; - u32 jedecid; - if(SPIReadJEDECIDAndStatusReg(t, &jedecid, NULL)) return; - if(!ShowPrompt(true, "ID: 0x%06lX\nDo flash test on this cart?\nThis will overwrite it completely\nseveral times!", jedecid)) return; - size = ShowHexPrompt(0, 8, "Memory size?"); - u8 *buf = calloc(size, 1); - if (buf == NULL) { - ShowPrompt(false, "Malloc failed!"); - return; - } - pageSize = ShowHexPrompt(0, 8, "Page size?"); - // method 1: - u64 time1 = SPITestWriteAll(t, size, 0x0A, pageSize, buf); - int result1 = SPITestBytes(t, jedecid, buf, 0x00, "write", size); - if(result1 < 0) { - free(buf); - return; - } - ShowPrompt(false, "ID: 0x%06lX\nWriting all took %llu\nError: %d", jedecid, time1, result1); - // method 2: Erasing - u8 eraseCommand; - u32 eraseSize; - for(eraseCommand = 0xC0; eraseCommand < 0xE0; eraseCommand++) { - ShowString("Testing %hhX", eraseCommand); - SPITestEraseSector(t, 0, eraseCommand); - SPIReadSaveData(t, 0, buf, size); - for(eraseSize = 0; eraseSize < size && buf[eraseSize] == 0xff; eraseSize++); - if(eraseSize) { - SPITestWriteAll(t, eraseSize, 0x02, pageSize, buf); - if(SPITestBytes(t, jedecid, buf, 0x00, "reset", size) < 0) { - free(buf); - return; - } - - if(ShowPrompt(true, "ID: 0x%06lX\n0x%02hhX erased %lu bytes\nTest?", jedecid, eraseCommand, eraseSize)) { - u64 eraseTime = SPITestEraseAll(t, size, eraseCommand, eraseSize); - int eraseResult = SPITestBytes(t, jedecid, buf, 0xFF, "erase", size); - if(eraseResult < 0) { - free(buf); - return; - } - u64 writeTime = SPITestWriteAll(t, size, 0x02, pageSize, buf); - int writeResult = SPITestBytes(t, jedecid, buf, 0x00, "write", size); - if(eraseResult < 0) { - free(buf); - return; - } - ShowPrompt(false, "ID: 0x%06lX\n0x%02hhX erases %lu bytes\nErase: %llu (%d)\nWrite: %llu (%d)\nTotal: %llu", jedecid, eraseCommand, eraseSize, eraseTime, eraseResult, writeTime, writeResult, eraseTime + writeTime); - } - } - } - free(buf); - -} - diff --git a/arm9/source/gamecart/spi_test.h b/arm9/source/gamecart/spi_test.h deleted file mode 100644 index 5fc89dc..0000000 --- a/arm9/source/gamecart/spi_test.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once -#include "common.h" -#include "spi.h" - -void SPIFlashTest(void);