/* Copyright 2016-2019 Arisotura This file is part of melonDS. melonDS 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 3 of the License, or (at your option) any later version. melonDS 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 melonDS. If not, see http://www.gnu.org/licenses/. */ #include #include #include "DSi.h" #include "DSi_SD.h" #include "DSi_NWifi.h" #include "Platform.h" #define SD_DESC (Num?"SDIO":"SD/MMC") DSi_SDHost::DSi_SDHost(u32 num) { Num = num; DataFIFO[0] = new FIFO(0x100); DataFIFO[1] = new FIFO(0x100); Ports[0] = NULL; Ports[1] = NULL; } DSi_SDHost::~DSi_SDHost() { delete DataFIFO[0]; delete DataFIFO[1]; if (Ports[0]) delete Ports[0]; if (Ports[1]) delete Ports[1]; } void DSi_SDHost::Reset() { if (Num == 0) { PortSelect = 0x0200; // CHECKME } else { PortSelect = 0x0100; // CHECKME } SoftReset = 0x0007; // CHECKME SDClock = 0; SDOption = 0; Command = 0; Param = 0; memset(ResponseBuffer, 0, sizeof(ResponseBuffer)); DataFIFO[0]->Clear(); DataFIFO[1]->Clear(); CurFIFO = 0; IRQStatus = 0; IRQMask = 0x8B7F031D; CardIRQStatus = 0; CardIRQMask = 0xC007; CardIRQCtl = 0; DataCtl = 0; Data32IRQ = 0; DataMode = 0; BlockCount16 = 0; BlockCount32 = 0; BlockCountInternal = 0; BlockLen16 = 0; BlockLen32 = 0; StopAction = 0; if (Ports[0]) delete Ports[0]; if (Ports[1]) delete Ports[1]; Ports[0] = NULL; Ports[1] = NULL; if (Num == 0) { DSi_MMCStorage* mmc = new DSi_MMCStorage(this, true, "nand.bin"); mmc->SetCID(DSi::eMMC_CID); // TODO: port 0 (SD) Ports[1] = mmc; } else { DSi_NWifi* nwifi = new DSi_NWifi(this); Ports[0] = nwifi; } } void DSi_SDHost::DoSavestate(Savestate* file) { // TODO! } void DSi_SDHost::UpdateData32IRQ() { if (DataMode == 0) return; u32 oldflags = ((Data32IRQ >> 8) & 0x1) | (((~Data32IRQ) >> 8) & 0x2); oldflags &= (Data32IRQ >> 11); Data32IRQ &= ~0x0300; if (IRQStatus & (1<<24)) Data32IRQ |= (1<<8); if (!(IRQStatus & (1<<25))) Data32IRQ |= (1<<9); u32 newflags = ((Data32IRQ >> 8) & 0x1) | (((~Data32IRQ) >> 8) & 0x2); newflags &= (Data32IRQ >> 11); if ((oldflags == 0) && (newflags != 0)) NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC); } void DSi_SDHost::ClearIRQ(u32 irq) { IRQStatus &= ~(1<IRQ) CardIRQStatus |= (1<<0); else CardIRQStatus &= ~(1<<0); u16 newflags = CardIRQStatus & ~CardIRQMask; if ((oldflags == 0) && (newflags != 0)) // checkme { NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC); NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO_Data1 : NDS::IRQ2_DSi_SD_Data1); } } void DSi_SDHost::SendResponse(u32 val, bool last) { *(u32*)&ResponseBuffer[6] = *(u32*)&ResponseBuffer[4]; *(u32*)&ResponseBuffer[4] = *(u32*)&ResponseBuffer[2]; *(u32*)&ResponseBuffer[2] = *(u32*)&ResponseBuffer[0]; *(u32*)&ResponseBuffer[0] = val; if (last) SetIRQ(0); } void DSi_SDHost::FinishSend(u32 param) { DSi_SDHost* host = (param & 0x1) ? DSi::SDIO : DSi::SDMMC; host->CurFIFO ^= 1; host->ClearIRQ(25); host->SetIRQ(24); //if (param & 0x2) host->SetIRQ(2); } u32 DSi_SDHost::SendData(u8* data, u32 len) { //printf("%s: data RX, len=%d, blkcnt=%d (%d) blklen=%d, irq=%08X\n", SD_DESC, len, BlockCount16, BlockCountInternal, BlockLen16, IRQMask); if (len != BlockLen16) { printf("!! BAD BLOCKLEN\n"); len = BlockLen16; } bool last = (BlockCountInternal == 0); u32 f = CurFIFO ^ 1; for (u32 i = 0; i < len; i += 2) DataFIFO[f]->Write(*(u16*)&data[i]); //CurFIFO = f; //SetIRQ(24); // TODO: determine what the delay should be! // for now, this is a placeholder // we need a delay because DSi boot2 will send a command and then wait for IRQ0 // but if IRQ24 is thrown instantly, the handler clears IRQ0 before the // send-command function starts polling IRQ status u32 param = Num | (last << 1); NDS::ScheduleEvent(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer, false, 512, FinishSend, param); return len; } void DSi_SDHost::FinishReceive(u32 param) { DSi_SDHost* host = (param & 0x1) ? DSi::SDIO : DSi::SDMMC; DSi_SDDevice* dev = host->Ports[host->PortSelect & 0x1]; host->ClearIRQ(24); host->SetIRQ(25); if (dev) dev->ContinueTransfer(); } u32 DSi_SDHost::ReceiveData(u8* data, u32 len) { printf("%s: data TX, len=%d, blkcnt=%d (%d) blklen=%d, irq=%08X\n", SD_DESC, len, BlockCount16, BlockCountInternal, BlockLen16, IRQMask); if (len != BlockLen16) { printf("!! BAD BLOCKLEN\n"); len = BlockLen16; } u32 f = CurFIFO; if ((DataFIFO[f]->Level() << 1) < len) { printf("%s: FIFO not full enough for a transfer (%d / %d)\n", SD_DESC, DataFIFO[f]->Level()<<1, len); return 0; } DSi_SDDevice* dev = Ports[PortSelect & 0x1]; for (u32 i = 0; i < len; i += 2) *(u16*)&data[i] = DataFIFO[f]->Read(); CurFIFO ^= 1; if (BlockCountInternal <= 1) { printf("%s: data TX complete", SD_DESC); if (StopAction & (1<<8)) { printf(", sending CMD12"); if (dev) dev->SendCMD(12, 0); } printf("\n"); // CHECKME: presumably IRQ2 should not trigger here, but rather // when the data transfer is done //SetIRQ(0); SetIRQ(2); } else { BlockCountInternal--; } return len; } u32 DSi_SDHost::GetTransferrableLen(u32 len) { if (len > BlockLen16) len = BlockLen16; // checkme return len; } u16 DSi_SDHost::Read(u32 addr) { //if(Num)printf("SDIO READ %08X %08X\n", addr, NDS::GetPC(1)); switch (addr & 0x1FF) { case 0x000: return Command; case 0x002: return PortSelect & 0x030F; case 0x004: return Param & 0xFFFF; case 0x006: return Param >> 16; case 0x008: return StopAction; case 0x00A: return BlockCount16; case 0x00C: return ResponseBuffer[0]; case 0x00E: return ResponseBuffer[1]; case 0x010: return ResponseBuffer[2]; case 0x012: return ResponseBuffer[3]; case 0x014: return ResponseBuffer[4]; case 0x016: return ResponseBuffer[5]; case 0x018: return ResponseBuffer[6]; case 0x01A: return ResponseBuffer[7]; case 0x01C: return (IRQStatus & 0x031D) | 0x0030; // TODO: adjust insert flags for SD card case 0x01E: return ((IRQStatus >> 16) & 0x8B7F); case 0x020: return IRQMask & 0x031D; case 0x022: return (IRQMask >> 16) & 0x8B7F; case 0x024: return SDClock; case 0x026: return BlockLen16; case 0x028: return SDOption; case 0x02C: return 0; // TODO case 0x034: return CardIRQCtl; case 0x036: return CardIRQStatus; case 0x038: return CardIRQMask; case 0x030: // FIFO16 { // TODO: decrement BlockLen???? u32 f = CurFIFO; if (DataFIFO[f]->IsEmpty()) { // TODO return 0; } DSi_SDDevice* dev = Ports[PortSelect & 0x1]; u16 ret = DataFIFO[f]->Read(); if (DataFIFO[f]->IsEmpty()) { ClearIRQ(24); if (BlockCountInternal <= 1) { printf("%s: data RX complete", SD_DESC); if (StopAction & (1<<8)) { printf(", sending CMD12"); if (dev) dev->SendCMD(12, 0); } printf("\n"); // CHECKME: presumably IRQ2 should not trigger here, but rather // when the data transfer is done //SetIRQ(0); SetIRQ(2); } else { BlockCountInternal--; if (dev) dev->ContinueTransfer(); } SetIRQ(25); } return ret; } case 0x0D8: return DataCtl; case 0x0E0: return SoftReset; case 0x100: return Data32IRQ; case 0x104: return BlockLen32; case 0x108: return BlockCount32; } printf("unknown %s read %08X @ %08X\n", SD_DESC, addr, NDS::GetPC(1)); return 0; } u32 DSi_SDHost::ReadFIFO32() { if (DataMode != 1) return 0; // TODO: decrement BlockLen???? u32 f = CurFIFO; if (DataFIFO[f]->IsEmpty()) { // TODO return 0; } DSi_SDDevice* dev = Ports[PortSelect & 0x1]; u32 ret = DataFIFO[f]->Read(); ret |= (DataFIFO[f]->Read() << 16); if (DataFIFO[f]->IsEmpty()) { ClearIRQ(24); if (BlockCountInternal <= 1) { printf("%s: data32 RX complete", SD_DESC); if (StopAction & (1<<8)) { printf(", sending CMD12"); if (dev) dev->SendCMD(12, 0); } printf("\n"); // CHECKME: presumably IRQ2 should not trigger here, but rather // when the data transfer is done //SetIRQ(0); SetIRQ(2); } else { BlockCountInternal--; if (dev) dev->ContinueTransfer(); } SetIRQ(25); } return ret; } void DSi_SDHost::Write(u32 addr, u16 val) { //if(Num)printf("SDIO WRITE %08X %04X %08X\n", addr, val, NDS::GetPC(1)); switch (addr & 0x1FF) { case 0x000: { Command = val; u8 cmd = Command & 0x3F; DSi_SDDevice* dev = Ports[PortSelect & 0x1]; if (dev) { // CHECKME // "Setting Command Type to "ACMD" is automatically sending an APP_CMD prefix prior to the command number" // except DSi boot2 manually sends an APP_CMD prefix AND sets the next command to be ACMD switch ((Command >> 6) & 0x3) { case 0: dev->SendCMD(cmd, Param); break; case 1: /*dev->SendCMD(55, 0);*/ dev->SendCMD(cmd, Param); break; default: printf("%s: unknown command type %d, %02X %08X\n", SD_DESC, (Command>>6)&0x3, cmd, Param); break; } } else printf("%s: SENDING CMD %04X TO NULL DEVICE\n", SD_DESC, val); } return; case 0x002: PortSelect = val; printf("%s: PORT SELECT %04X\n", SD_DESC, val); return; case 0x004: Param = (Param & 0xFFFF0000) | val; return; case 0x006: Param = (Param & 0x0000FFFF) | (val << 16); return; case 0x008: StopAction = val & 0x0101; return; case 0x00A: BlockCount16 = val; BlockCountInternal = val; printf("%s: BLOCK COUNT %d\n", SD_DESC, val); return; case 0x01C: IRQStatus &= (val | 0xFFFF0000); return; case 0x01E: IRQStatus &= ((val << 16) | 0xFFFF); return; case 0x020: { u32 oldmask = IRQMask; IRQMask = (IRQMask & 0x8B7F0000) | (val & 0x031D); UpdateIRQ(oldmask); } return; case 0x022: { u32 oldmask = IRQMask; IRQMask = (IRQMask & 0x0000031D) | ((val & 0x8B7F) << 16); UpdateIRQ(oldmask); if (!DataFIFO[CurFIFO]->IsEmpty()) SetIRQ(24); // checkme if (DataFIFO[CurFIFO]->IsEmpty()) SetIRQ(25); // checkme } return; case 0x024: SDClock = val & 0x03FF; return; case 0x026: BlockLen16 = val & 0x03FF; if (BlockLen16 > 0x200) BlockLen16 = 0x200; return; case 0x028: SDOption = val & 0xC1FF; return; case 0x030: // FIFO16 { DSi_SDDevice* dev = Ports[PortSelect & 0x1]; u32 f = CurFIFO; if (DataFIFO[f]->IsFull()) { // TODO printf("!!!! %s FIFO FULL\n", SD_DESC); return; } DataFIFO[f]->Write(val); if (DataFIFO[f]->Level() < (BlockLen16>>1)) { ClearIRQ(25); SetIRQ(24); return; } // we completed one block, send it to the SD card // TODO measure the actual delay!! NDS::ScheduleEvent(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer, false, 2048, FinishReceive, Num); } return; case 0x034: CardIRQCtl = val & 0x0305; printf("[%d] CardIRQCtl = %04X\n", Num, val); SetCardIRQ(); return; case 0x036: CardIRQStatus &= val; return; case 0x038: CardIRQMask = val & 0xC007; printf("[%d] CardIRQMask = %04X\n", Num, val); SetCardIRQ(); return; case 0x0D8: DataCtl = (val & 0x0022); DataMode = ((DataCtl >> 1) & 0x1) & ((Data32IRQ >> 1) & 0x1); printf("%s: data mode %d-bit\n", SD_DESC, DataMode?32:16); return; case 0x0E0: if ((SoftReset & 0x0001) && !(val & 0x0001)) { printf("%s: RESET\n", SD_DESC); StopAction = 0; memset(ResponseBuffer, 0, sizeof(ResponseBuffer)); IRQStatus = 0; // TODO: ERROR_DETAIL_STATUS SDClock &= ~0x0500; SDOption = 0x40EE; // TODO: CARD_IRQ_STAT // TODO: FIFO16 shit } SoftReset = 0x0006 | (val & 0x0001); return; case 0x100: Data32IRQ = (val & 0x1802) | (Data32IRQ & 0x0300); if (val & (1<<10)) { // kind of hacky u32 f = CurFIFO; DataFIFO[f]->Clear(); } DataMode = ((DataCtl >> 1) & 0x1) & ((Data32IRQ >> 1) & 0x1); printf("%s: data mode %d-bit\n", SD_DESC, DataMode?32:16); return; case 0x104: BlockLen32 = val & 0x03FF; return; case 0x108: BlockCount32 = val; return; } printf("unknown %s write %08X %04X\n", SD_DESC, addr, val); } void DSi_SDHost::WriteFIFO32(u32 val) { if (DataMode != 1) return; DSi_SDDevice* dev = Ports[PortSelect & 0x1]; u32 f = CurFIFO; if (DataFIFO[f]->IsFull()) { // TODO printf("!!!! %s FIFO FULL\n", SD_DESC); return; } DataFIFO[f]->Write(val & 0xFFFF); DataFIFO[f]->Write(val >> 16); if (DataFIFO[f]->Level() < (BlockLen16>>1)) { ClearIRQ(25); SetIRQ(24); return; } // we completed one block, send it to the SD card // TODO measure the actual delay!! NDS::ScheduleEvent(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer, false, 2048, FinishReceive, Num); } DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, const char* path) : DSi_SDDevice(host) { Internal = internal; strncpy(FilePath, path, 1023); FilePath[1023] = '\0'; File = Platform::OpenLocalFile(path, "r+b"); CSR = 0x00000100; // checkme // TODO: busy bit // TODO: SDHC/SDXC bit OCR = 0x80FF8000; // TODO: customize based on card size etc u8 csd_template[16] = {0x40, 0x40, 0x96, 0xE9, 0x7F, 0xDB, 0xF6, 0xDF, 0x01, 0x59, 0x0F, 0x2A, 0x01, 0x26, 0x90, 0x00}; memcpy(CSD, csd_template, 16); // checkme memset(SCR, 0, 8); *(u32*)&SCR[0] = 0x012A0000; memset(SSR, 0, 64); BlockSize = 0; RWAddress = 0; RWCommand = 0; } DSi_MMCStorage::~DSi_MMCStorage() { if (File) fclose(File); } void DSi_MMCStorage::SendCMD(u8 cmd, u32 param) { if (CSR & (1<<5)) { CSR &= ~(1<<5); return SendACMD(cmd, param); } switch (cmd) { case 0: // reset/etc Host->SendResponse(CSR, true); return; case 2: case 10: // get CID Host->SendResponse(*(u32*)&CID[12], false); Host->SendResponse(*(u32*)&CID[8], false); Host->SendResponse(*(u32*)&CID[4], false); Host->SendResponse(*(u32*)&CID[0], true); if (cmd == 2) SetState(0x02); return; case 3: // get/set RCA if (Internal) { RCA = param >> 16; Host->SendResponse(CSR|0x10000, true); // huh?? } else { // TODO printf("CMD3 on SD card: TODO\n"); } return; case 7: // select card (by RCA) Host->SendResponse(CSR, true); return; case 8: // set voltage Host->SendResponse(param, true); return; case 9: // get CSD Host->SendResponse(*(u32*)&CSD[12], false); Host->SendResponse(*(u32*)&CSD[8], false); Host->SendResponse(*(u32*)&CSD[4], false); Host->SendResponse(*(u32*)&CSD[0], true); return; case 12: // stop operation SetState(0x04); if (File) fflush(File); RWCommand = 0; Host->SendResponse(CSR, true); return; case 13: // get status Host->SendResponse(CSR, true); return; case 16: // set block size BlockSize = param; if (BlockSize > 0x200) { // TODO! raise error printf("!! SD/MMC: BAD BLOCK LEN %d\n", BlockSize); BlockSize = 0x200; } SetState(0x04); // CHECKME Host->SendResponse(CSR, true); return; case 18: // read multiple blocks printf("READ_MULTIPLE_BLOCKS addr=%08X size=%08X\n", param, BlockSize); RWAddress = param; if (OCR & (1<<30)) { RWAddress <<= 9; BlockSize = 512; } RWCommand = 18; Host->SendResponse(CSR, true); ReadBlock(RWAddress); RWAddress += BlockSize; SetState(0x05); return; case 25: // write multiple blocks printf("WRITE_MULTIPLE_BLOCKS addr=%08X size=%08X\n", param, BlockSize); RWAddress = param; if (OCR & (1<<30)) { RWAddress <<= 9; BlockSize = 512; } RWCommand = 25; Host->SendResponse(CSR, true); WriteBlock(RWAddress); RWAddress += BlockSize; SetState(0x04); return; case 55: // appcmd prefix CSR |= (1<<5); Host->SendResponse(CSR, true); return; } printf("MMC: unknown CMD %d %08X\n", cmd, param); } void DSi_MMCStorage::SendACMD(u8 cmd, u32 param) { switch (cmd) { case 6: // set bus width (TODO?) printf("SET BUS WIDTH %08X\n", param); Host->SendResponse(CSR, true); return; case 13: // get SSR Host->SendResponse(CSR, true); Host->SendData(SSR, 64); return; case 41: // set operating conditions // CHECKME: // DSi boot2 sets this to 0x40100000 (hardcoded) // then has two codepaths depending on whether bit30 did get set // is it settable at all on the MMC? probably not. if (Internal) param &= ~(1<<30); OCR &= 0xBF000000; OCR |= (param & 0x40FFFFFF); Host->SendResponse(OCR, true); SetState(0x01); return; case 42: // ??? Host->SendResponse(CSR, true); return; case 51: // get SCR Host->SendResponse(CSR, true); Host->SendData(SCR, 8); return; } printf("MMC: unknown ACMD %d %08X\n", cmd, param); } void DSi_MMCStorage::ContinueTransfer() { if (RWCommand == 0) return; u32 len = 0; switch (RWCommand) { case 18: len = ReadBlock(RWAddress); break; case 25: len = WriteBlock(RWAddress); break; } RWAddress += len; } u32 DSi_MMCStorage::ReadBlock(u64 addr) { //printf("SD/MMC: reading block @ %08X, len=%08X\n", addr, BlockSize); u32 len = BlockSize; len = Host->GetTransferrableLen(len); u8 data[0x200]; if (File) { fseek(File, addr, SEEK_SET); fread(data, 1, len, File); } return Host->SendData(data, len); } u32 DSi_MMCStorage::WriteBlock(u64 addr) { printf("SD/MMC: write block @ %08X, len=%08X\n", addr, BlockSize); u32 len = BlockSize; len = Host->GetTransferrableLen(len); u8 data[0x200]; if (len = Host->ReceiveData(data, len)) { if (File) { fseek(File, addr, SEEK_SET); fwrite(data, 1, len, File); } } return len; }