sdmmc: finish outward-facing api (untested)

This commit is contained in:
Michael Scire 2020-10-27 23:32:45 -07:00
parent 5120b70231
commit a6c6a95053
10 changed files with 584 additions and 5 deletions

View file

@ -25,3 +25,4 @@
#include <vapours/sdmmc/sdmmc_common.hpp>
#include <vapours/sdmmc/sdmmc_mmc.hpp>
#include <vapours/sdmmc/sdmmc_sd_card.hpp>
#include <vapours/sdmmc/sdmmc_gc_asic.hpp>

View file

@ -0,0 +1,34 @@
/*
* 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/sdmmc/sdmmc_build_config.hpp>
namespace ams::sdmmc {
constexpr inline size_t GcAsicOperationSize = 0x40;
void PutGcAsicToSleep(Port port);
Result AwakenGcAsic(Port port);
Result WriteGcAsicOperation(Port port, const void *op_buf, size_t op_buf_size);
Result FinishGcAsicOperation(Port port);
Result AbortGcAsicOperation(Port port);
Result SleepGcAsic(Port port);
Result UpdateGcAsicKey(Port port);
void SignalGcRemovedEvent(Port port);
void ClearGcRemovedEvent(Port port);
}

View file

@ -0,0 +1,352 @@
/*
* 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/>.
*/
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
#include <stratosphere.hpp>
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
#include <mesosphere.hpp>
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
#include <exosphere.hpp>
#else
#include <vapours.hpp>
#endif
#include "sdmmc_gc_asic_device_accessor.hpp"
#include "sdmmc_timer.hpp"
namespace ams::sdmmc::impl {
#if defined(AMS_SDMMC_THREAD_SAFE)
#define AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX() std::scoped_lock lk(this->gc_asic_device.device_mutex)
#else
#define AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX()
#endif
#if defined(AMS_SDMMC_USE_OS_EVENTS)
#define AMS_SDMMC_CHECK_GC_ASIC_REMOVED() R_UNLESS(!this->gc_asic_device.IsRemoved(), sdmmc::ResultDeviceRemoved())
#else
#define AMS_SDMMC_CHECK_GC_ASIC_REMOVED()
#endif
Result GcAsicDeviceAccessor::IssueCommandWriteOperation(const void *op_buf, size_t op_buf_size) const {
/* Validate the operation buffer. */
AMS_ABORT_UNLESS(op_buf != nullptr);
AMS_ABORT_UNLESS(op_buf_size >= GcAsicOperationSize);
/* Issue the command. */
constexpr ResponseType CommandResponseType = ResponseType_R1;
Command command(CommandIndex_GcAsicWriteOperation, 0, CommandResponseType, false);
TransferData xfer_data(const_cast<void *>(op_buf), GcAsicOperationSize, 1, TransferDirection_WriteToDevice);
IHostController *hc = BaseDeviceAccessor::GetHostController();
Result result = hc->IssueCommand(std::addressof(command), std::addressof(xfer_data));
if (R_FAILED(result)) {
/* We failed to write operation. Check if we were removed. */
AMS_SDMMC_CHECK_GC_ASIC_REMOVED();
/* Determine what result we should return. */
Result return_result = result;
{
/* Issue a stop transmission command. */
u32 resp = 0;
result = hc->IssueStopTransmissionCommand(std::addressof(resp));
if (R_SUCCEEDED(result)) {
/* If we successfully stopped transmission but have an error status, we prefer to return that. */
result = this->gc_asic_device.CheckDeviceStatus(resp);
if (R_FAILED(result)) {
return_result = result;
}
}
/* Check again if we were removed. */
AMS_SDMMC_CHECK_GC_ASIC_REMOVED();
/* Request device status. */
u32 device_status;
result = BaseDeviceAccessor::IssueCommandSendStatus(std::addressof(device_status), 0);
/* If we got a device status error here and we didn't previously, we prefer to return that. */
if (!sdmmc::ResultDeviceStatusHasError::Includes(return_result) && sdmmc::ResultDeviceStatusHasError::Includes(result)) {
return_result = result;
}
}
return return_result;
}
/* Get the response. */
u32 resp;
hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType);
R_TRY(this->gc_asic_device.CheckDeviceStatus(resp));
return ResultSuccess();
}
Result GcAsicDeviceAccessor::IssueCommandFinishOperation() const {
/* Issue the command. */
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_GcAsicFinishOperation, 0, true, DeviceState_Tran));
return ResultSuccess();
}
Result GcAsicDeviceAccessor::IssueCommandSleep() {
/* Issue the command. */
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_GcAsicSleep, 0, true, DeviceState_Tran));
return ResultSuccess();
}
Result GcAsicDeviceAccessor::IssueCommandUpdateKey() const {
/* Issue the command. */
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_GcAsicUpdateKey, 0, true, DeviceState_Tran));
return ResultSuccess();
}
Result GcAsicDeviceAccessor::StartupGcAsicDevice() {
/* Start up the host controller. */
IHostController *hc = BaseDeviceAccessor::GetHostController();
R_TRY(hc->Startup(BusPower_1_8V, BusWidth_8Bit, SpeedMode_GcAsicSpeed, false));
/* Wait 10 clocks for configuration to take. */
WaitClocks(10, hc->GetDeviceClockFrequencyKHz());
/* Perform tuning with command index 21. */
AMS_ABORT_UNLESS(hc->IsSupportedTuning());
R_TRY(hc->Tuning(SpeedMode_GcAsicSpeed, 21));
/* Set the device as low capacity/no memory. */
this->gc_asic_device.SetHighCapacity(false);
this->gc_asic_device.SetMemoryCapacity(0);
/* Enable power saving. */
hc->SetPowerSaving(true);
return ResultSuccess();
}
Result GcAsicDeviceAccessor::OnActivate() {
/* If we fail to start up the device, ensure the host controller is shut down. */
auto power_guard = SCOPE_GUARD { BaseDeviceAccessor::GetHostController()->Shutdown(); };
/* Try to start up the device. */
R_TRY(this->StartupGcAsicDevice());
/* We started up, so we don't need to power down. */
power_guard.Cancel();
return ResultSuccess();
}
Result GcAsicDeviceAccessor::OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) {
/* Check that we're not performing zero-byte rw. */
AMS_ABORT_UNLESS(num_sectors > 0);
/* Check that the buffer is big enough for the rw. */
AMS_ABORT_UNLESS((buf_size / SectorSize) >= num_sectors);
/* Perform the read/write. */
u32 num_transferred_blocks;
R_TRY(BaseDeviceAccessor::ReadWriteSingle(std::addressof(num_transferred_blocks), sector_index, num_sectors, buf, is_read));
/* Require that we read/wrote as many sectors as we expected. */
AMS_ABORT_UNLESS(num_transferred_blocks == num_sectors);
return ResultSuccess();
}
void GcAsicDeviceAccessor::Initialize() {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
/* If we've already initialized, we don't need to do anything. */
if (this->is_initialized) {
return;
}
/* Set the base device to our gc asic device. */
BaseDeviceAccessor::SetDevice(std::addressof(this->gc_asic_device));
/* Initialize. */
IHostController *hc = BaseDeviceAccessor::GetHostController();
#if defined(AMS_SDMMC_USE_OS_EVENTS)
{
this->gc_asic_device.InitializeRemovedEvent();
hc->PreSetRemovedEvent(this->gc_asic_device.GetRemovedEvent());
}
#endif
hc->Initialize();
/* Mark ourselves as initialized. */
this->is_initialized = true;
}
void GcAsicDeviceAccessor::Finalize() {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
/* If we've already finalized, we don't need to do anything. */
if (!this->is_initialized) {
return;
}
this->is_initialized = false;
/* Deactivate the device. */
BaseDeviceAccessor::Deactivate();
/* Finalize the host controller. */
BaseDeviceAccessor::GetHostController()->Finalize();
/* Finalize the removed event. */
#if defined(AMS_SDMMC_USE_OS_EVENTS)
{
this->gc_asic_device.FinalizeRemovedEvent();
}
#endif
}
Result GcAsicDeviceAccessor::GetSpeedMode(SpeedMode *out_speed_mode) const {
/* Check that we can write to output. */
AMS_ABORT_UNLESS(out_speed_mode != nullptr);
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
/* Check that we're accessible. */
R_TRY(this->gc_asic_device.CheckAccessible());
*out_speed_mode = SpeedMode_GcAsicSpeed;
return ResultSuccess();
}
void GcAsicDeviceAccessor::PutGcAsicToSleep() {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
/* If the device isn't awake, we don't need to do anything. */
if (!this->gc_asic_device.IsAwake()) {
return;
}
/* If necessary, put the host controller to sleep. */
#if defined(AMS_SDMMC_USE_OS_EVENTS)
if (this->gc_asic_device.IsActive() && !this->gc_asic_device.IsRemoved())
#else
if (this->gc_asic_device.IsActive())
#endif
{
BaseDeviceAccessor::GetHostController()->PutToSleep();
}
/* Put the gc asic device to sleep. */
this->gc_asic_device.PutToSleep();
}
Result GcAsicDeviceAccessor::AwakenGcAsic() {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
/* If the device is awake, we don't need to do anything. */
R_SUCCEED_IF(this->gc_asic_device.IsAwake());
/* Wake the device. */
this->gc_asic_device.Awaken();
/* Wake the host controller, if we need to.*/
#if defined(AMS_SDMMC_USE_OS_EVENTS)
if (this->gc_asic_device.IsActive() && !this->gc_asic_device.IsRemoved())
#else
if (this->gc_asic_device.IsActive())
#endif
{
R_TRY(BaseDeviceAccessor::GetHostController()->Awaken());
}
return ResultSuccess();
}
Result GcAsicDeviceAccessor::WriteGcAsicOperation(const void *op_buf, size_t op_buf_size) {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
/* Check that we're accessible. */
R_TRY(this->gc_asic_device.CheckAccessible());
/* Issue the command. */
R_TRY(this->IssueCommandWriteOperation(op_buf, op_buf_size));
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
return ResultSuccess();
}
Result GcAsicDeviceAccessor::FinishGcAsicOperation() {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
/* Check that we're accessible. */
R_TRY(this->gc_asic_device.CheckAccessible());
/* Issue the command. */
R_TRY(this->IssueCommandFinishOperation());
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
return ResultSuccess();
}
Result GcAsicDeviceAccessor::AbortGcAsicOperation() {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
/* Check that we're accessible. */
R_TRY(this->gc_asic_device.CheckAccessible());
/* Issue stop transmission command. */
u32 resp = 0;
R_TRY(BaseDeviceAccessor::GetHostController()->IssueStopTransmissionCommand(std::addressof(resp)));
R_TRY(this->gc_asic_device.CheckDeviceStatus(resp));
return ResultSuccess();
}
Result GcAsicDeviceAccessor::SleepGcAsic() {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
/* Check that we're accessible. */
R_TRY(this->gc_asic_device.CheckAccessible());
/* Issue the command. */
R_TRY(this->IssueCommandSleep());
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
return ResultSuccess();
}
Result GcAsicDeviceAccessor::UpdateGcAsicKey() {
/* Acquire exclusive access to the device. */
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
/* Check that we're accessible. */
R_TRY(this->gc_asic_device.CheckAccessible());
/* Issue the command. */
R_TRY(this->IssueCommandUpdateKey());
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
return ResultSuccess();
}
}

