mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2025-01-18 11:16:10 +00:00
sdmmc: implement BaseDeviceAccessor
This commit is contained in:
parent
631de13133
commit
3981527bb8
9 changed files with 1166 additions and 2 deletions
|
@ -40,6 +40,25 @@ namespace ams::sdmmc {
|
|||
R_DEFINE_ERROR_RESULT(AutoCommandResponseTimeoutError, 44);
|
||||
R_DEFINE_ERROR_RESULT(CommandCompleteSoftwareTimeout, 45);
|
||||
R_DEFINE_ERROR_RESULT(TransferCompleteSoftwareTimeout, 46);
|
||||
R_DEFINE_ERROR_RANGE(DeviceStatusHasError, 48, 70);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusAddressOutOfRange, 49);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusAddressMisaligned, 50);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusBlockLenError, 51);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusEraseSeqError, 52);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusEraseParam, 53);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusWpViolation, 54);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusLockUnlockFailed, 55);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusComCrcError, 56);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusIllegalCommand, 57);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusDeviceEccFailed, 58);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusCcError, 59);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusError, 60);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusCidCsdOverwrite, 61);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusWpEraseSkip, 62);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusEraseReset, 63);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusSwitchError, 64);
|
||||
R_DEFINE_ERROR_RESULT(UnexpectedDeviceState, 72);
|
||||
R_DEFINE_ERROR_RESULT(UnexpectedDeviceCsdValue, 73);
|
||||
R_DEFINE_ERROR_RESULT(AbortTransactionSoftwareTimeout, 74);
|
||||
R_DEFINE_ERROR_RESULT(CommandInhibitCmdSoftwareTimeout, 75);
|
||||
R_DEFINE_ERROR_RESULT(CommandInhibitDatSoftwareTimeout, 76);
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
//#define AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS
|
||||
//#define AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL
|
||||
//#define AMS_SDMMC_USE_DEVICE_DETECTOR
|
||||
//#define AMS_SDMMC_USE_LOGGER
|
||||
//#define AMS_SDMMC_USE_OS_EVENTS
|
||||
//#define AMS_SDMMC_USE_OS_TIMER
|
||||
#define AMS_SDMMC_USE_UTIL_TIMER
|
||||
|
@ -38,6 +39,7 @@
|
|||
//#define AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS
|
||||
//#define AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL
|
||||
//#define AMS_SDMMC_USE_DEVICE_DETECTOR
|
||||
//#define AMS_SDMMC_USE_LOGGER
|
||||
//#define AMS_SDMMC_USE_OS_EVENTS
|
||||
//#define AMS_SDMMC_USE_OS_TIMER
|
||||
#define AMS_SDMMC_USE_UTIL_TIMER
|
||||
|
@ -48,6 +50,7 @@
|
|||
#define AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS
|
||||
#define AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL
|
||||
#define AMS_SDMMC_USE_DEVICE_DETECTOR
|
||||
#define AMS_SDMMC_USE_LOGGER
|
||||
#define AMS_SDMMC_USE_OS_EVENTS
|
||||
#define AMS_SDMMC_USE_OS_TIMER
|
||||
//#define AMS_SDMMC_USE_UTIL_TIMER
|
||||
|
|
|
@ -72,6 +72,8 @@ namespace ams::sdmmc {
|
|||
|
||||
using DeviceDetectionEventCallback = void (*)(void *);
|
||||
|
||||
constexpr inline size_t SectorSize = 0x200;
|
||||
|
||||
constexpr inline size_t DeviceCidSize = 0x10;
|
||||
constexpr inline size_t DeviceCsdSize = 0x10;
|
||||
|
||||
|
|
|
@ -0,0 +1,573 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
#include "sdmmc_base_device_accessor.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
#if defined(AMS_SDMMC_THREAD_SAFE)
|
||||
|
||||
#define AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX() std::scoped_lock lk(this->base_device->device_mutex)
|
||||
|
||||
#else
|
||||
|
||||
#define AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX()
|
||||
|
||||
#endif
|
||||
|
||||
void BaseDevice::GetLegacyCapacityParameters(u8 *out_c_size_mult, u8 *out_read_bl_len) const {
|
||||
AMS_ABORT_UNLESS(out_c_size_mult != nullptr);
|
||||
AMS_ABORT_UNLESS(out_read_bl_len != nullptr);
|
||||
|
||||
/* Extract C_SIZE_MULT and READ_BL_LEN from the CSD. */
|
||||
*out_c_size_mult = static_cast<u8>((this->csd[2] >> 7) & 0x7);
|
||||
*out_read_bl_len = static_cast<u8>((this->csd[4] >> 8) & 0xF);
|
||||
}
|
||||
|
||||
Result BaseDevice::SetLegacyMemoryCapacity() {
|
||||
/* Get csize from the csd. */
|
||||
const u32 c_size = ((this->csd[3] >> 6) & 0x3FF) | ((this->csd[4] & 0x3) << 10);
|
||||
|
||||
/* Get c_size_mult and read_bl_len. */
|
||||
u8 c_size_mult, read_bl_len;
|
||||
this->GetLegacyCapacityParameters(std::addressof(c_size_mult), std::addressof(read_bl_len));
|
||||
|
||||
/* Validate the parameters. */
|
||||
R_UNLESS((read_bl_len + c_size_mult + 2) >= 9, sdmmc::ResultUnexpectedDeviceCsdValue());
|
||||
|
||||
/* Set memory capacity. */
|
||||
this->memory_capacity = (c_size + 1) << ((read_bl_len + c_size_mult + 2) - 9);
|
||||
this->is_valid_memory_capacity = true;
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDevice::CheckDeviceStatus(u32 r1_resp) const {
|
||||
/* Check if there are any errors at all. */
|
||||
R_SUCCEED_IF((r1_resp & DeviceStatus_ErrorMask) == 0);
|
||||
|
||||
/* Check errors individually. */
|
||||
#define AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(__ERROR__) R_UNLESS((r1_resp & DeviceStatus_##__ERROR__) == 0, sdmmc::ResultDeviceStatus##__ERROR__())
|
||||
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(ComCrcError);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(DeviceEccFailed);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(CcError);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(Error);
|
||||
|
||||
if (this->GetDeviceType() == DeviceType_Mmc) {
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(SwitchError);
|
||||
}
|
||||
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(AddressMisaligned);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(BlockLenError);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(EraseSeqError);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(EraseParam);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(WpViolation);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(LockUnlockFailed);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(CidCsdOverwrite);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(WpEraseSkip);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(WpEraseSkip);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(EraseReset);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(IllegalCommand);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(AddressOutOfRange);
|
||||
|
||||
#undef AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
DeviceState BaseDevice::GetDeviceState(u32 r1_resp) const {
|
||||
return static_cast<DeviceState>((r1_resp & DeviceStatus_CurrentStateMask) >> DeviceStatus_CurrentStateShift);
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::IssueCommandAndCheckR1(u32 *out_response, u32 command_index, u32 command_arg, bool is_busy, DeviceState expected_state, u32 status_ignore_mask) const {
|
||||
/* Issue the command. */
|
||||
constexpr ResponseType CommandResponseType = ResponseType_R1;
|
||||
Command command(command_index, command_arg, CommandResponseType, is_busy);
|
||||
R_TRY(this->host_controller->IssueCommand(std::addressof(command)));
|
||||
|
||||
/* Get the response. */
|
||||
AMS_ABORT_UNLESS(out_response != nullptr);
|
||||
this->host_controller->GetLastResponse(out_response, sizeof(u32), CommandResponseType);
|
||||
|
||||
/* Mask out the ignored status bits. */
|
||||
if (status_ignore_mask != 0) {
|
||||
*out_response &= ~status_ignore_mask;
|
||||
}
|
||||
|
||||
/* Check the r1 response for errors. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
R_TRY(this->base_device->CheckDeviceStatus(*out_response));
|
||||
|
||||
/* Check the device state. */
|
||||
if (expected_state != DeviceState_Unknown) {
|
||||
R_UNLESS(this->base_device->GetDeviceState(*out_response) == expected_state, sdmmc::ResultUnexpectedDeviceState());
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::IssueCommandGoIdleState() const {
|
||||
/* Issue the command. */
|
||||
Command command(CommandIndex_GoIdleState, 0, ResponseType_R0, false);
|
||||
return this->host_controller->IssueCommand(std::addressof(command));
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::IssueCommandAllSendCid(void *dst, size_t dst_size) const {
|
||||
/* Issue the command. */
|
||||
constexpr ResponseType CommandResponseType = ResponseType_R2;
|
||||
Command command(CommandIndex_AllSendCid, 0, CommandResponseType, false);
|
||||
R_TRY(this->host_controller->IssueCommand(std::addressof(command)));
|
||||
|
||||
/* Copy the data out. */
|
||||
AMS_ABORT_UNLESS(dst != nullptr);
|
||||
AMS_ABORT_UNLESS(util::IsAligned(reinterpret_cast<uintptr_t>(dst), alignof(u32)));
|
||||
AMS_ABORT_UNLESS(dst_size >= DeviceCidSize);
|
||||
this->host_controller->GetLastResponse(static_cast<u32 *>(dst), DeviceCidSize, CommandResponseType);
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::IssueCommandSelectCard() const {
|
||||
/* Get the command argument. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
const u32 arg = static_cast<u32>(this->base_device->GetRca()) << 16;
|
||||
|
||||
/* Issue the command. */
|
||||
return this->IssueCommandAndCheckR1(CommandIndex_SelectCard, arg, true, DeviceState_Unknown);
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::IssueCommandSendCsd(void *dst, size_t dst_size) const {
|
||||
/* Get the command argument. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
const u32 arg = static_cast<u32>(this->base_device->GetRca()) << 16;
|
||||
|
||||
/* Issue the command. */
|
||||
constexpr ResponseType CommandResponseType = ResponseType_R2;
|
||||
Command command(CommandIndex_SendCsd, arg, CommandResponseType, false);
|
||||
R_TRY(this->host_controller->IssueCommand(std::addressof(command)));
|
||||
|
||||
/* Copy the data out. */
|
||||
AMS_ABORT_UNLESS(dst != nullptr);
|
||||
AMS_ABORT_UNLESS(util::IsAligned(reinterpret_cast<uintptr_t>(dst), alignof(u32)));
|
||||
AMS_ABORT_UNLESS(dst_size >= DeviceCsdSize);
|
||||
this->host_controller->GetLastResponse(static_cast<u32 *>(dst), DeviceCsdSize, CommandResponseType);
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::IssueCommandSendStatus(u32 *out_device_status, u32 status_ignore_mask) const {
|
||||
/* Get the command argument. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
const u32 arg = static_cast<u32>(this->base_device->GetRca()) << 16;
|
||||
|
||||
/* Issue the command. */
|
||||
return this->IssueCommandAndCheckR1(out_device_status, CommandIndex_SendStatus, arg, false, DeviceState_Tran, status_ignore_mask);
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::IssueCommandSetBlockLenToSectorSize() const {
|
||||
/* Issue the command. */
|
||||
return this->IssueCommandAndCheckR1(CommandIndex_SetBlockLen, SectorSize, false, DeviceState_Tran);
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::IssueCommandMultipleBlock(u32 *out_num_transferred_blocks, u32 sector_index, u32 num_sectors, void *buf, bool is_read) const {
|
||||
/* Get the argument. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
const u32 arg = this->base_device->IsHighCapacity() ? sector_index : sector_index * SectorSize;
|
||||
|
||||
/* Get the command index and transfer direction. */
|
||||
const u32 command_index = is_read ? CommandIndex_ReadMultipleBlock : CommandIndex_WriteMultipleBlock;
|
||||
const auto xfer_direction = is_read ? TransferDirection_ReadFromDevice : TransferDirection_WriteToDevice;
|
||||
|
||||
/* Issue the command. */
|
||||
constexpr ResponseType CommandResponseType = ResponseType_R1;
|
||||
Command command(command_index, arg, CommandResponseType, false);
|
||||
TransferData xfer_data(buf, SectorSize, num_sectors, xfer_direction, true, true);
|
||||
Result result = this->host_controller->IssueCommand(std::addressof(command), std::addressof(xfer_data), out_num_transferred_blocks);
|
||||
|
||||
/* Handle the failure case. */
|
||||
if (R_FAILED(result)) {
|
||||
/* Check if we were removed. */
|
||||
R_TRY(this->CheckRemoved());
|
||||
|
||||
/* By default, we'll want to return the result we just got. */
|
||||
Result result_to_return = result;
|
||||
|
||||
/* Stop transmission. */
|
||||
u32 resp = 0;
|
||||
result = this->host_controller->IssueStopTransmissionCommand(std::addressof(resp));
|
||||
if (R_SUCCEEDED(result)) {
|
||||
result = this->base_device->CheckDeviceStatus(resp & (~DeviceStatus_IllegalCommand));
|
||||
if (R_FAILED(result)) {
|
||||
result_to_return = result;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if we were removed. */
|
||||
R_TRY(this->CheckRemoved());
|
||||
|
||||
/* Get the device status. */
|
||||
u32 device_status = 0;
|
||||
result = this->IssueCommandSendStatus(std::addressof(device_status), DeviceStatus_IllegalCommand);
|
||||
|
||||
/* If there's a device status error we don't already have, we prefer to return it. */
|
||||
if (!sdmmc::ResultDeviceStatusHasError::Includes(result_to_return) && sdmmc::ResultDeviceStatusHasError::Includes(result)) {
|
||||
result_to_return = result;
|
||||
}
|
||||
|
||||
/* Return the result we chose. */
|
||||
return result_to_return;
|
||||
}
|
||||
|
||||
/* Get the responses. */
|
||||
u32 resp, st_resp;
|
||||
this->host_controller->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType);
|
||||
this->host_controller->GetLastStopTransmissionResponse(std::addressof(st_resp), sizeof(st_resp));
|
||||
|
||||
/* Check the device status. */
|
||||
R_TRY(this->base_device->CheckDeviceStatus(resp));
|
||||
|
||||
/* Decide on what errors to ignore. */
|
||||
u32 status_ignore_mask = 0;
|
||||
if (is_read) {
|
||||
AMS_ABORT_UNLESS(out_num_transferred_blocks != nullptr);
|
||||
if ((*out_num_transferred_blocks + sector_index) == this->base_device->GetMemoryCapacity()) {
|
||||
status_ignore_mask = DeviceStatus_AddressOutOfRange;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check the device status. */
|
||||
R_TRY(this->base_device->CheckDeviceStatus(st_resp & ~status_ignore_mask));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::ReadWriteSingle(u32 *out_num_transferred_blocks, u32 sector_index, u32 num_sectors, void *buf, bool is_read) const {
|
||||
/* Issue the read/write command. */
|
||||
AMS_ABORT_UNLESS(out_num_transferred_blocks != nullptr);
|
||||
R_TRY(this->IssueCommandMultipleBlock(out_num_transferred_blocks, sector_index, num_sectors, buf, is_read));
|
||||
|
||||
/* Decide on what errors to ignore. */
|
||||
u32 status_ignore_mask = 0;
|
||||
if (is_read) {
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
if ((*out_num_transferred_blocks + sector_index) == this->base_device->GetMemoryCapacity()) {
|
||||
status_ignore_mask = DeviceStatus_AddressOutOfRange;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get and check the status. */
|
||||
u32 device_status;
|
||||
R_TRY(this->IssueCommandSendStatus(std::addressof(device_status), status_ignore_mask));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::ReadWriteMultiple(u32 sector_index, u32 num_sectors, u32 sector_index_alignment, void *buf, size_t buf_size, bool is_read) {
|
||||
/* Verify that we can send the command. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
|
||||
/* If we want to read zero sectors, there's no work for us to do. */
|
||||
R_SUCCEED_IF(num_sectors == 0);
|
||||
|
||||
/* Check that the buffer is big enough for the sectors we're reading. */
|
||||
AMS_ABORT_UNLESS((buf_size / SectorSize) >= num_sectors);
|
||||
|
||||
/* Read sectors repeatedly until we've read all the ones we want. */
|
||||
u32 cur_sector_index = sector_index;
|
||||
u32 remaining_sectors = num_sectors;
|
||||
u8 *cur_buf = static_cast<u8 *>(buf);
|
||||
while (remaining_sectors > 0) {
|
||||
/* Determine how many sectors we can read in this iteration. */
|
||||
u32 cur_sectors = remaining_sectors;
|
||||
if (sector_index_alignment > 0) {
|
||||
AMS_ABORT_UNLESS((cur_sector_index % sector_index_alignment) == 0);
|
||||
|
||||
const u32 max_sectors = this->host_controller->GetMaxTransferNumBlocks();
|
||||
if (remaining_sectors > max_sectors) {
|
||||
cur_sectors = max_sectors - (max_sectors % sector_index_alignment);
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to perform the read/write. */
|
||||
u32 num_transferred_blocks = 0;
|
||||
Result result = this->ReadWriteSingle(std::addressof(num_transferred_blocks), cur_sector_index, cur_sectors, cur_buf, is_read);
|
||||
if (R_FAILED(result)) {
|
||||
/* Check if we were removed. */
|
||||
R_TRY(this->CheckRemoved());
|
||||
|
||||
/* Log that we failed to read/write. */
|
||||
this->PushErrorLog(false, "%s %X %X:%X", is_read ? "R" : "W", cur_sector_index, cur_sectors, result.GetValue());
|
||||
|
||||
/* Retry the read/write. */
|
||||
num_transferred_blocks = 0;
|
||||
result = this->ReadWriteSingle(std::addressof(num_transferred_blocks), cur_sector_index, cur_sectors, cur_buf, is_read);
|
||||
if (R_FAILED(result)) {
|
||||
/* Check if we were removed. */
|
||||
R_TRY(this->CheckRemoved());
|
||||
|
||||
/* Log that we failed to read/write. */
|
||||
this->PushErrorLog(false, "%s %X %X:%X", is_read ? "R" : "W", cur_sector_index, cur_sectors, result.GetValue());
|
||||
|
||||
/* Re-startup the connection, to see if that helps. */
|
||||
R_TRY(this->ReStartup());
|
||||
|
||||
/* Retry the read/write a third time. */
|
||||
num_transferred_blocks = 0;
|
||||
result = this->ReadWriteSingle(std::addressof(num_transferred_blocks), cur_sector_index, cur_sectors, cur_buf, is_read);
|
||||
if (R_FAILED(result)) {
|
||||
/* Log that we failed after a re-startup. */
|
||||
this->PushErrorLog(true, "%s %X %X:%X", is_read ? "R" : "W", cur_sector_index, cur_sectors, result.GetValue());
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Log that we succeeded after a retry. */
|
||||
this->PushErrorLog(true, "%s %X %X:0", is_read ? "R" : "W", cur_sector_index, cur_sectors);
|
||||
|
||||
/* Increment the number of error corrections we've done. */
|
||||
++this->num_read_write_error_corrections;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update our tracking variables. */
|
||||
AMS_ABORT_UNLESS(remaining_sectors >= num_transferred_blocks);
|
||||
remaining_sectors -= num_transferred_blocks;
|
||||
cur_sector_index += num_transferred_blocks;
|
||||
cur_buf += num_transferred_blocks * SectorSize;
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
|
||||
void BaseDeviceAccessor::RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Register the address. */
|
||||
return this->host_controller->RegisterDeviceVirtualAddress(buffer, buffer_size, buffer_device_virtual_address);
|
||||
}
|
||||
|
||||
void BaseDeviceAccessor::UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Register the address. */
|
||||
return this->host_controller->UnregisterDeviceVirtualAddress(buffer, buffer_size, buffer_device_virtual_address);
|
||||
}
|
||||
#endif
|
||||
|
||||
Result BaseDeviceAccessor::Activate() {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is awake. */
|
||||
R_UNLESS(this->base_device->IsAwake(), sdmmc::ResultNotAwakened());
|
||||
|
||||
/* If the device is already active, we don't need to do anything. */
|
||||
R_SUCCEED_IF(this->base_device->IsActive());
|
||||
|
||||
/* Activate the base device. */
|
||||
auto activate_guard = SCOPE_GUARD { ++this->num_activation_failures; };
|
||||
R_TRY(this->OnActivate());
|
||||
|
||||
/* We successfully activated the device. */
|
||||
activate_guard.Cancel();
|
||||
this->base_device->SetActive();
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
void BaseDeviceAccessor::Deactivate() {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Deactivate the base device. */
|
||||
if (this->base_device->IsActive()) {
|
||||
this->host_controller->Shutdown();
|
||||
this->base_device->Deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::ReadWrite(u32 sector_index, u32 num_sectors, void *buffer, size_t buffer_size, bool is_read) {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is accessible. */
|
||||
R_TRY(this->base_device->CheckAccessible());
|
||||
|
||||
/* Perform the read/write. */
|
||||
auto rw_guard = SCOPE_GUARD { ++this->num_read_write_failures; };
|
||||
R_TRY(this->OnReadWrite(sector_index, num_sectors, buffer, buffer_size, is_read));
|
||||
|
||||
/* We successfully performed the read/write. */
|
||||
rw_guard.Cancel();
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::CheckConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width) {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is accessible. */
|
||||
R_TRY(this->base_device->CheckAccessible());
|
||||
|
||||
/* Get the current speed mode/bus width. */
|
||||
*out_speed_mode = this->host_controller->GetSpeedMode();
|
||||
*out_bus_width = this->host_controller->GetBusWidth();
|
||||
|
||||
/* Verify that we can get the status. */
|
||||
R_TRY(this->host_controller->GetInternalStatus());
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::GetMemoryCapacity(u32 *out_sectors) const {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is accessible. */
|
||||
R_TRY(this->base_device->CheckAccessible());
|
||||
|
||||
/* Get the capacity. */
|
||||
AMS_ABORT_UNLESS(out_sectors != nullptr);
|
||||
*out_sectors = this->base_device->GetMemoryCapacity();
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::GetDeviceStatus(u32 *out) const {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is accessible. */
|
||||
R_TRY(this->base_device->CheckAccessible());
|
||||
|
||||
/* Get the status. */
|
||||
R_TRY(this->IssueCommandSendStatus(out, 0));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::GetOcr(u32 *out) const {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is accessible. */
|
||||
R_TRY(this->base_device->CheckAccessible());
|
||||
|
||||
/* Get the ocr. */
|
||||
AMS_ABORT_UNLESS(out != nullptr);
|
||||
*out = this->base_device->GetOcr();
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::GetRca(u16 *out) const {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is accessible. */
|
||||
R_TRY(this->base_device->CheckAccessible());
|
||||
|
||||
/* Get the rca. */
|
||||
AMS_ABORT_UNLESS(out != nullptr);
|
||||
*out = this->base_device->GetRca();
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::GetCid(void *out, size_t size) const {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is accessible. */
|
||||
R_TRY(this->base_device->CheckAccessible());
|
||||
|
||||
/* Get the cid. */
|
||||
this->base_device->GetCid(out, size);
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::GetCsd(void *out, size_t size) const {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is accessible. */
|
||||
R_TRY(this->base_device->CheckAccessible());
|
||||
|
||||
/* Get the csd. */
|
||||
this->base_device->GetCsd(out, size);
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
void BaseDeviceAccessor::GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size) {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Set the output error info. */
|
||||
AMS_ABORT_UNLESS(out_error_info != nullptr);
|
||||
out_error_info->num_activation_failures = this->num_activation_failures;
|
||||
out_error_info->num_activation_error_corrections = this->num_activation_error_corrections;
|
||||
out_error_info->num_read_write_failures = this->num_read_write_failures;
|
||||
out_error_info->num_read_write_error_corrections = this->num_read_write_error_corrections;
|
||||
this->ClearErrorInfo();
|
||||
|
||||
/* Check if we should write logs. */
|
||||
if (out_log_size == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if we can write logs. */
|
||||
if (out_log_buffer == nullptr || log_buffer_size == 0) {
|
||||
*out_log_size = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get and clear our logs. */
|
||||
#if defined(AMS_SDMMC_USE_LOGGER)
|
||||
{
|
||||
if (this->error_logger.HasLog()) {
|
||||
this->PushErrorTimeStamp();
|
||||
|
||||
*out_log_size = this->error_logger.GetAndClearLogs(out_log_buffer, log_buffer_size);
|
||||
} else {
|
||||
*out_log_size = 0;
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
*out_log_size = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,512 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include "sdmmc_i_host_controller.hpp"
|
||||
#include "sdmmc_i_device_accessor.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
#if defined(AMS_SDMMC_USE_LOGGER)
|
||||
class Logger {
|
||||
private:
|
||||
static constexpr size_t LogLengthMax = 0x20;
|
||||
static constexpr size_t LogCountMax = 0x10;
|
||||
private:
|
||||
char logs[LogCountMax][LogLengthMax];
|
||||
int log_index;
|
||||
private:
|
||||
void Clear() {
|
||||
for (size_t i = 0; i < LogCountMax; ++i) {
|
||||
this->logs[i][0] = '\0';
|
||||
}
|
||||
this->log_index = 0;
|
||||
}
|
||||
|
||||
size_t Pop(char *dst, size_t dst_size) {
|
||||
/* Decrease log index. */
|
||||
if ((--this->log_index) < 0) {
|
||||
this->log_index = LogCountMax - 1;
|
||||
}
|
||||
|
||||
/* Check if we have a log. */
|
||||
if (this->logs[this->log_index][0] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Copy log to output. */
|
||||
const int len = ::ams::util::Strlcpy(dst, this->logs[this->log_index], dst_size);
|
||||
|
||||
/* Clear the log we copied. */
|
||||
this->logs[this->log_index][0] = '\0';
|
||||
|
||||
return static_cast<size_t>(len);
|
||||
}
|
||||
|
||||
public:
|
||||
Logger() {
|
||||
this->Clear();
|
||||
}
|
||||
|
||||
void Push(const char *fmt, std::va_list vl) {
|
||||
/* Format the log into the current buffer. */
|
||||
::ams::util::TVSNPrintf(this->logs[this->log_index], LogLengthMax, fmt, vl);
|
||||
|
||||
/* Update our log index. */
|
||||
if ((++this->log_index) >= static_cast<int>(LogCountMax)) {
|
||||
this->log_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Push(const char *fmt, ...) {
|
||||
std::va_list vl;
|
||||
va_start(vl, fmt);
|
||||
this->Push(fmt, vl);
|
||||
va_end(vl);
|
||||
}
|
||||
|
||||
bool HasLog() const {
|
||||
const int index = this->log_index > 0 ? this->log_index - 1 : static_cast<int>(LogCountMax - 1);
|
||||
return this->logs[index][0] != '\0';
|
||||
}
|
||||
|
||||
size_t GetAndClearLogs(char *dst, size_t dst_size) {
|
||||
AMS_ABORT_UNLESS(dst != nullptr);
|
||||
AMS_ABORT_UNLESS(dst_size > 0);
|
||||
|
||||
/* Pop logs until we run out of them. */
|
||||
size_t total_len = 0;
|
||||
while (true) {
|
||||
/* Pop the current log. */
|
||||
const size_t cur_len = this->Pop(dst + total_len, dst_size - total_len);
|
||||
if (cur_len == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check if the log exceeded the buffer size. */
|
||||
if (total_len + cur_len + 1 >= dst_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Advance the total length. */
|
||||
total_len += cur_len;
|
||||
|
||||
/* Check if there's space for our separator. */
|
||||
if (total_len + 3 >= dst_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
dst[total_len + 0] = ',';
|
||||
dst[total_len + 1] = ' ';
|
||||
total_len += 2;
|
||||
}
|
||||
|
||||
/* Ensure that the complete log fits in the buffer. */
|
||||
if (total_len >= dst_size) {
|
||||
total_len = dst_size - 1;
|
||||
}
|
||||
|
||||
/* Ensure null termination. */
|
||||
dst[total_len] = '\0';
|
||||
|
||||
/* Clear any remaining logs. */
|
||||
this->Clear();
|
||||
|
||||
/* Return the length of the logs, including null terminator. */
|
||||
return total_len + 1;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
enum DeviceType {
|
||||
DeviceType_Mmc = 0,
|
||||
DeviceType_SdCard = 1,
|
||||
DeviceType_GcAsic = 2,
|
||||
};
|
||||
|
||||
enum DeviceState {
|
||||
DeviceState_Idle = 0,
|
||||
DeviceState_Ready = 1,
|
||||
DeviceState_Ident = 2,
|
||||
DeviceState_Stby = 3,
|
||||
DeviceState_Tran = 4,
|
||||
DeviceState_Data = 5,
|
||||
DeviceState_Rcv = 6,
|
||||
DeviceState_Prg = 7,
|
||||
DeviceState_Dis = 8,
|
||||
DeviceState_Rsvd0 = 9,
|
||||
DeviceState_Rsvd1 = 10,
|
||||
DeviceState_Rsvd2 = 11,
|
||||
DeviceState_Rsvd3 = 12,
|
||||
DeviceState_Rsvd4 = 13,
|
||||
DeviceState_Rsvd5 = 14,
|
||||
DeviceState_RsvdIoMode = 15,
|
||||
DeviceState_Unknown = 16,
|
||||
};
|
||||
|
||||
enum DeviceStatus : u32 {
|
||||
DeviceStatus_AkeSeqError = (1u << 3),
|
||||
DeviceStatus_AppCmd = (1u << 5),
|
||||
DeviceStatus_SwitchError = (1u << 7),
|
||||
DeviceStatus_EraseReset = (1u << 13),
|
||||
DeviceStatus_WpEraseSkip = (1u << 15),
|
||||
DeviceStatus_CidCsdOverwrite = (1u << 16),
|
||||
DeviceStatus_Error = (1u << 19),
|
||||
DeviceStatus_CcError = (1u << 20),
|
||||
DeviceStatus_DeviceEccFailed = (1u << 21),
|
||||
DeviceStatus_IllegalCommand = (1u << 22),
|
||||
DeviceStatus_ComCrcError = (1u << 23),
|
||||
DeviceStatus_LockUnlockFailed = (1u << 24),
|
||||
DeviceStatus_WpViolation = (1u << 26),
|
||||
DeviceStatus_EraseParam = (1u << 27),
|
||||
DeviceStatus_EraseSeqError = (1u << 28),
|
||||
DeviceStatus_BlockLenError = (1u << 29),
|
||||
DeviceStatus_AddressMisaligned = (1u << 30),
|
||||
DeviceStatus_AddressOutOfRange = (1u << 31),
|
||||
|
||||
DeviceStatus_CurrentStateShift = 9,
|
||||
DeviceStatus_CurrentStateMask = (0b1111u << DeviceStatus_CurrentStateShift),
|
||||
|
||||
DeviceStatus_ErrorMask = (DeviceStatus_SwitchError |
|
||||
DeviceStatus_EraseReset |
|
||||
DeviceStatus_WpEraseSkip |
|
||||
DeviceStatus_CidCsdOverwrite |
|
||||
DeviceStatus_Error |
|
||||
DeviceStatus_CcError |
|
||||
DeviceStatus_DeviceEccFailed |
|
||||
DeviceStatus_IllegalCommand |
|
||||
DeviceStatus_ComCrcError |
|
||||
DeviceStatus_LockUnlockFailed |
|
||||
DeviceStatus_WpViolation |
|
||||
DeviceStatus_EraseParam |
|
||||
DeviceStatus_EraseSeqError |
|
||||
DeviceStatus_BlockLenError |
|
||||
DeviceStatus_AddressMisaligned |
|
||||
DeviceStatus_AddressOutOfRange),
|
||||
};
|
||||
|
||||
class BaseDevice {
|
||||
private:
|
||||
u32 ocr;
|
||||
u8 cid[DeviceCidSize];
|
||||
u16 csd[DeviceCsdSize / sizeof(u16)];
|
||||
u32 memory_capacity;
|
||||
bool is_high_capacity;
|
||||
bool is_valid_ocr;
|
||||
bool is_valid_cid;
|
||||
bool is_valid_csd;
|
||||
bool is_valid_high_capacity;
|
||||
bool is_valid_memory_capacity;
|
||||
bool is_active;
|
||||
bool is_awake;
|
||||
public:
|
||||
#if defined(AMS_SDMMC_THREAD_SAFE)
|
||||
mutable os::Mutex device_mutex;
|
||||
public:
|
||||
BaseDevice() : device_mutex(true)
|
||||
#else
|
||||
BaseDevice()
|
||||
#endif
|
||||
{
|
||||
this->is_awake = true;
|
||||
this->ocr = 0;
|
||||
this->memory_capacity = 0;
|
||||
this->is_high_capacity = false;
|
||||
this->OnDeactivate();
|
||||
}
|
||||
|
||||
void OnDeactivate() {
|
||||
this->is_active = false;
|
||||
this->is_valid_ocr = false;
|
||||
this->is_valid_cid = false;
|
||||
this->is_valid_csd = false;
|
||||
this->is_valid_high_capacity = false;
|
||||
this->is_valid_memory_capacity = false;
|
||||
}
|
||||
|
||||
bool IsAwake() const {
|
||||
return this->is_awake;
|
||||
}
|
||||
|
||||
void Awaken() {
|
||||
this->is_awake = true;
|
||||
}
|
||||
|
||||
void PutToSleep() {
|
||||
this->is_awake = false;
|
||||
}
|
||||
|
||||
bool IsActive() const {
|
||||
return this->is_active;
|
||||
}
|
||||
|
||||
void SetActive() {
|
||||
this->is_active = true;
|
||||
}
|
||||
|
||||
virtual void Deactivate() {
|
||||
this->OnDeactivate();
|
||||
}
|
||||
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
virtual os::EventType *GetRemovedEvent() const = 0;
|
||||
#endif
|
||||
|
||||
virtual DeviceType GetDeviceType() const = 0;
|
||||
virtual u16 GetRca() const = 0;
|
||||
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
void InitializeRemovedEvent() {
|
||||
if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) {
|
||||
os::InitializeEvent(removed_event, false, os::EventClearMode_ManualClear);
|
||||
}
|
||||
}
|
||||
|
||||
void FinalizeRemovedEvent() {
|
||||
if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) {
|
||||
os::FinalizeEvent(removed_event);
|
||||
}
|
||||
}
|
||||
|
||||
void SignalRemovedEvent() {
|
||||
if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) {
|
||||
os::SignalEvent(removed_event);
|
||||
}
|
||||
}
|
||||
|
||||
void ClearRemovedEvent() {
|
||||
if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) {
|
||||
os::ClearEvent(removed_event);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsRemoved() const {
|
||||
if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) {
|
||||
return os::TryWaitEvent(removed_event);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
Result CheckRemoved() const {
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
R_UNLESS(!this->IsRemoved(), sdmmc::ResultDeviceRemoved());
|
||||
#endif
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result CheckAccessible() const {
|
||||
/* Check awake. */
|
||||
R_UNLESS(this->IsAwake(), sdmmc::ResultNotAwakened());
|
||||
|
||||
/* Check active. */
|
||||
R_UNLESS(this->IsActive(), sdmmc::ResultNotActivated());
|
||||
|
||||
/* Check removed. */
|
||||
R_TRY(this->CheckRemoved());
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
void SetHighCapacity(bool en) {
|
||||
this->is_high_capacity = en;
|
||||
this->is_valid_high_capacity = true;
|
||||
}
|
||||
|
||||
bool IsHighCapacity() const {
|
||||
AMS_ABORT_UNLESS(this->is_valid_high_capacity);
|
||||
return this->is_high_capacity;
|
||||
}
|
||||
|
||||
void SetOcr(u32 o) {
|
||||
this->ocr = o;
|
||||
this->is_valid_ocr = true;
|
||||
}
|
||||
|
||||
u32 GetOcr() const {
|
||||
AMS_ABORT_UNLESS(this->is_valid_ocr);
|
||||
return this->ocr;
|
||||
}
|
||||
|
||||
void SetCid(const void *src, size_t src_size) {
|
||||
AMS_ABORT_UNLESS(src != nullptr);
|
||||
AMS_ABORT_UNLESS(src_size >= DeviceCidSize);
|
||||
std::memcpy(this->cid, src, DeviceCidSize);
|
||||
this->is_valid_cid = true;
|
||||
}
|
||||
|
||||
void GetCid(void *dst, size_t dst_size) const {
|
||||
AMS_ABORT_UNLESS(this->is_valid_cid);
|
||||
AMS_ABORT_UNLESS(dst != nullptr);
|
||||
AMS_ABORT_UNLESS(dst_size >= DeviceCidSize);
|
||||
std::memcpy(dst, this->cid, DeviceCidSize);
|
||||
}
|
||||
|
||||
void SetCsd(const void *src, size_t src_size) {
|
||||
AMS_ABORT_UNLESS(src != nullptr);
|
||||
AMS_ABORT_UNLESS(src_size >= DeviceCsdSize);
|
||||
std::memcpy(this->csd, src, DeviceCsdSize);
|
||||
this->is_valid_csd = true;
|
||||
}
|
||||
|
||||
void GetCsd(void *dst, size_t dst_size) const {
|
||||
AMS_ABORT_UNLESS(this->is_valid_csd);
|
||||
AMS_ABORT_UNLESS(dst != nullptr);
|
||||
AMS_ABORT_UNLESS(dst_size >= DeviceCsdSize);
|
||||
std::memcpy(dst, this->csd, DeviceCsdSize);
|
||||
}
|
||||
|
||||
void SetMemoryCapacity(u32 num_sectors) {
|
||||
this->memory_capacity = num_sectors;
|
||||
this->is_valid_memory_capacity = true;
|
||||
}
|
||||
|
||||
u32 GetMemoryCapacity() const {
|
||||
AMS_ABORT_UNLESS(this->is_valid_memory_capacity);
|
||||
return this->memory_capacity;
|
||||
}
|
||||
|
||||
void GetLegacyCapacityParameters(u8 *out_c_size_mult, u8 *out_read_bl_len) const;
|
||||
Result SetLegacyMemoryCapacity();
|
||||
|
||||
Result CheckDeviceStatus(u32 r1_resp) const;
|
||||
DeviceState GetDeviceState(u32 r1_resp) const;
|
||||
};
|
||||
|
||||
class BaseDeviceAccessor : public IDeviceAccessor {
|
||||
private:
|
||||
IHostController *host_controller;
|
||||
BaseDevice *base_device;
|
||||
u32 num_activation_failures;
|
||||
u32 num_activation_error_corrections;
|
||||
u32 num_read_write_failures;
|
||||
u32 num_read_write_error_corrections;
|
||||
#if defined(AMS_SDMMC_USE_LOGGER)
|
||||
Logger error_logger;
|
||||
#endif
|
||||
private:
|
||||
void ClearErrorInfo() {
|
||||
this->num_activation_failures = 0;
|
||||
this->num_activation_error_corrections = 0;
|
||||
this->num_read_write_failures = 0;
|
||||
this->num_read_write_error_corrections = 0;
|
||||
}
|
||||
protected:
|
||||
explicit BaseDeviceAccessor(IHostController *hc) : host_controller(hc), base_device(nullptr) {
|
||||
this->ClearErrorInfo();
|
||||
}
|
||||
|
||||
IHostController *GetHostController() const {
|
||||
return this->host_controller;
|
||||
}
|
||||
|
||||
void SetDevice(BaseDevice *bd) {
|
||||
this->base_device = bd;
|
||||
}
|
||||
|
||||
Result CheckRemoved() const {
|
||||
return this->base_device->CheckRemoved();
|
||||
}
|
||||
|
||||
Result IssueCommandAndCheckR1(u32 *out_response, u32 command_index, u32 command_arg, bool is_busy, DeviceState expected_state, u32 status_ignore_mask) const;
|
||||
|
||||
Result IssueCommandAndCheckR1(u32 command_index, u32 command_arg, bool is_busy, DeviceState expected_state, u32 status_ignore_mask) const {
|
||||
u32 dummy;
|
||||
return this->IssueCommandAndCheckR1(std::addressof(dummy), command_index, command_arg, is_busy, expected_state, status_ignore_mask);
|
||||
}
|
||||
|
||||
Result IssueCommandAndCheckR1(u32 command_index, u32 command_arg, bool is_busy, DeviceState expected_state) const {
|
||||
return this->IssueCommandAndCheckR1(command_index, command_arg, is_busy, expected_state, 0);
|
||||
}
|
||||
|
||||
Result IssueCommandGoIdleState() const;
|
||||
Result IssueCommandAllSendCid(void *dst, size_t dst_size) const;
|
||||
Result IssueCommandSelectCard() const;
|
||||
Result IssueCommandSendCsd(void *dst, size_t dst_size) const;
|
||||
Result IssueCommandSendStatus(u32 *out_device_status, u32 status_ignore_mask) const;
|
||||
|
||||
Result IssueCommandSendStatus(u32 status_ignore_mask) const {
|
||||
u32 dummy;
|
||||
return this->IssueCommandSendStatus(std::addressof(dummy), status_ignore_mask);
|
||||
}
|
||||
|
||||
Result IssueCommandSendStatus() const {
|
||||
return this->IssueCommandSendStatus(0);
|
||||
}
|
||||
|
||||
Result IssueCommandSetBlockLenToSectorSize() const;
|
||||
Result IssueCommandMultipleBlock(u32 *out_num_transferred_blocks, u32 sector_index, u32 num_sectors, void *buf, bool is_read) const;
|
||||
Result ReadWriteSingle(u32 *out_num_transferred_blocks, u32 sector_index, u32 num_sectors, void *buf, bool is_read) const;
|
||||
Result ReadWriteMultiple(u32 sector_index, u32 num_sectors, u32 sector_index_alignment, void *buf, size_t buf_size, bool is_read);
|
||||
|
||||
void IncrementNumActivationErrorCorrections() {
|
||||
++this->num_activation_error_corrections;
|
||||
}
|
||||
|
||||
void PushErrorTimeStamp() {
|
||||
#if defined(AMS_SDMMC_USE_LOGGER)
|
||||
{
|
||||
this->error_logger.Push("%u", static_cast<u32>(os::ConvertToTimeSpan(os::GetSystemTick()).GetSeconds()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushErrorLog(bool with_timestamp, const char *fmt, ...) {
|
||||
#if defined(AMS_SDMMC_USE_LOGGER)
|
||||
{
|
||||
std::va_list vl;
|
||||
va_start(vl, fmt);
|
||||
this->error_logger.Push(fmt, vl);
|
||||
va_end(vl);
|
||||
|
||||
if (with_timestamp) {
|
||||
this->PushErrorTimeStamp();
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
AMS_UNUSED(with_timestamp, fmt);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual Result OnActivate() = 0;
|
||||
virtual Result OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_sie, bool is_read) = 0;
|
||||
virtual Result ReStartup() = 0;
|
||||
public:
|
||||
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
|
||||
virtual void RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) override;
|
||||
virtual void UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) override;
|
||||
#endif
|
||||
|
||||
virtual Result Activate() = 0;
|
||||
virtual void Deactivate() = 0;
|
||||
|
||||
virtual Result ReadWrite(u32 sector_index, u32 num_sectors, void *buffer, size_t buffer_size, bool is_read) = 0;
|
||||
virtual Result CheckConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width) = 0;
|
||||
|
||||
virtual Result GetMemoryCapacity(u32 *out_sectors) const override;
|
||||
virtual Result GetDeviceStatus(u32 *out) const override;
|
||||
virtual Result GetOcr(u32 *out) const override;
|
||||
virtual Result GetRca(u16 *out) const override;
|
||||
virtual Result GetCid(void *out, size_t size) const override;
|
||||
virtual Result GetCsd(void *out, size_t size) const override;
|
||||
|
||||
virtual void GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size) override;
|
||||
};
|
||||
|
||||
}
|
|
@ -38,11 +38,12 @@ namespace ams::sdmmc::impl {
|
|||
virtual Result GetSpeedMode(SpeedMode *out) const = 0;
|
||||
virtual Result GetMemoryCapacity(u32 *out_sectors) const = 0;
|
||||
virtual Result GetDeviceStatus(u32 *out) const = 0;
|
||||
virtual Result GetOcr(u32 *out) const = 0;
|
||||
virtual Result GetRca(u16 *out) const = 0;
|
||||
virtual Result GetCid(void *out, size_t size) const = 0;
|
||||
virtual Result GetCsd(void *out, size_t size) const = 0;
|
||||
|
||||
virtual void GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size) = 0;
|
||||
/* TODO */
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -36,6 +36,59 @@ namespace ams::sdmmc::impl {
|
|||
TransferDirection_WriteToDevice = 1,
|
||||
};
|
||||
|
||||
enum CommandIndex {
|
||||
/* Generic commands. */
|
||||
CommandIndex_GoIdleState = 0,
|
||||
|
||||
CommandIndex_AllSendCid = 2,
|
||||
CommandIndex_SendRelativeAddr = 3,
|
||||
CommandIndex_SetDsr = 4,
|
||||
|
||||
CommandIndex_SelectCard = 7,
|
||||
CommandIndex_DeselectCard = 7,
|
||||
|
||||
CommandIndex_SendIfCond = 8,
|
||||
CommandIndex_SendCsd = 9,
|
||||
CommandIndex_SendCid = 10,
|
||||
CommandIndex_VoltageSwitch = 11,
|
||||
CommandIndex_StopTransmission = 12,
|
||||
CommandIndex_SendStatus = 13,
|
||||
CommandIndex_SendTaskStatus = 13,
|
||||
|
||||
CommandIndex_GoInactiveState = 15,
|
||||
CommandIndex_SetBlockLen = 16,
|
||||
CommandIndex_ReadSingleBlock = 17,
|
||||
CommandIndex_ReadMultipleBlock = 18,
|
||||
CommandIndex_SendTuningBlock = 19,
|
||||
CommandIndex_SpeedClassControl = 20,
|
||||
|
||||
CommandIndex_AddressExtension = 22,
|
||||
CommandIndex_SetBlockCount = 23,
|
||||
CommandIndex_WriteBlock = 24,
|
||||
CommandIndex_WriteMultipleBlock = 25,
|
||||
|
||||
CommandIndex_ProgramCsd = 27,
|
||||
CommandIndex_SetWriteProt = 28,
|
||||
CommandIndex_ClearWriteProt = 29,
|
||||
CommandIndex_SendWriteProt = 30,
|
||||
|
||||
CommandIndex_EraseWriteBlockStart = 32,
|
||||
CommandIndex_EraseWriteBlockEnd = 33,
|
||||
|
||||
CommandIndex_Erase = 38,
|
||||
|
||||
CommandIndex_LockUnlock = 42,
|
||||
|
||||
CommandIndex_AppCmd = 55,
|
||||
CommandIndex_GenCmd = 56,
|
||||
|
||||
/* Nintendo specific vendor commands for lotus3. */
|
||||
CommandIndex_GcAsicWriteOperation = 60,
|
||||
CommandIndex_GcAsicFinishOperation = 61,
|
||||
CommandIndex_GcAsicSleep = 62,
|
||||
CommandIndex_GcAsicUpdateKey = 63,
|
||||
};
|
||||
|
||||
struct Command {
|
||||
u32 command_index;
|
||||
u32 command_argument;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <vapours.hpp>
|
||||
#include "sdmmc_port_mmc0.hpp"
|
||||
#include "sdmmc_select_sdmmc_controller.hpp"
|
||||
#include "sdmmc_base_device_accessor.hpp"
|
||||
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
|
|
@ -721,7 +721,7 @@ namespace ams::sdmmc::impl {
|
|||
ON_SCOPE_EXIT { this->DisableInterruptStatus(); };
|
||||
|
||||
/* Set the command. */
|
||||
Command command(12, 0, StopTransmissionCommandResponseType, true);
|
||||
Command command(CommandIndex_StopTransmission, 0, StopTransmissionCommandResponseType, true);
|
||||
this->SetCommand(std::addressof(command), false);
|
||||
|
||||
/* Wait for the command to complete. */
|
||||
|
|
Loading…
Reference in a new issue