summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Lam <leolino.lam@gmail.com>2017-08-06 11:17:50 +0800
committerGitHub <noreply@github.com>2017-08-06 11:17:50 +0800
commit84ecc0ecc9cf9acf355dbfbd7b15032ffffaa9eb (patch)
treeaf945ddf7c21bf435998699183adcd7b6d8e98e8
parent5e54290271d69b670a7c1274562cb72ff9cff116 (diff)
parent10c615d7db3dca26d0237a890dfc99f585e698fe (diff)
Merge pull request #5842 from TBoshoven/sdhc-support
Add support for SDHC
-rw-r--r--Source/Core/Core/IOS/SDIO/SDIOSlot0.cpp305
-rw-r--r--Source/Core/Core/IOS/SDIO/SDIOSlot0.h33
2 files changed, 285 insertions, 53 deletions
diff --git a/Source/Core/Core/IOS/SDIO/SDIOSlot0.cpp b/Source/Core/Core/IOS/SDIO/SDIOSlot0.cpp
index 8e1689c9e6..d75135d5b9 100644
--- a/Source/Core/Core/IOS/SDIO/SDIOSlot0.cpp
+++ b/Source/Core/Core/IOS/SDIO/SDIOSlot0.cpp
@@ -25,7 +25,28 @@ namespace HLE
{
namespace Device
{
-SDIOSlot0::SDIOSlot0(Kernel& ios, const std::string& device_name) : Device(ios, device_name)
+constexpr bool SupportsSDHC(u32 ios_version)
+{
+ switch (ios_version)
+ {
+ // Known versions to support SDHC
+ case 48:
+ case 56:
+ case 57:
+ case 58:
+ case 59:
+ case 60:
+ case 61:
+ case 70:
+ case 80:
+ return true;
+ default:
+ return false;
+ };
+}
+
+SDIOSlot0::SDIOSlot0(Kernel& ios, const std::string& device_name)
+ : Device(ios, device_name), m_sdhc_supported(SupportsSDHC(ios.GetVersion()))
{
}
@@ -36,9 +57,9 @@ void SDIOSlot0::DoState(PointerWrap& p)
{
OpenInternal();
}
- p.Do(m_Status);
- p.Do(m_BlockLength);
- p.Do(m_BusWidth);
+ p.Do(m_status);
+ p.Do(m_block_length);
+ p.Do(m_bus_width);
p.Do(m_registers);
}
@@ -59,16 +80,16 @@ void SDIOSlot0::EventNotify()
void SDIOSlot0::OpenInternal()
{
const std::string filename = File::GetUserPath(F_WIISDCARD_IDX);
- m_Card.Open(filename, "r+b");
- if (!m_Card)
+ m_card.Open(filename, "r+b");
+ if (!m_card)
{
WARN_LOG(IOS_SD, "Failed to open SD Card image, trying to create a new 128MB image...");
if (SDCardCreate(128, filename))
{
INFO_LOG(IOS_SD, "Successfully created %s", filename.c_str());
- m_Card.Open(filename, "r+b");
+ m_card.Open(filename, "r+b");
}
- if (!m_Card)
+ if (!m_card)
{
ERROR_LOG(IOS_SD, "Could not open SD Card image or create a new one, are you running "
"from a read-only directory?");
@@ -82,14 +103,15 @@ ReturnCode SDIOSlot0::Open(const OpenRequest& request)
m_registers.fill(0);
m_is_active = true;
+
return IPC_SUCCESS;
}
ReturnCode SDIOSlot0::Close(u32 fd)
{
- m_Card.Close();
- m_BlockLength = 0;
- m_BusWidth = 0;
+ m_card.Close();
+ m_block_length = 0;
+ m_bus_width = 0;
return Device::Close(fd);
}
@@ -171,7 +193,9 @@ s32 SDIOSlot0::ExecuteCommand(const Request& request, u32 _BufferIn, u32 _Buffer
switch (req.command)
{
case GO_IDLE_STATE:
- // libogc can use it during init..
+ INFO_LOG(IOS_SD, "GO_IDLE_STATE");
+ // Response is R1 (idle state)
+ Memory::Write_U32(0x00, _BufferOut);
break;
case SEND_RELATIVE_ADDR:
@@ -187,22 +211,20 @@ s32 SDIOSlot0::ExecuteCommand(const Request& request, u32 _BufferIn, u32 _Buffer
break;
case SEND_IF_COND:
+ INFO_LOG(IOS_SD, "SEND_IF_COND");
// If the card can operate on the supplied voltage, the response echoes back the supply
// voltage and the check pattern that were set in the command argument.
+ // This command is used to differentiate between protocol v1 and v2.
+ InitSDHC();
Memory::Write_U32(req.arg, _BufferOut);
break;
case SEND_CSD:
- INFO_LOG(IOS_SD, "SEND_CSD");
- // <WntrMute> shuffle2_, OCR: 0x80ff8000 CID: 0x38a00000 0x480032d5 0x3c608030 0x8803d420
- // CSD: 0xff928040 0xc93efbcf 0x325f5a83 0x00002600
-
- // Values used currently are from lpfaint99
- Memory::Write_U32(0x80168000, _BufferOut);
- Memory::Write_U32(0xa9ffffff, _BufferOut + 4);
- Memory::Write_U32(0x325b5a83, _BufferOut + 8);
- Memory::Write_U32(0x00002e00, _BufferOut + 12);
- break;
+ {
+ const std::array<u32, 4> csd = m_protocol == SDProtocol::V1 ? GetCSDv1() : GetCSDv2();
+ Memory::CopyToEmuSwapped(_BufferOut, csd.data(), csd.size() * sizeof(u32));
+ }
+ break;
case ALL_SEND_CID:
case SEND_CID:
@@ -214,7 +236,7 @@ s32 SDIOSlot0::ExecuteCommand(const Request& request, u32 _BufferIn, u32 _Buffer
break;
case SET_BLOCKLEN:
- m_BlockLength = req.arg;
+ m_block_length = req.arg;
Memory::Write_U32(0x900, _BufferOut);
break;
@@ -225,14 +247,14 @@ s32 SDIOSlot0::ExecuteCommand(const Request& request, u32 _BufferIn, u32 _Buffer
case ACMD_SETBUSWIDTH:
// 0 = 1bit, 2 = 4bit
- m_BusWidth = (req.arg & 3);
+ m_bus_width = (req.arg & 3);
Memory::Write_U32(0x920, _BufferOut);
break;
case ACMD_SENDOPCOND:
// Sends host capacity support information (HCS) and asks the accessed card to send
// its operating condition register (OCR) content
- Memory::Write_U32(0x80ff8000, _BufferOut);
+ Memory::Write_U32(GetOCRegister(), _BufferOut);
break;
case READ_MULTIPLE_BLOCK:
@@ -242,21 +264,22 @@ s32 SDIOSlot0::ExecuteCommand(const Request& request, u32 _BufferIn, u32 _Buffer
INFO_LOG(IOS_SD, "%sRead %i Block(s) from 0x%08x bsize %i into 0x%08x!",
req.isDMA ? "DMA " : "", req.blocks, req.arg, req.bsize, req.addr);
- if (m_Card)
+ if (m_card)
{
u32 size = req.bsize * req.blocks;
+ u64 address = GetAddressFromRequest(req.arg);
- if (!m_Card.Seek(req.arg, SEEK_SET))
+ if (!m_card.Seek(address, SEEK_SET))
ERROR_LOG(IOS_SD, "Seek failed WTF");
- if (m_Card.ReadBytes(Memory::GetPointer(req.addr), size))
+ if (m_card.ReadBytes(Memory::GetPointer(req.addr), size))
{
DEBUG_LOG(IOS_SD, "Outbuffer size %i got %i", _rwBufferSize, size);
}
else
{
- ERROR_LOG(IOS_SD, "Read Failed - error: %i, eof: %i", ferror(m_Card.GetHandle()),
- feof(m_Card.GetHandle()));
+ ERROR_LOG(IOS_SD, "Read Failed - error: %i, eof: %i", ferror(m_card.GetHandle()),
+ feof(m_card.GetHandle()));
ret = RET_FAIL;
}
}
@@ -271,17 +294,18 @@ s32 SDIOSlot0::ExecuteCommand(const Request& request, u32 _BufferIn, u32 _Buffer
INFO_LOG(IOS_SD, "%sWrite %i Block(s) from 0x%08x bsize %i to offset 0x%08x!",
req.isDMA ? "DMA " : "", req.blocks, req.addr, req.bsize, req.arg);
- if (m_Card && SConfig::GetInstance().bEnableMemcardSdWriting)
+ if (m_card && SConfig::GetInstance().bEnableMemcardSdWriting)
{
u32 size = req.bsize * req.blocks;
+ u64 address = GetAddressFromRequest(req.arg);
- if (!m_Card.Seek(req.arg, SEEK_SET))
+ if (!m_card.Seek(address, SEEK_SET))
ERROR_LOG(IOS_SD, "fseeko failed WTF");
- if (!m_Card.WriteBytes(Memory::GetPointer(req.addr), size))
+ if (!m_card.WriteBytes(Memory::GetPointer(req.addr), size))
{
- ERROR_LOG(IOS_SD, "Write Failed - error: %i, eof: %i", ferror(m_Card.GetHandle()),
- feof(m_Card.GetHandle()));
+ ERROR_LOG(IOS_SD, "Write Failed - error: %i, eof: %i", ferror(m_card.GetHandle()),
+ feof(m_card.GetHandle()));
ret = RET_FAIL;
}
}
@@ -371,11 +395,9 @@ IPCCommandResult SDIOSlot0::ResetCard(const IOCtlRequest& request)
{
INFO_LOG(IOS_SD, "IOCTL_RESETCARD");
- if (m_Card)
- m_Status |= CARD_INITIALIZED;
-
// Returns 16bit RCA and 16bit 0s (meaning success)
- Memory::Write_U32(0x9f620000, request.buffer_out);
+ Memory::Write_U32(m_status, request.buffer_out);
+
return GetDefaultReply(IPC_SUCCESS);
}
@@ -412,23 +434,47 @@ IPCCommandResult SDIOSlot0::SendCommand(const IOCtlRequest& request)
IPCCommandResult SDIOSlot0::GetStatus(const IOCtlRequest& request)
{
- if (SConfig::GetInstance().m_WiiSDCard)
- m_Status |= CARD_INSERTED;
- else
- m_Status = CARD_NOT_EXIST;
+ // Since IOS does the SD initialization itself, we just say we're always initialized.
+ if (m_card)
+ {
+ if (m_card.GetSize() < SDHC_BYTES)
+ {
+ // No further initialization required.
+ m_status |= CARD_INITIALIZED;
+ }
+ else
+ {
+ // Some IOS versions support SDHC.
+ // Others will work if they are manually initialized (SEND_IF_COND)
+ if (m_sdhc_supported)
+ {
+ // All of the initialization is done internally by IOS, so we get to skip some steps.
+ InitSDHC();
+ }
+ m_status |= CARD_SDHC;
+ }
+ }
+
+ // Evaluate whether a card is currently inserted (config value).
+ // Make sure we don't modify m_status so we don't lose track of whether the card is SDHC.
+ const u32 status =
+ SConfig::GetInstance().m_WiiSDCard ? (m_status | CARD_INSERTED) : CARD_NOT_EXIST;
- INFO_LOG(IOS_SD, "IOCTL_GETSTATUS. Replying that SD card is %s%s",
- (m_Status & CARD_INSERTED) ? "inserted" : "not present",
- (m_Status & CARD_INITIALIZED) ? " and initialized" : "");
+ INFO_LOG(IOS_SD, "IOCTL_GETSTATUS. Replying that %s card is %s%s",
+ (status & CARD_SDHC) ? "SDHC" : "SD",
+ (status & CARD_INSERTED) ? "inserted" : "not present",
+ (status & CARD_INITIALIZED) ? " and initialized" : "");
- Memory::Write_U32(m_Status, request.buffer_out);
+ Memory::Write_U32(status, request.buffer_out);
return GetDefaultReply(IPC_SUCCESS);
}
IPCCommandResult SDIOSlot0::GetOCRegister(const IOCtlRequest& request)
{
- INFO_LOG(IOS_SD, "IOCTL_GETOCR");
- Memory::Write_U32(0x80ff8000, request.buffer_out);
+ u32 ocr = GetOCRegister();
+ INFO_LOG(IOS_SD, "IOCTL_GETOCR. Replying with ocr %x", ocr);
+ Memory::Write_U32(ocr, request.buffer_out);
+
return GetDefaultReply(IPC_SUCCESS);
}
@@ -444,6 +490,167 @@ IPCCommandResult SDIOSlot0::SendCommand(const IOCtlVRequest& request)
return GetDefaultReply(return_value);
}
+
+u32 SDIOSlot0::GetOCRegister() const
+{
+ u32 ocr = 0x00ff8000;
+ if (m_status & CARD_INITIALIZED)
+ ocr |= 0x80000000;
+ if (m_status & CARD_SDHC)
+ ocr |= 0x40000000;
+ return ocr;
+}
+
+std::array<u32, 4> SDIOSlot0::GetCSDv1()
+{
+ u64 size = m_card.GetSize();
+
+ // 2048 bytes/sector
+ // We could make this dynamic to support a wider range of file sizes
+ constexpr u32 read_bl_len = 11;
+
+ // size = (c_size + 1) * (1 << (2 + c_size_mult + read_bl_len))
+ u32 c_size_mult = 0;
+ bool invalid_size = false;
+ while (size > 4096)
+ {
+ invalid_size |= size & 1;
+ size >>= 1;
+ if (++c_size_mult >= 8 + 2 + read_bl_len)
+ {
+ ERROR_LOG(IOS_SD, "SD Card is too big!");
+ // Set max values
+ size = 4096;
+ c_size_mult = 7 + 2 + read_bl_len;
+ }
+ }
+ c_size_mult -= 2 + read_bl_len;
+ --size;
+ const u32 c_size(size);
+
+ if (invalid_size)
+ WARN_LOG(IOS_SD, "SD Card size is invalid");
+ else
+ INFO_LOG(IOS_SD, "SD C_SIZE = %u, C_SIZE_MULT = %u", c_size, c_size_mult);
+
+ // 0b00 CSD_STRUCTURE (SDv1)
+ // 0b000000 reserved
+ // 0b01111111 TAAC (8.0 * 10ms)
+ // 0b00000000 NSAC
+ // 0b00110010 TRAN_SPEED (2.5 * 10 Mbit/s, max operating frequency)
+
+ // 0b010110110101 CCC
+ // 0b1111 READ_BL_LEN (2048 bytes)
+ // 0b1 READ_BL_PARTIAL
+ // 0b0 WRITE_BL_MISALIGN
+ // 0b0 READ_BLK_MISALIGN
+ // 0b0 DSR_IMP (no driver stage register implemented)
+ // 0b00 reserved
+ // 0b?????????? C_SIZE (most significant 10 bits)
+
+ // 0b?? C_SIZE (least significant 2 bits)
+ // 0b111 VDD_R_CURR_MIN (100 mA)
+ // 0b111 VDD_R_CURR_MAX (100 mA)
+ // 0b111 VDD_W_CURR_MIN (100 mA)
+ // 0b111 VDD_W_CURR_MAX (100 mA)
+ // 0b??? C_SIZE_MULT
+ // 0b1 ERASE_BLK_EN (erase unit = 512 bytes)
+ // 0b1111111 SECTOR_SIZE (128 write blocks)
+ // 0b0000000 WP_GRP_SIZE
+
+ // 0b0 WP_GRP_ENABLE (no write protection)
+ // 0b00 reserved
+ // 0b001 R2W_FACTOR (write half as fast as read)
+ // 0b1111 WRITE_BL_LEN (= READ_BL_LEN)
+ // 0b0 WRITE_BL_PARTIAL (no partial block writes)
+ // 0b00000 reserved
+ // 0b0 FILE_FORMAT_GRP (default)
+ // 0b1 COPY (contents are copied)
+ // 0b0 PERM_WRITE_PROTECT (not permanently write protected)
+ // 0b0 TMP_READ_PROTECT (not temporarily write protected)
+ // 0b00 FILE_FORMAT (contains partition table)
+ // 0b00 reserved
+ // 0b??????? CRC
+ // 0b1 reserved
+
+ // TODO: CRC7 (but so far it looks like nobody is actually verifying this)
+ constexpr u32 crc = 0;
+
+ // Form the csd using the description above
+ return {
+ 0x007f003, 0x5b5f8000 | (c_size >> 2), 0x3ffc7f80 | (c_size << 30) | (c_size_mult << 15),
+ 0x07c04001 | (crc << 1),
+ };
+}
+
+std::array<u32, 4> SDIOSlot0::GetCSDv2()
+{
+ const u64 size = m_card.GetSize();
+
+ if (size % (512 * 1024) != 0)
+ WARN_LOG(IOS_SD, "SDHC Card size cannot be divided by 1024 * 512");
+
+ const u32 c_size(size / (512 * 1024) - 1);
+
+ // 0b01 CSD_STRUCTURE (SDv2)
+ // 0b000000 reserved
+ // 0b00001110 TAAC (1.0 * 1ms)
+ // 0b00000000 NSAC
+ // 0b01011010 TRAN_SPEED (5.0 * 10 Mbit/s, max operating frequency)
+
+ // 0b010111110101 CCC (TODO: Figure out what each command class does)
+ // 0b1001 READ_BL_LEN (512 bytes, fixed for SDHC)
+ // 0b0 READ_BL_PARTIAL
+ // 0b0 WRITE_BLK_MISALIGN
+ // 0b0 READ_BLK_MISALIGN
+ // 0b0 DSR_IMP (no driver stage register implemented)
+ // 0b000000 reserved
+ // 0b?????? C_SIZE (most significant 6 bits)
+
+ // 0b???????????????? C_SIZE (least significant 16 bits)
+ // 0b0 reserved
+ // 0b1 ERASE_BLK_EN
+ // 0b1111111 SECTOR_SIZE
+ // 0b0000000 WP_GRP_SIZE (not supported in SDHC)
+
+ // 0b0 WP_GRP_ENABLE
+ // 0b00 reserved
+ // 0b010 R2W_FACTOR (x4)
+ // 0b1001 WRITE_BL_LEN (512 bytes)
+ // 0b0 WRITE_BL_PARTIAL
+ // 0b00000 reserved
+ // 0b0 FILE_FORMAT_GRP
+ // 0b0 COPY
+ // 0b0 PERM_WRITE_PROTECT
+ // 0b0 TMP_WRITE_PROTECT
+ // 0b00 FILE_FORMAT
+ // 0b00 reserved
+ // 0b0000000 CRC
+ // 0b1 reserved
+
+ // TODO: CRC7 (but so far it looks like nobody is actually verifying this)
+ constexpr u32 crc = 0;
+
+ // Form the csd using the description above
+ return {
+ 0x400e005a, 0x5f590000 | (c_size >> 16), 0x00007f80 | (c_size << 16), 0x0a400001 | (crc << 1),
+ };
+}
+
+u64 SDIOSlot0::GetAddressFromRequest(u32 arg) const
+{
+ u64 address(arg);
+ if (m_status & CARD_SDHC)
+ address *= 512;
+ return address;
+}
+
+void SDIOSlot0::InitSDHC()
+{
+ m_protocol = SDProtocol::V2;
+ m_status |= CARD_INITIALIZED;
+}
+
} // namespace Device
} // namespace HLE
} // namespace IOS
diff --git a/Source/Core/Core/IOS/SDIO/SDIOSlot0.h b/Source/Core/Core/IOS/SDIO/SDIOSlot0.h
index 9ca7729dd0..6f0f0255c6 100644
--- a/Source/Core/Core/IOS/SDIO/SDIOSlot0.h
+++ b/Source/Core/Core/IOS/SDIO/SDIOSlot0.h
@@ -77,6 +77,7 @@ private:
CARD_NOT_EXIST = 0,
CARD_INSERTED = 1,
CARD_INITIALIZED = 0x10000,
+ CARD_SDHC = 0x100000,
};
// Commands
@@ -111,6 +112,15 @@ private:
EVENT_INVALID = 0xc210000
};
+ enum class SDProtocol
+ {
+ V1,
+ V2,
+ };
+
+ // Number of bytes to trigger using SDHC instead of SDSC
+ static constexpr u32 SDHC_BYTES = 0x80000000;
+
struct Event
{
Event(EventType type_, Request request_) : type(type_), request(request_) {}
@@ -131,17 +141,32 @@ private:
s32 ExecuteCommand(const Request& request, u32 BufferIn, u32 BufferInSize, u32 BufferIn2,
u32 BufferInSize2, u32 _BufferOut, u32 BufferOutSize);
void OpenInternal();
+ void InitStatus();
+
+ u32 GetOCRegister() const;
+
+ std::array<u32, 4> GetCSDv1();
+ std::array<u32, 4> GetCSDv2();
+ void InitSDHC();
+
+ u64 GetAddressFromRequest(u32 arg) const;
// TODO: do we need more than one?
std::unique_ptr<Event> m_event;
- u32 m_Status = CARD_NOT_EXIST;
- u32 m_BlockLength = 0;
- u32 m_BusWidth = 0;
+ u32 m_status = CARD_NOT_EXIST;
+ SDProtocol m_protocol = SDProtocol::V1;
+
+ // Is SDHC supported by the IOS?
+ // Other IOS requires manual SDHC initialization
+ const bool m_sdhc_supported;
+
+ u32 m_block_length = 0;
+ u32 m_bus_width = 0;
std::array<u32, 0x200 / sizeof(u32)> m_registers;
- File::IOFile m_Card;
+ File::IOFile m_card;
};
} // namespace Device
} // namespace HLE