View file

@ -0,0 +1,96 @@
/*
* 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_base_device_accessor.hpp"
namespace ams::sdmmc::impl {
class GcAsicDevice : public BaseDevice {
private:
static constexpr u16 Rca = 0;
private:
#if defined(AMS_SDMMC_USE_OS_EVENTS)
mutable os::EventType removed_event;
#endif
public:
#if defined(AMS_SDMMC_USE_OS_EVENTS)
virtual os::EventType *GetRemovedEvent() const override {
return std::addressof(this->removed_event);
}
#endif
virtual DeviceType GetDeviceType() const override {
return DeviceType_GcAsic;
}
virtual u16 GetRca() const override {
return Rca;
}
};
class GcAsicDeviceAccessor : public BaseDeviceAccessor {
private:
GcAsicDevice gc_asic_device;
bool is_initialized;
private:
Result IssueCommandWriteOperation(const void *op_buf, size_t op_buf_size) const;
Result IssueCommandFinishOperation() const;
Result IssueCommandSleep();
Result IssueCommandUpdateKey() const;
Result StartupGcAsicDevice();
protected:
virtual Result OnActivate() override;
virtual Result OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) override;
virtual Result ReStartup() override {
AMS_ABORT("Can't ReStartup GcAsic\n");
}
public:
virtual void Initialize() override;
virtual void Finalize() override;
virtual Result GetSpeedMode(SpeedMode *out_speed_mode) const override;
public:
explicit GcAsicDeviceAccessor(IHostController *hc) : BaseDeviceAccessor(hc), is_initialized(false) {
/* ... */
}
void PutGcAsicToSleep();
Result AwakenGcAsic();
Result WriteGcAsicOperation(const void *op_buf, size_t op_buf_size);
Result FinishGcAsicOperation();
Result AbortGcAsicOperation();
Result SleepGcAsic();
Result UpdateGcAsicKey();
void SignalGcRemovedEvent() {
#if defined(AMS_SDMMC_USE_OS_EVENTS)
this->gc_asic_device.SignalRemovedEvent();
#else
AMS_ABORT("SignalGcRemovedEvent called without event support\n");
#endif
}
void ClearGcRemovedEvent() {
#if defined(AMS_SDMMC_USE_OS_EVENTS)
this->gc_asic_device.ClearRemovedEvent();
#else
AMS_ABORT("ClearGcRemovedEvent called without event support\n");
#endif
}
};
}

