@@ -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