diff --git a/libraries/libvapours/include/vapours/results/sdmmc_results.hpp b/libraries/libvapours/include/vapours/results/sdmmc_results.hpp
index dec582eb0..77780b54b 100644
--- a/libraries/libvapours/include/vapours/results/sdmmc_results.hpp
+++ b/libraries/libvapours/include/vapours/results/sdmmc_results.hpp
@@ -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);
diff --git a/libraries/libvapours/include/vapours/sdmmc/sdmmc_build_config.hpp b/libraries/libvapours/include/vapours/sdmmc/sdmmc_build_config.hpp
index 4ecbdc7ea..bc8da5de2 100644
--- a/libraries/libvapours/include/vapours/sdmmc/sdmmc_build_config.hpp
+++ b/libraries/libvapours/include/vapours/sdmmc/sdmmc_build_config.hpp
@@ -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
diff --git a/libraries/libvapours/include/vapours/sdmmc/sdmmc_common.hpp b/libraries/libvapours/include/vapours/sdmmc/sdmmc_common.hpp
index 3c6752e8f..e06976fce 100644
--- a/libraries/libvapours/include/vapours/sdmmc/sdmmc_common.hpp
+++ b/libraries/libvapours/include/vapours/sdmmc/sdmmc_common.hpp
@@ -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;
diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_base_device_accessor.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_base_device_accessor.cpp
new file mode 100644
index 000000000..0169e5138
--- /dev/null
+++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_base_device_accessor.cpp
@@ -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 .
+ */
+#include
+#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((this->csd[2] >> 7) & 0x7);
+ *out_read_bl_len = static_cast((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((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(dst), alignof(u32)));
+ AMS_ABORT_UNLESS(dst_size >= DeviceCidSize);
+ this->host_controller->GetLastResponse(static_cast(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(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(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(dst), alignof(u32)));
+ AMS_ABORT_UNLESS(dst_size >= DeviceCsdSize);
+ this->host_controller->GetLastResponse(static_cast(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(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(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
+ }
+
+}
diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_base_device_accessor.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_base_device_accessor.hpp
new file mode 100644
index 000000000..4389cdb42
--- /dev/null
+++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_base_device_accessor.hpp
@@ -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 .
+ */
+#pragma once
+#include
+#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(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(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(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(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;
+ };
+
+}
diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_i_device_accessor.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_i_device_accessor.hpp
index a1980813d..94e85bd59 100644
--- a/libraries/libvapours/source/sdmmc/impl/sdmmc_i_device_accessor.hpp
+++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_i_device_accessor.hpp
@@ -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 */
};
}
diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_i_host_controller.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_i_host_controller.hpp
index 52c53fafe..d86ea157e 100644
--- a/libraries/libvapours/source/sdmmc/impl/sdmmc_i_host_controller.hpp
+++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_i_host_controller.hpp
@@ -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;
diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_port_mmc0.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_mmc0.cpp
index 60512b485..e404b6b6a 100644
--- a/libraries/libvapours/source/sdmmc/impl/sdmmc_port_mmc0.cpp
+++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_mmc0.cpp
@@ -16,6 +16,7 @@
#include
#include "sdmmc_port_mmc0.hpp"
#include "sdmmc_select_sdmmc_controller.hpp"
+#include "sdmmc_base_device_accessor.hpp"
namespace ams::sdmmc::impl {
diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_controller.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_controller.cpp
index 6c12ec22d..3a19e5643 100644
--- a/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_controller.cpp
+++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_controller.cpp
@@ -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. */