View file

@ -590,7 +590,7 @@ namespace ams::sdmmc::impl {
AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX();
/* If the device is awake, we don't need to do anything. */
if (!this->mmc_device.IsAwake()) {
if (this->mmc_device.IsAwake()) {
return;
}

View file

@ -22,7 +22,7 @@
#else
#include <vapours.hpp>
#endif
#include "sdmmc_port_mmc0.hpp"
#include "sdmmc_port_gc_asic0.hpp"
#include "sdmmc_select_sdmmc_controller.hpp"
@ -31,6 +31,7 @@ namespace ams::sdmmc::impl {
namespace {
SdmmcControllerForPortGcAsic0 g_gc_asic0_host_controller;
GcAsicDeviceAccessor g_gc_asic0_device_accessor(std::addressof(g_gc_asic0_host_controller));
}
@ -38,4 +39,12 @@ namespace ams::sdmmc::impl {
return std::addressof(g_gc_asic0_host_controller);
}
IDeviceAccessor *GetDeviceAccessorOfPortGcAsic0() {
return std::addressof(g_gc_asic0_device_accessor);
}
GcAsicDeviceAccessor *GetGcAsicDeviceAccessorOfPortGcAsic0() {
return std::addressof(g_gc_asic0_device_accessor);
}
}

View file

@ -17,10 +17,12 @@
#include <vapours.hpp>
#include "sdmmc_i_host_controller.hpp"
#include "sdmmc_i_device_accessor.hpp"
#include "sdmmc_gc_asic_device_accessor.hpp"
namespace ams::sdmmc::impl {
IHostController *GetHostControllerOfPortGcAsic0();
IDeviceAccessor *GetDeviceAccessorOfPortGcAsic0();
GcAsicDeviceAccessor *GetGcAsicDeviceAccessorOfPortGcAsic0();
}

View file

@ -885,7 +885,7 @@ namespace ams::sdmmc::impl {
AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX();
/* If the device is awake, we don't need to do anything. */
if (!this->sd_card_device.IsAwake()) {
if (this->sd_card_device.IsAwake()) {
return;
}

View file

@ -39,7 +39,7 @@ namespace ams::sdmmc {
switch (port) {
case Port_Mmc0: host_controller = impl::GetHostControllerOfPortMmc0(); break;
case Port_SdCard0: host_controller = impl::GetHostControllerOfPortSdCard0(); break;
//TODO: case Port_GcAsic0: host_controller = impl::GetHostControllerOfPortGcAsic0(); break;
case Port_GcAsic0: host_controller = impl::GetHostControllerOfPortGcAsic0(); break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
@ -54,7 +54,7 @@ namespace ams::sdmmc {
switch (port) {
case Port_Mmc0: device_accessor = impl::GetDeviceAccessorOfPortMmc0(); break;
case Port_SdCard0: device_accessor = impl::GetDeviceAccessorOfPortSdCard0(); break;
//TODO: case Port_GcAsic0: device_accessor = impl::GetDeviceAccessorOfPortGcAsic0(); break;
case Port_GcAsic0: device_accessor = impl::GetDeviceAccessorOfPortGcAsic0(); break;
AMS_UNREACHABLE_DEFAULT_CASE();
}

View file

@ -0,0 +1,85 @@
/*
* 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/>.
*/
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
#include <stratosphere.hpp>
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
#include <mesosphere.hpp>
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
#include <exosphere.hpp>
#else
#include <vapours.hpp>
#endif
#include "impl/sdmmc_gc_asic_device_accessor.hpp"
#include "impl/sdmmc_port_mmc0.hpp"
#include "impl/sdmmc_port_sd_card0.hpp"
#include "impl/sdmmc_port_gc_asic0.hpp"
namespace ams::sdmmc {
namespace {
impl::GcAsicDeviceAccessor *GetGcAsicDeviceAccessor(Port port) {
/* Get the accessor. */
impl::GcAsicDeviceAccessor *gc_asic_device_accessor = nullptr;
switch (port) {
case Port_GcAsic0: gc_asic_device_accessor = impl::GetGcAsicDeviceAccessorOfPortGcAsic0(); break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
/* Ensure it's valid */
AMS_ABORT_UNLESS(gc_asic_device_accessor != nullptr);
return gc_asic_device_accessor;
}
}
void PutGcAsicToSleep(Port port) {
return GetGcAsicDeviceAccessor(port)->PutGcAsicToSleep();
}
Result AwakenGcAsic(Port port) {
return GetGcAsicDeviceAccessor(port)->AwakenGcAsic();
}
Result WriteGcAsicOperation(Port port, const void *op_buf, size_t op_buf_size) {
return GetGcAsicDeviceAccessor(port)->WriteGcAsicOperation(op_buf, op_buf_size);
}
Result FinishGcAsicOperation(Port port) {
return GetGcAsicDeviceAccessor(port)->FinishGcAsicOperation();
}
Result AbortGcAsicOperation(Port port) {
return GetGcAsicDeviceAccessor(port)->AbortGcAsicOperation();
}
Result SleepGcAsic(Port port) {
return GetGcAsicDeviceAccessor(port)->SleepGcAsic();
}
Result UpdateGcAsicKey(Port port) {
return GetGcAsicDeviceAccessor(port)->UpdateGcAsicKey();
}
void SignalGcRemovedEvent(Port port) {
return GetGcAsicDeviceAccessor(port)->SignalGcRemovedEvent();
}
void ClearGcRemovedEvent(Port port) {
return GetGcAsicDeviceAccessor(port)->ClearGcRemovedEvent();
}
}