mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-02-03 11:32:43 +01:00
Initial implementation of reads and writes
No idea if this actually works
This commit is contained in:
parent
bd895d0a15
commit
40c4f3e8c1
@ -43,7 +43,14 @@ CEXISD::CEXISD(Core::System& system) : IEXIDevice(system)
|
|||||||
|
|
||||||
void CEXISD::ImmWrite(u32 data, u32 size)
|
void CEXISD::ImmWrite(u32 data, u32 size)
|
||||||
{
|
{
|
||||||
if (inited)
|
if (state == State::Uninitialized || state == State::GetId)
|
||||||
|
{
|
||||||
|
// Get ID command
|
||||||
|
INFO_LOG_FMT(EXPANSIONINTERFACE, "SD: EXI_GetID detected (size = {:x}, data = {:x})", size,
|
||||||
|
data);
|
||||||
|
state = State::GetId;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
while (size--)
|
while (size--)
|
||||||
{
|
{
|
||||||
@ -52,23 +59,19 @@ void CEXISD::ImmWrite(u32 data, u32 size)
|
|||||||
data <<= 8;
|
data <<= 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (size == 2 && data == 0)
|
|
||||||
{
|
|
||||||
// Get ID command
|
|
||||||
INFO_LOG_FMT(EXPANSIONINTERFACE, "SD: EXI_GetID detected (size = {:x}, data = {:x})", size,
|
|
||||||
data);
|
|
||||||
get_id = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 CEXISD::ImmRead(u32 size)
|
u32 CEXISD::ImmRead(u32 size)
|
||||||
{
|
{
|
||||||
if (get_id)
|
if (state == State::Uninitialized)
|
||||||
|
{
|
||||||
|
// ?
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (state == State::GetId)
|
||||||
{
|
{
|
||||||
// This is not a good way of handling state
|
|
||||||
inited = true;
|
|
||||||
get_id = false;
|
|
||||||
INFO_LOG_FMT(EXPANSIONINTERFACE, "SD: EXI_GetID finished (size = {:x})", size);
|
INFO_LOG_FMT(EXPANSIONINTERFACE, "SD: EXI_GetID finished (size = {:x})", size);
|
||||||
|
state = State::ReadyForCommand;
|
||||||
// Same signed/unsigned mismatch in libogc; it wants -1
|
// Same signed/unsigned mismatch in libogc; it wants -1
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -103,61 +106,73 @@ bool CEXISD::IsPresent() const
|
|||||||
|
|
||||||
void CEXISD::DoState(PointerWrap& p)
|
void CEXISD::DoState(PointerWrap& p)
|
||||||
{
|
{
|
||||||
p.Do(inited);
|
p.Do(state);
|
||||||
p.Do(get_id);
|
|
||||||
p.Do(next_is_appcmd);
|
|
||||||
p.Do(command_position);
|
p.Do(command_position);
|
||||||
p.Do(block_position);
|
|
||||||
p.DoArray(command_buffer);
|
p.DoArray(command_buffer);
|
||||||
p.Do(response);
|
p.Do(response);
|
||||||
|
p.Do(block_position);
|
||||||
p.DoArray(block_buffer);
|
p.DoArray(block_buffer);
|
||||||
|
p.Do(address);
|
||||||
|
p.Do(block_crc);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEXISD::WriteByte(u8 byte)
|
void CEXISD::WriteByte(u8 byte)
|
||||||
{
|
{
|
||||||
// TODO: Write-protect inversion(?)
|
if (state == State::SingleBlockRead || state == State::MultipleBlockRead)
|
||||||
if (command_position == 0)
|
|
||||||
{
|
{
|
||||||
if ((byte & 0b11000000) == 0b01000000)
|
WriteForBlockRead(byte);
|
||||||
{
|
|
||||||
INFO_LOG_FMT(EXPANSIONINTERFACE, "EXI SD command started: {:02x}", byte);
|
|
||||||
command_buffer[command_position++] = byte;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (command_position < 6)
|
else if (state == State::SingleBlockWrite || state == State::MultipleBlockWrite)
|
||||||
{
|
{
|
||||||
command_buffer[command_position++] = byte;
|
WriteForBlockWrite(byte);
|
||||||
|
}
|
||||||
if (command_position == 6)
|
else
|
||||||
|
{
|
||||||
|
// TODO: Write-protect inversion(?)
|
||||||
|
if (command_position == 0)
|
||||||
{
|
{
|
||||||
// Buffer now full
|
if ((byte & 0b11000000) == 0b01000000)
|
||||||
command_position = 0;
|
|
||||||
|
|
||||||
u8 hash = (Common::HashCrc7(command_buffer.data(), 5) << 1) | 1;
|
|
||||||
if (byte != hash)
|
|
||||||
{
|
{
|
||||||
WARN_LOG_FMT(EXPANSIONINTERFACE,
|
INFO_LOG_FMT(EXPANSIONINTERFACE, "EXI SD command started: {:02x}", byte);
|
||||||
"EXI SD command invalid, incorrect CRC7 or missing end bit: got {:02x}, "
|
command_buffer[command_position++] = byte;
|
||||||
"should be {:02x}",
|
|
||||||
byte, hash);
|
|
||||||
response.push_back(static_cast<u8>(R1::CommunicationCRCError));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else if (command_position < 6)
|
||||||
|
{
|
||||||
|
command_buffer[command_position++] = byte;
|
||||||
|
|
||||||
u8 command = command_buffer[0] & 0x3f;
|
if (command_position == 6)
|
||||||
u32 argument = command_buffer[1] << 24 | command_buffer[2] << 16 | command_buffer[3] << 8 |
|
|
||||||
command_buffer[4];
|
|
||||||
|
|
||||||
INFO_LOG_FMT(EXPANSIONINTERFACE, "EXI SD command received: {:02x} {:08x}", command, argument);
|
|
||||||
|
|
||||||
if (next_is_appcmd)
|
|
||||||
{
|
{
|
||||||
next_is_appcmd = false;
|
// Buffer now full
|
||||||
HandleAppCommand(static_cast<AppCommand>(command), argument);
|
command_position = 0;
|
||||||
}
|
|
||||||
else
|
u8 hash = (Common::HashCrc7(command_buffer.data(), 5) << 1) | 1;
|
||||||
{
|
if (byte != hash)
|
||||||
HandleCommand(static_cast<Command>(command), argument);
|
{
|
||||||
|
WARN_LOG_FMT(EXPANSIONINTERFACE,
|
||||||
|
"EXI SD command invalid, incorrect CRC7 or missing end bit: got {:02x}, "
|
||||||
|
"should be {:02x}",
|
||||||
|
byte, hash);
|
||||||
|
response.push_back(static_cast<u8>(R1::CommunicationCRCError));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 command = command_buffer[0] & 0x3f;
|
||||||
|
u32 argument = command_buffer[1] << 24 | command_buffer[2] << 16 | command_buffer[3] << 8 |
|
||||||
|
command_buffer[4];
|
||||||
|
|
||||||
|
INFO_LOG_FMT(EXPANSIONINTERFACE, "EXI SD command received: {:02x} {:08x}", command,
|
||||||
|
argument);
|
||||||
|
|
||||||
|
if (state == State::ReadyForAppCommand)
|
||||||
|
{
|
||||||
|
state = State::ReadyForCommand;
|
||||||
|
HandleAppCommand(static_cast<AppCommand>(command), argument);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HandleCommand(static_cast<Command>(command), argument);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -174,7 +189,7 @@ void CEXISD::HandleCommand(Command command, u32 argument)
|
|||||||
{
|
{
|
||||||
// Used by libogc for non-SDHC cards
|
// Used by libogc for non-SDHC cards
|
||||||
bool hcs = argument & (1 << 30); // Host Capacity Support (for SDHC/SDXC cards)
|
bool hcs = argument & (1 << 30); // Host Capacity Support (for SDHC/SDXC cards)
|
||||||
(void)hcs;
|
INFO_LOG_FMT(EXPANSIONINTERFACE, "SD Host Capacity Support: {}", hcs);
|
||||||
response.push_back(0); // R1 - not idle
|
response.push_back(0); // R1 - not idle
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -225,7 +240,7 @@ void CEXISD::HandleCommand(Command command, u32 argument)
|
|||||||
// R1
|
// R1
|
||||||
response.push_back(0);
|
response.push_back(0);
|
||||||
// Data ready token
|
// Data ready token
|
||||||
response.push_back(0xfe);
|
response.push_back(START_BLOCK);
|
||||||
// CSD
|
// CSD
|
||||||
// 0b00 CSD_STRUCTURE (SDv1)
|
// 0b00 CSD_STRUCTURE (SDv1)
|
||||||
// 0b000000 reserved
|
// 0b000000 reserved
|
||||||
@ -302,7 +317,7 @@ void CEXISD::HandleCommand(Command command, u32 argument)
|
|||||||
// R1
|
// R1
|
||||||
response.push_back(0);
|
response.push_back(0);
|
||||||
// Data ready token
|
// Data ready token
|
||||||
response.push_back(0xfe);
|
response.push_back(START_BLOCK);
|
||||||
// The CID -- no idea what the format is, copied from SDIOSlot0
|
// The CID -- no idea what the format is, copied from SDIOSlot0
|
||||||
std::array<u8, 16> cid = {
|
std::array<u8, 16> cid = {
|
||||||
0x80, 0x11, 0x4d, 0x1c, 0x80, 0x08, 0x00, 0x00,
|
0x80, 0x11, 0x4d, 0x1c, 0x80, 0x08, 0x00, 0x00,
|
||||||
@ -326,9 +341,29 @@ void CEXISD::HandleCommand(Command command, u32 argument)
|
|||||||
response.push_back(0); // R1
|
response.push_back(0); // R1
|
||||||
break;
|
break;
|
||||||
case Command::AppCmd:
|
case Command::AppCmd:
|
||||||
next_is_appcmd = true;
|
state = State::ReadyForAppCommand;
|
||||||
response.push_back(0); // R1
|
response.push_back(0); // R1
|
||||||
break;
|
break;
|
||||||
|
case Command::ReadSingleBlock:
|
||||||
|
state = State::SingleBlockRead;
|
||||||
|
block_state = BlockState::Response;
|
||||||
|
address = argument;
|
||||||
|
break;
|
||||||
|
case Command::ReadMultipleBlock:
|
||||||
|
state = State::MultipleBlockRead;
|
||||||
|
block_state = BlockState::Response;
|
||||||
|
address = argument;
|
||||||
|
break;
|
||||||
|
case Command::WriteSingleBlock:
|
||||||
|
state = State::SingleBlockWrite;
|
||||||
|
block_state = BlockState::Response;
|
||||||
|
address = argument;
|
||||||
|
break;
|
||||||
|
case Command::WriteMultipleBlock:
|
||||||
|
state = State::MultipleBlockWrite;
|
||||||
|
block_state = BlockState::Response;
|
||||||
|
address = argument;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// Don't know it
|
// Don't know it
|
||||||
WARN_LOG_FMT(EXPANSIONINTERFACE, "Unimplemented SD command {:02x} {:08x}",
|
WARN_LOG_FMT(EXPANSIONINTERFACE, "Unimplemented SD command {:02x} {:08x}",
|
||||||
@ -343,9 +378,10 @@ void CEXISD::HandleAppCommand(AppCommand app_command, u32 argument)
|
|||||||
{
|
{
|
||||||
case AppCommand::SDStatus:
|
case AppCommand::SDStatus:
|
||||||
{
|
{
|
||||||
response.push_back(0); // R1
|
response.push_back(0); // R1
|
||||||
response.push_back(0); // R2
|
response.push_back(0); // R2
|
||||||
response.push_back(0xfe); // Data ready token
|
// Data ready token
|
||||||
|
response.push_back(START_BLOCK);
|
||||||
// All-zero for now
|
// All-zero for now
|
||||||
std::array<u8, 64> status = {};
|
std::array<u8, 64> status = {};
|
||||||
for (auto byte : status)
|
for (auto byte : status)
|
||||||
@ -362,10 +398,18 @@ void CEXISD::HandleAppCommand(AppCommand app_command, u32 argument)
|
|||||||
{
|
{
|
||||||
// Used by Pokémon Channel for all cards, and libogc for SDHC cards
|
// Used by Pokémon Channel for all cards, and libogc for SDHC cards
|
||||||
bool hcs = argument & (1 << 30); // Host Capacity Support (for SDHC/SDXC cards)
|
bool hcs = argument & (1 << 30); // Host Capacity Support (for SDHC/SDXC cards)
|
||||||
(void)hcs;
|
INFO_LOG_FMT(EXPANSIONINTERFACE, "SD Host Capacity Support: {}", hcs);
|
||||||
response.push_back(0); // R1 - not idle
|
response.push_back(0); // R1 - not idle
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case AppCommand::AppCmd:
|
||||||
|
// According to the spec, any unknown app command should be treated as a regular command, but
|
||||||
|
// also things should not use this functionality. It also specifically mentions that sending
|
||||||
|
// CMD55 multiple times is the same as sending it only once: the next command that isn't 55 is
|
||||||
|
// treated as an app command.
|
||||||
|
state = State::ReadyForAppCommand;
|
||||||
|
response.push_back(0); // R1 - not idle
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// Don't know it
|
// Don't know it
|
||||||
WARN_LOG_FMT(EXPANSIONINTERFACE, "Unimplemented SD app command {:02x} {:08x}",
|
WARN_LOG_FMT(EXPANSIONINTERFACE, "Unimplemented SD app command {:02x} {:08x}",
|
||||||
@ -376,16 +420,223 @@ void CEXISD::HandleAppCommand(AppCommand app_command, u32 argument)
|
|||||||
|
|
||||||
u8 CEXISD::ReadByte()
|
u8 CEXISD::ReadByte()
|
||||||
{
|
{
|
||||||
if (response.empty())
|
if (state == State::SingleBlockRead || state == State::MultipleBlockRead)
|
||||||
{
|
{
|
||||||
// WARN_LOG_FMT(EXPANSIONINTERFACE, "Attempted to read from empty SD queue");
|
return ReadForBlockRead();
|
||||||
return 0xFF;
|
}
|
||||||
|
else if (state == State::SingleBlockWrite || state == State::MultipleBlockWrite)
|
||||||
|
{
|
||||||
|
return ReadForBlockWrite();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
u8 result = response.front();
|
if (response.empty())
|
||||||
response.pop_front();
|
{
|
||||||
|
// WARN_LOG_FMT(EXPANSIONINTERFACE, "Attempted to read from empty SD queue");
|
||||||
|
return 0xff;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u8 result = response.front();
|
||||||
|
response.pop_front();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 CEXISD::ReadForBlockRead()
|
||||||
|
{
|
||||||
|
switch (block_state)
|
||||||
|
{
|
||||||
|
case BlockState::Response:
|
||||||
|
{
|
||||||
|
if (!m_card.Seek(address, File::SeekOrigin::Begin))
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(EXPANSIONINTERFACE, "fseeko failed WTF");
|
||||||
|
block_state = BlockState::Token;
|
||||||
|
}
|
||||||
|
else if (!m_card.ReadBytes(block_buffer.data(), block_buffer.size()))
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(EXPANSIONINTERFACE, "SD read failed - error: {}, eof: {}",
|
||||||
|
ferror(m_card.GetHandle()), feof(m_card.GetHandle()));
|
||||||
|
block_state = BlockState::Token;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
block_position = 0;
|
||||||
|
block_state = BlockState::Block;
|
||||||
|
block_crc = Common::HashCrc16(block_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Would return address error or parameter error here
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case BlockState::Token:
|
||||||
|
// A bit awkward of a setup; a data error token is only read on an actual error.
|
||||||
|
// For now only use the generic error, not e.g. out of bounds
|
||||||
|
// (which can be handled in the main response... why are there 2 ways?)
|
||||||
|
state = State::ReadyForCommand;
|
||||||
|
block_state = BlockState::Nothing;
|
||||||
|
return DATA_ERROR_ERROR;
|
||||||
|
case BlockState::Block:
|
||||||
|
{
|
||||||
|
u8 result = block_buffer[block_position++];
|
||||||
|
if (block_position > BLOCK_SIZE)
|
||||||
|
{
|
||||||
|
block_state = BlockState::Checksum1;
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
case BlockState::Checksum1:
|
||||||
|
block_state = BlockState::Checksum2;
|
||||||
|
return static_cast<u8>(block_crc >> 8);
|
||||||
|
case BlockState::Checksum2:
|
||||||
|
{
|
||||||
|
u8 result = static_cast<u8>(block_crc);
|
||||||
|
if (state == State::MultipleBlockRead)
|
||||||
|
{
|
||||||
|
if (!m_card.ReadBytes(block_buffer.data(), block_buffer.size()))
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(EXPANSIONINTERFACE, "SD read failed - error: {}, eof: {}",
|
||||||
|
ferror(m_card.GetHandle()), feof(m_card.GetHandle()));
|
||||||
|
block_state = BlockState::Token;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
INFO_LOG_FMT(EXPANSIONINTERFACE, "SD read succeeded");
|
||||||
|
block_position = 0;
|
||||||
|
block_state = BlockState::Block;
|
||||||
|
block_crc = Common::HashCrc16(block_buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
block_state = BlockState::Nothing;
|
||||||
|
state = State::ReadyForCommand;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
ERROR_LOG_FMT(EXPANSIONINTERFACE, "Unexpected block_state {} for reading", u32(block_state));
|
||||||
|
return 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXISD::WriteForBlockRead(u8 byte)
|
||||||
|
{
|
||||||
|
if (byte != 0xff)
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(EXPANSIONINTERFACE, "Data written during block read: {:02x}", byte);
|
||||||
|
}
|
||||||
|
// TODO: Read the whole command
|
||||||
|
if (((byte & 0b11000000) == 0b01000000) &&
|
||||||
|
static_cast<Command>(byte & 0x3f) == Command::StopTransmission)
|
||||||
|
{
|
||||||
|
// Finish transmitting the current block and then stop
|
||||||
|
state = State::SingleBlockRead;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 CEXISD::ReadForBlockWrite()
|
||||||
|
{
|
||||||
|
switch (block_state)
|
||||||
|
{
|
||||||
|
case BlockState::Response:
|
||||||
|
block_state = BlockState::Token;
|
||||||
|
// Would return address error or parameter error here
|
||||||
|
return 0;
|
||||||
|
case BlockState::Token:
|
||||||
|
case BlockState::Block:
|
||||||
|
case BlockState::Checksum1:
|
||||||
|
case BlockState::Checksum2:
|
||||||
|
return 0xff;
|
||||||
|
case BlockState::ChecksumWritten:
|
||||||
|
{
|
||||||
|
u16 actual_crc = Common::HashCrc16(block_buffer);
|
||||||
|
u8 result;
|
||||||
|
if (actual_crc != block_crc)
|
||||||
|
{
|
||||||
|
result = DATA_RESPONSE_BAD_CRC;
|
||||||
|
}
|
||||||
|
else if (!m_card.Seek(address, File::SeekOrigin::Begin))
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(EXPANSIONINTERFACE, "fseeko failed WTF");
|
||||||
|
result = DATA_RESPONSE_WRITE_ERROR;
|
||||||
|
}
|
||||||
|
else if (!m_card.WriteBytes(block_buffer.data(), block_buffer.size()))
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(EXPANSIONINTERFACE, "SD write failed - error: {}, eof: {}",
|
||||||
|
ferror(m_card.GetHandle()), feof(m_card.GetHandle()));
|
||||||
|
result = DATA_RESPONSE_WRITE_ERROR;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
INFO_LOG_FMT(EXPANSIONINTERFACE, "SD write succeeded");
|
||||||
|
result = DATA_RESPONSE_ACCEPTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == State::SingleBlockWrite)
|
||||||
|
{
|
||||||
|
state = State::ReadyForCommand;
|
||||||
|
block_state = BlockState::Nothing;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
block_state = BlockState::Token;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
ERROR_LOG_FMT(EXPANSIONINTERFACE, "Unexpected block_state {} for writing", u32(block_state));
|
||||||
|
return 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXISD::WriteForBlockWrite(u8 byte)
|
||||||
|
{
|
||||||
|
switch (block_state)
|
||||||
|
{
|
||||||
|
case BlockState::Response:
|
||||||
|
// Do nothing
|
||||||
|
break;
|
||||||
|
case BlockState::Token:
|
||||||
|
if (byte == START_MULTI_BLOCK)
|
||||||
|
{
|
||||||
|
block_position = 0;
|
||||||
|
block_crc = 0;
|
||||||
|
block_state = BlockState::Block;
|
||||||
|
}
|
||||||
|
else if (byte == END_BLOCK)
|
||||||
|
{
|
||||||
|
state = State::ReadyForCommand;
|
||||||
|
block_state = BlockState::Nothing;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(EXPANSIONINTERFACE, "Unexpected token for block write {:02x}", byte);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BlockState::Block:
|
||||||
|
block_buffer[block_position++] = byte;
|
||||||
|
if (block_position > BLOCK_SIZE)
|
||||||
|
{
|
||||||
|
block_state = BlockState::Checksum1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BlockState::Checksum1:
|
||||||
|
block_crc |= byte << 8;
|
||||||
|
block_state = BlockState::Checksum2;
|
||||||
|
break;
|
||||||
|
case BlockState::Checksum2:
|
||||||
|
block_crc |= byte << 8;
|
||||||
|
block_state = BlockState::ChecksumWritten;
|
||||||
|
break;
|
||||||
|
case BlockState::ChecksumWritten:
|
||||||
|
// Do nothing
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ERROR_LOG_FMT(EXPANSIONINTERFACE, "Unexpected block_state {} for writing", u32(block_state));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} // namespace ExpansionInterface
|
} // namespace ExpansionInterface
|
||||||
|
@ -121,11 +121,54 @@ private:
|
|||||||
// 56-59 Reserved for security spec
|
// 56-59 Reserved for security spec
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static constexpr u8 START_BLOCK = 0xfe, START_MULTI_BLOCK = 0xfc, END_BLOCK = 0xfd;
|
||||||
|
// The spec has the first 3 bits of the data responses marked with an x, and doesn't explain why
|
||||||
|
static constexpr u8 DATA_RESPONSE_ACCEPTED = 0b0'010'0;
|
||||||
|
static constexpr u8 DATA_RESPONSE_BAD_CRC = 0b0'101'0;
|
||||||
|
static constexpr u8 DATA_RESPONSE_WRITE_ERROR = 0b0'110'0;
|
||||||
|
// "Same error bits" as in R2, but I guess that only refers to meaning, not the actual bit values
|
||||||
|
static constexpr u8 DATA_ERROR_ERROR = 0x01;
|
||||||
|
static constexpr u8 DATA_ERROR_CONTROLLER = 0x02;
|
||||||
|
static constexpr u8 DATA_ERROR_ECC = 0x04;
|
||||||
|
static constexpr u8 DATA_ERROR_OUT_OF_RANGE = 0x08;
|
||||||
|
|
||||||
|
static constexpr size_t BLOCK_SIZE = 512;
|
||||||
|
|
||||||
|
enum class State
|
||||||
|
{
|
||||||
|
// Hacky setup
|
||||||
|
Uninitialized,
|
||||||
|
GetId,
|
||||||
|
// Actual states for transmiting and receiving
|
||||||
|
ReadyForCommand,
|
||||||
|
ReadyForAppCommand,
|
||||||
|
SingleBlockRead,
|
||||||
|
MultipleBlockRead,
|
||||||
|
SingleBlockWrite,
|
||||||
|
MultipleBlockWrite,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class BlockState
|
||||||
|
{
|
||||||
|
Nothing,
|
||||||
|
Response,
|
||||||
|
Token,
|
||||||
|
Block,
|
||||||
|
Checksum1,
|
||||||
|
Checksum2,
|
||||||
|
ChecksumWritten,
|
||||||
|
};
|
||||||
|
|
||||||
void WriteByte(u8 byte);
|
void WriteByte(u8 byte);
|
||||||
void HandleCommand(Command command, u32 argument);
|
void HandleCommand(Command command, u32 argument);
|
||||||
void HandleAppCommand(AppCommand app_command, u32 argument);
|
void HandleAppCommand(AppCommand app_command, u32 argument);
|
||||||
u8 ReadByte();
|
u8 ReadByte();
|
||||||
|
|
||||||
|
u8 ReadForBlockRead();
|
||||||
|
void WriteForBlockRead(u8 byte);
|
||||||
|
u8 ReadForBlockWrite();
|
||||||
|
void WriteForBlockWrite(u8 byte);
|
||||||
|
|
||||||
enum class R1
|
enum class R1
|
||||||
{
|
{
|
||||||
InIdleState = 1 << 0,
|
InIdleState = 1 << 0,
|
||||||
@ -152,13 +195,14 @@ private:
|
|||||||
File::IOFile m_card;
|
File::IOFile m_card;
|
||||||
|
|
||||||
// STATE_TO_SAVE
|
// STATE_TO_SAVE
|
||||||
bool inited = false;
|
State state = State::Uninitialized;
|
||||||
bool get_id = false;
|
BlockState block_state = BlockState::Nothing;
|
||||||
bool next_is_appcmd = false;
|
|
||||||
u32 command_position = 0;
|
u32 command_position = 0;
|
||||||
u32 block_position = 0;
|
u32 block_position = 0;
|
||||||
std::array<u8, 6> command_buffer = {};
|
std::array<u8, 6> command_buffer = {};
|
||||||
std::deque<u8> response;
|
std::deque<u8> response = {};
|
||||||
std::array<u8, 512> block_buffer = {};
|
std::array<u8, BLOCK_SIZE> block_buffer = {};
|
||||||
|
u64 address = 0;
|
||||||
|
u16 block_crc = 0;
|
||||||
};
|
};
|
||||||
} // namespace ExpansionInterface
|
} // namespace ExpansionInterface
|
||||||
|
Loading…
x
Reference in New Issue
Block a user