sdmmc: add DeviceDetector, gpio: implement client api

This commit is contained in:
Michael Scire 2020-10-26 04:06:02 -07:00
parent 9a41e19004
commit 22fb4ff095
25 changed files with 950 additions and 42 deletions

View file

@ -47,6 +47,7 @@
#include <stratosphere/erpt.hpp> #include <stratosphere/erpt.hpp>
#include <stratosphere/err.hpp> #include <stratosphere/err.hpp>
#include <stratosphere/fatal.hpp> #include <stratosphere/fatal.hpp>
#include <stratosphere/gpio.hpp>
#include <stratosphere/hid.hpp> #include <stratosphere/hid.hpp>
#include <stratosphere/hos.hpp> #include <stratosphere/hos.hpp>
#include <stratosphere/kvdb.hpp> #include <stratosphere/kvdb.hpp>

View file

@ -39,12 +39,14 @@ namespace ams::impl {
AMS_DEFINE_SYSTEM_THREAD(21, pm, Main); AMS_DEFINE_SYSTEM_THREAD(21, pm, Main);
AMS_DEFINE_SYSTEM_THREAD(21, pm, ProcessTrack); AMS_DEFINE_SYSTEM_THREAD(21, pm, ProcessTrack);
/* NCM. */ /* NCM. */
AMS_DEFINE_SYSTEM_THREAD(21, ncm, MainWaitThreads); AMS_DEFINE_SYSTEM_THREAD(21, ncm, MainWaitThreads);
AMS_DEFINE_SYSTEM_THREAD(21, ncm, ContentManagerServerIpcSession); AMS_DEFINE_SYSTEM_THREAD(21, ncm, ContentManagerServerIpcSession);
AMS_DEFINE_SYSTEM_THREAD(21, ncm, LocationResolverServerIpcSession); AMS_DEFINE_SYSTEM_THREAD(21, ncm, LocationResolverServerIpcSession);
/* FS. */ /* FS. */
AMS_DEFINE_SYSTEM_THREAD(11, sdmmc, DeviceDetector);
AMS_DEFINE_SYSTEM_THREAD(16, fs, WorkerThreadPool); AMS_DEFINE_SYSTEM_THREAD(16, fs, WorkerThreadPool);
AMS_DEFINE_SYSTEM_THREAD(17, fs, Main); AMS_DEFINE_SYSTEM_THREAD(17, fs, Main);
AMS_DEFINE_SYSTEM_THREAD(17, fs, WorkerRealTimeAccess); AMS_DEFINE_SYSTEM_THREAD(17, fs, WorkerRealTimeAccess);

View file

@ -0,0 +1,19 @@
/*
* 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 <stratosphere/ddsf/ddsf_types.hpp>

View file

@ -0,0 +1,30 @@
/*
* 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>
namespace ams::ddsf {
enum AccessMode {
AccessMode_None = (0u << 0),
AccessMode_Read = (1u << 0),
AccessMode_Write = (1u << 1),
AccessMode_ReadWrite = AccessMode_Read | AccessMode_Write,
};
}

View file

@ -0,0 +1,20 @@
/*
* 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 <stratosphere/gpio/gpio_types.hpp>
#include <stratosphere/gpio/gpio_api.hpp>
#include <stratosphere/gpio/gpio_pad_api.hpp>

View file

@ -0,0 +1,25 @@
/*
* 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 <stratosphere/gpio/gpio_types.hpp>
namespace ams::gpio {
void Initialize();
void Finalize();
}

View file

@ -0,0 +1,58 @@
/*
* 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 <stratosphere/ddsf/ddsf_types.hpp>
#include <stratosphere/gpio/gpio_types.hpp>
#include <stratosphere/gpio/gpio_select_pad_name.hpp>
namespace ams::gpio {
struct GpioPadSession {
void *_session;
os::SystemEventType *_event;
};
Result OpenSession(GpioPadSession *out_session, ams::DeviceCode device_code);
void CloseSession(GpioPadSession *session);
Direction GetDirection(GpioPadSession *session);
void SetDirection(GpioPadSession *session, Direction direction);
GpioValue GetValue(GpioPadSession *session);
void SetValue(GpioPadSession *session, GpioValue value);
InterruptMode GetInterruptMode(GpioPadSession *session);
void SetInterruptMode(GpioPadSession *session, InterruptMode mode);
bool GetInterruptEnable(GpioPadSession *session);
void SetInterruptEnable(GpioPadSession *session, bool en);
InterruptStatus GetInterruptStatus(GpioPadSession *session);
void ClearInterruptStatus(GpioPadSession *session);
int GetDebounceTime(GpioPadSession *session);
void SetDebounceTime(GpioPadSession *session, int ms);
bool GetDebounceEnabled(GpioPadSession *session);
void SetDebounceEnabled(GpioPadSession *session, bool en);
Result BindInterrupt(os::SystemEventType *event, GpioPadSession *session);
void UnbindInterrupt(GpioPadSession *session);
Result IsWakeEventActive(bool *out_is_active, ams::DeviceCode device_code);
}

View file

@ -0,0 +1,55 @@
/*
* 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 <stratosphere/gpio/gpio_types.hpp>
namespace ams::gpio {
enum GpioPadName {
GpioPadName_CodecLdoEnTemp = 1,
GpioPadName_ButtonVolUp = 25,
GpioPadName_ButtonVolDn = 26,
GpioPadName_SdCd = 56,
};
/* TODO: Better place for this? */
constexpr inline const DeviceCode DeviceCode_CodecLdoEnTemp = 0x33000002;
constexpr inline const DeviceCode DeviceCode_ButtonVolUp = 0x35000002;
constexpr inline const DeviceCode DeviceCode_ButtonVolDn = 0x35000003;
constexpr inline const DeviceCode DeviceCode_SdCd = 0x3C000002;
constexpr inline GpioPadName ConvertToGpioPadName(DeviceCode dc) {
switch (dc.GetInternalValue()) {
case DeviceCode_CodecLdoEnTemp.GetInternalValue(): return GpioPadName_CodecLdoEnTemp;
case DeviceCode_ButtonVolUp .GetInternalValue(): return GpioPadName_ButtonVolUp;
case DeviceCode_ButtonVolDn .GetInternalValue(): return GpioPadName_ButtonVolDn;
case DeviceCode_SdCd .GetInternalValue(): return GpioPadName_SdCd;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
constexpr inline DeviceCode ConvertToDeviceCode(GpioPadName gpn) {
switch (gpn) {
case GpioPadName_CodecLdoEnTemp: return DeviceCode_CodecLdoEnTemp;
case GpioPadName_ButtonVolUp: return DeviceCode_ButtonVolUp;
case GpioPadName_ButtonVolDn: return DeviceCode_ButtonVolDn;
case GpioPadName_SdCd: return DeviceCode_SdCd;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
}

View file

@ -0,0 +1,24 @@
/*
* 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 <stratosphere/gpio/gpio_types.hpp>
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
#include <stratosphere/gpio/gpio_pad_name.board.nintendo_nx.hpp>
#else
/* Error? */
#endif

View file

@ -0,0 +1,44 @@
/*
* 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>
namespace ams::gpio {
enum InterruptMode {
InterruptMode_LowLevel = 0,
InterruptMode_HighLevel = 1,
InterruptMode_RisingEdge = 2,
InterruptMode_FallingEdge = 3,
InterruptMode_AnyEdge = 4,
};
enum Direction {
Direction_Input = 0,
Direction_Output = 1,
};
enum GpioValue {
GpioValue_Low = 0,
GpioValue_High = 1,
};
enum InterruptStatus {
InterruptStatus_Inactive = 0,
InterruptStatus_Active = 1,
};
}

View file

@ -167,20 +167,19 @@ namespace ams::boot2 {
} }
} }
bool GetGpioPadLow(GpioPadName pad) { bool GetGpioPadLow(DeviceCode device_code) {
GpioPadSession button; gpio::GpioPadSession button;
if (R_FAILED(gpioOpenSession(&button, pad))) { if (R_FAILED(gpio::OpenSession(std::addressof(button), device_code))) {
return false; return false;
} }
/* Ensure we close even on early return. */ /* Ensure we close even on early return. */
ON_SCOPE_EXIT { gpioPadClose(&button); }; ON_SCOPE_EXIT { gpio::CloseSession(std::addressof(button)); };
/* Set direction input. */ /* Set direction input. */
gpioPadSetDirection(&button, GpioDirection_Input); gpio::SetDirection(std::addressof(button), gpio::Direction_Input);
GpioValue val; return gpio::GetValue(std::addressof(button)) == gpio::GpioValue_Low;
return R_SUCCEEDED(gpioPadGetValue(&button, &val)) && val == GpioValue_Low;
} }
bool IsForceMaintenance() { bool IsForceMaintenance() {
@ -197,7 +196,7 @@ namespace ams::boot2 {
/* Contact GPIO, read plus/minus buttons. */ /* Contact GPIO, read plus/minus buttons. */
{ {
return GetGpioPadLow(GpioPadName_ButtonVolUp) && GetGpioPadLow(GpioPadName_ButtonVolDown); return GetGpioPadLow(gpio::DeviceCode_ButtonVolUp) && GetGpioPadLow(gpio::DeviceCode_ButtonVolDn);
} }
} }

View file

@ -0,0 +1,190 @@
/*
* 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 <stratosphere.hpp>
namespace ams::gpio {
/* TODO: This deserves proper new interface def support. */
namespace {
/* TODO: Manager object. */
constinit os::SdkMutex g_init_mutex;
constinit int g_initialize_count = 0;
using InternalSession = ::GpioPadSession;
InternalSession *GetInterface(GpioPadSession *session) {
AMS_ASSERT(session->_session != nullptr);
return static_cast<::GpioPadSession *>(session->_session);
}
}
void Initialize() {
std::scoped_lock lk(g_init_mutex);
if ((g_initialize_count++) == 0) {
R_ABORT_UNLESS(::gpioInitialize());
}
}
void Finalize() {
std::scoped_lock lk(g_init_mutex);
AMS_ASSERT(g_initialize_count > 0);
if ((--g_initialize_count) == 0) {
::gpioExit();
}
}
Result OpenSession(GpioPadSession *out_session, ams::DeviceCode device_code) {
/* Allocate the session. */
InternalSession *internal_session = static_cast<InternalSession *>(std::malloc(sizeof(InternalSession)));
AMS_ABORT_UNLESS(internal_session != nullptr);
auto session_guard = SCOPE_GUARD { std::free(internal_session); };
/* Get the session. */
if (hos::GetVersion() >= hos::Version_7_0_0) {
R_TRY(::gpioOpenSession2(internal_session, device_code.GetInternalValue(), ddsf::AccessMode_ReadWrite));
} else {
R_ABORT_UNLESS(::gpioOpenSession(internal_session, static_cast<::GpioPadName>(static_cast<s32>(ConvertToGpioPadName(device_code)))));
}
/* Set output. */
out_session->_session = internal_session;
out_session->_event = nullptr;
/* We succeeded. */
session_guard.Cancel();
return ResultSuccess();
}
void CloseSession(GpioPadSession *session) {
AMS_ASSERT(session != nullptr);
/* Unbind the interrupt, if it's still bound. */
if (session->_event != nullptr) {
gpio::UnbindInterrupt(session);
}
/* Close the session. */
::gpioPadClose(GetInterface(session));
std::free(session->_session);
session->_session = nullptr;
}
Result IsWakeEventActive(bool *out_is_active, ams::DeviceCode device_code) {
if (hos::GetVersion() >= hos::Version_7_0_0) {
R_TRY(::gpioIsWakeEventActive2(out_is_active, device_code.GetInternalValue()));
} else {
R_ABORT_UNLESS(::gpioIsWakeEventActive(out_is_active, static_cast<::GpioPadName>(static_cast<s32>(ConvertToGpioPadName(device_code)))));
}
return ResultSuccess();
}
Direction GetDirection(GpioPadSession *session) {
::GpioDirection out;
R_ABORT_UNLESS(::gpioPadGetDirection(GetInterface(session), std::addressof(out)));
return static_cast<Direction>(static_cast<s32>(out));
}
void SetDirection(GpioPadSession *session, Direction direction) {
R_ABORT_UNLESS(::gpioPadSetDirection(GetInterface(session), static_cast<::GpioDirection>(static_cast<s32>(direction))));
}
GpioValue GetValue(GpioPadSession *session) {
::GpioValue out;
R_ABORT_UNLESS(::gpioPadGetValue(GetInterface(session), std::addressof(out)));
return static_cast<GpioValue>(static_cast<s32>(out));
}
void SetValue(GpioPadSession *session, GpioValue value) {
R_ABORT_UNLESS(::gpioPadSetValue(GetInterface(session), static_cast<::GpioValue>(static_cast<s32>(value))));
}
InterruptMode GetInterruptMode(GpioPadSession *session) {
::GpioInterruptMode out;
R_ABORT_UNLESS(::gpioPadGetInterruptMode(GetInterface(session), std::addressof(out)));
return static_cast<InterruptMode>(static_cast<s32>(out));
}
void SetInterruptMode(GpioPadSession *session, InterruptMode mode) {
R_ABORT_UNLESS(::gpioPadSetInterruptMode(GetInterface(session), static_cast<::GpioInterruptMode>(static_cast<s32>(mode))));
}
bool GetInterruptEnable(GpioPadSession *session) {
bool out;
R_ABORT_UNLESS(::gpioPadGetInterruptEnable(GetInterface(session), std::addressof(out)));
return out;
}
void SetInterruptEnable(GpioPadSession *session, bool en) {
R_ABORT_UNLESS(::gpioPadSetInterruptEnable(GetInterface(session), en));
}
InterruptStatus GetInterruptStatus(GpioPadSession *session) {
::GpioInterruptStatus out;
R_ABORT_UNLESS(::gpioPadGetInterruptStatus(GetInterface(session), std::addressof(out)));
return static_cast<InterruptStatus>(static_cast<s32>(out));
}
void ClearInterruptStatus(GpioPadSession *session) {
R_ABORT_UNLESS(::gpioPadClearInterruptStatus(GetInterface(session)));
}
int GetDebounceTime(GpioPadSession *session) {
int out;
R_ABORT_UNLESS(::gpioPadGetDebounceTime(GetInterface(session), std::addressof(out)));
return out;
}
void SetDebounceTime(GpioPadSession *session, int ms) {
R_ABORT_UNLESS(::gpioPadSetDebounceTime(GetInterface(session), ms));
}
bool GetDebounceEnabled(GpioPadSession *session) {
bool out;
R_ABORT_UNLESS(::gpioPadGetDebounceEnabled(GetInterface(session), std::addressof(out)));
return out;
}
void SetDebounceEnabled(GpioPadSession *session, bool en) {
R_ABORT_UNLESS(::gpioPadSetDebounceEnabled(GetInterface(session), en));
}
Result BindInterrupt(os::SystemEventType *event, GpioPadSession *session) {
::Event ev;
R_TRY(::gpioPadBindInterrupt(GetInterface(session), std::addressof(ev)));
os::AttachReadableHandleToSystemEvent(event, ev.revent, true, os::EventClearMode_ManualClear);
session->_event = event;
return ResultSuccess();
}
void UnbindInterrupt(GpioPadSession *session) {
AMS_ASSERT(session->_event != nullptr);
R_ABORT_UNLESS(::gpioPadUnbindInterrupt(GetInterface(session)));
os::DestroySystemEvent(session->_event);
session->_event = nullptr;
}
}

View file

@ -20,6 +20,7 @@
#include <vapours/literals.hpp> #include <vapours/literals.hpp>
#include <vapours/allocator.hpp> #include <vapours/allocator.hpp>
#include <vapours/device_code.hpp>
#include <vapours/timespan.hpp> #include <vapours/timespan.hpp>
#include <vapours/span.hpp> #include <vapours/span.hpp>

View file

@ -0,0 +1,49 @@
/*
* 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/common.hpp>
#include <vapours/assert.hpp>
namespace ams {
namespace impl {
using DeviceCodeType = u32;
}
/* TODO: Better understand device code components. */
class DeviceCode {
private:
impl::DeviceCodeType inner_value;
public:
constexpr DeviceCode(impl::DeviceCodeType v) : inner_value(v) { /* ... */ }
constexpr impl::DeviceCodeType GetInternalValue() const { return this->inner_value; }
constexpr bool operator==(const DeviceCode &rhs) const {
return this->GetInternalValue() == rhs.GetInternalValue();
}
constexpr bool operator!=(const DeviceCode &rhs) const {
return !(*this == rhs);
}
};
constexpr inline const DeviceCode InvalidDeviceCode(0);
}

View file

@ -34,6 +34,7 @@
#define AMS_SDMMC_USE_UTIL_TIMER #define AMS_SDMMC_USE_UTIL_TIMER
//#define AMS_SDMMC_ENABLE_MMC_HS400 //#define AMS_SDMMC_ENABLE_MMC_HS400
//#define AMS_SDMMC_SET_PLLC4_BASE //#define AMS_SDMMC_SET_PLLC4_BASE
//#define AMS_SDMMC_USE_SD_CARD_DETECTOR
#elif defined(ATMOSPHERE_IS_MESOSPHERE) #elif defined(ATMOSPHERE_IS_MESOSPHERE)
@ -47,6 +48,7 @@
#define AMS_SDMMC_USE_UTIL_TIMER #define AMS_SDMMC_USE_UTIL_TIMER
//#define AMS_SDMMC_ENABLE_MMC_HS400 //#define AMS_SDMMC_ENABLE_MMC_HS400
//#define AMS_SDMMC_SET_PLLC4_BASE //#define AMS_SDMMC_SET_PLLC4_BASE
//#define AMS_SDMMC_USE_SD_CARD_DETECTOR
#elif defined(ATMOSPHERE_IS_STRATOSPHERE) #elif defined(ATMOSPHERE_IS_STRATOSPHERE)
@ -60,6 +62,7 @@
//#define AMS_SDMMC_USE_UTIL_TIMER //#define AMS_SDMMC_USE_UTIL_TIMER
#define AMS_SDMMC_ENABLE_MMC_HS400 #define AMS_SDMMC_ENABLE_MMC_HS400
#define AMS_SDMMC_SET_PLLC4_BASE #define AMS_SDMMC_SET_PLLC4_BASE
#define AMS_SDMMC_USE_SD_CARD_DETECTOR
#else #else
#error "Unknown execution context for ams::sdmmc!" #error "Unknown execution context for ams::sdmmc!"

View file

@ -0,0 +1,262 @@
/*
* 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>
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
#include "sdmmc_device_detector.hpp"
namespace ams::sdmmc::impl {
bool DeviceDetector::IsCurrentInserted() {
return gpio::GetValue(std::addressof(this->gpio_pad_session)) == this->inserted_gpio_value;
}
void DeviceDetector::HandleDeviceStatus(bool prev_inserted, bool cur_inserted) {
if (!prev_inserted && !cur_inserted) {
/* Not inserted -> Not inserted, nothing to do. */
} else if (!prev_inserted && cur_inserted) {
/* Card was inserted. */
if (this->callback_info.inserted_callback != nullptr) {
this->callback_info.inserted_callback(this->callback_info.inserted_callback_arg);
}
} else if (prev_inserted && !cur_inserted) {
/* Card was removed. */
if (this->callback_info.removed_callback != nullptr) {
this->callback_info.removed_callback(this->callback_info.removed_callback_arg);
}
} else /* if (prev_inserted && cur_inserted) */ {
/* Card was removed, and then inserted. */
if (this->callback_info.removed_callback != nullptr) {
this->callback_info.removed_callback(this->callback_info.removed_callback_arg);
}
if (this->callback_info.inserted_callback != nullptr) {
this->callback_info.inserted_callback(this->callback_info.inserted_callback_arg);
}
}
}
void DeviceDetector::DetectorThread() {
/* Initialize the gpio session. */
sm::DoWithSession([] { gpio::Initialize(); });
gpio::OpenSession(std::addressof(this->gpio_pad_session), this->gpio_device_code);
gpio::SetDirection(std::addressof(this->gpio_pad_session), gpio::Direction_Input);
gpio::SetDebounceTime(std::addressof(this->gpio_pad_session), this->gpio_debounce_ms);
gpio::SetDebounceEnabled(std::addressof(this->gpio_pad_session), true);
gpio::SetInterruptMode(std::addressof(this->gpio_pad_session), gpio::InterruptMode_AnyEdge);
/* Get the gpio session's interrupt event. */
os::SystemEventType gpio_event;
R_ABORT_UNLESS(gpio::BindInterrupt(std::addressof(gpio_event), std::addressof(this->gpio_pad_session)));
/* Initialize and link waitable holders. */
os::WaitableManagerType wait_manager;
os::WaitableHolderType detector_thread_end_holder;
os::WaitableHolderType request_sleep_wake_event_holder;
os::WaitableHolderType gpio_event_holder;
os::InitializeWaitableManager(std::addressof(wait_manager));
os::InitializeWaitableHolder(std::addressof(detector_thread_end_holder), std::addressof(this->detector_thread_end_event));
os::LinkWaitableHolder(std::addressof(wait_manager), std::addressof(detector_thread_end_holder));
os::InitializeWaitableHolder(std::addressof(request_sleep_wake_event_holder), std::addressof(this->request_sleep_wake_event));
os::LinkWaitableHolder(std::addressof(wait_manager), std::addressof(request_sleep_wake_event_holder));
os::InitializeWaitableHolder(std::addressof(gpio_event_holder), std::addressof(gpio_event));
os::LinkWaitableHolder(std::addressof(wait_manager), std::addressof(gpio_event_holder));
/* Wait before detecting the initial state of the card. */
os::SleepThread(TimeSpan::FromMilliSeconds(this->gpio_debounce_ms));
bool cur_inserted = this->IsCurrentInserted();
this->is_prev_inserted = cur_inserted;
/* Set state as awake. */
this->state = State_Awake;
os::SignalEvent(std::addressof(this->ready_device_status_event));
/* Enable interrupts to be informed of device status. */
gpio::SetInterruptEnable(std::addressof(this->gpio_pad_session), true);
/* Wait, servicing our events. */
while (true) {
/* Get the signaled holder. */
os::WaitableHolderType *signaled_holder = os::WaitAny(std::addressof(wait_manager));
/* Process the holder. */
bool insert_change = false;
if (signaled_holder == std::addressof(detector_thread_end_holder)) {
/* We should kill ourselves. */
os::ClearEvent(std::addressof(this->detector_thread_end_event));
this->state = State_Finalized;
break;
} else if (signaled_holder == std::addressof(request_sleep_wake_event_holder)) {
/* A request for us to sleep/wake has come in, so we'll acknowledge it. */
os::ClearEvent(std::addressof(this->request_sleep_wake_event));
this->state = State_Sleep;
os::SignalEvent(std::addressof(this->acknowledge_sleep_awake_event));
/* Temporarily unlink our interrupt event. */
os::UnlinkWaitableHolder(std::addressof(gpio_event_holder));
/* Wait to be signaled. */
signaled_holder = os::WaitAny(std::addressof(wait_manager));
/* Link our interrupt event back in. */
os::LinkWaitableHolder(std::addressof(wait_manager), std::addressof(gpio_event_holder));
/* We're awake again. Either because we should exit, or because we were asked to wake up. */
os::ClearEvent(std::addressof(this->request_sleep_wake_event));
this->state = State_Awake;
os::SignalEvent(std::addressof(this->acknowledge_sleep_awake_event));
/* If we were asked to exit, do so. */
if (signaled_holder == std::addressof(detector_thread_end_holder)) {
/* We should kill ourselves. */
os::ClearEvent(std::addressof(this->detector_thread_end_event));
this->state = State_Finalized;
break;
} else /* if (signaled_holder == std::addressof(request_sleep_wake_event_holder)) */ {
if ((this->force_detection) ||
(({ bool active; R_SUCCEEDED(gpio::IsWakeEventActive(std::addressof(active), this->gpio_device_code)) && active; })) ||
(os::TryWaitSystemEvent(std::addressof(gpio_event))) ||
(this->is_prev_inserted != this->IsCurrentInserted()))
{
insert_change = true;
}
}
} else /* if (signaled_holder == std::addressof(gpio_event_holder)) */ {
/* An event was detected. */
insert_change = true;
}
/* Handle an insert change, if one occurred. */
if (insert_change) {
/* Call the relevant callback, if we have one. */
if (this->device_detection_event_callback != nullptr) {
this->device_detection_event_callback(this->device_detection_event_callback_arg);
}
/* Clear the interrupt event. */
os::ClearSystemEvent(std::addressof(gpio_event));
gpio::ClearInterruptStatus(std::addressof(this->gpio_pad_session));
gpio::SetInterruptEnable(std::addressof(this->gpio_pad_session), true);
/* Update insertion status. */
cur_inserted = this->IsCurrentInserted();
this->HandleDeviceStatus(this->is_prev_inserted, cur_inserted);
this->is_prev_inserted = cur_inserted;
}
}
/* Disable interrupts to our gpio event. */
gpio::SetInterruptEnable(std::addressof(this->gpio_pad_session), false);
/* Finalize and unlink waitable holders. */
os::UnlinkWaitableHolder(std::addressof(gpio_event_holder));
os::FinalizeWaitableHolder(std::addressof(gpio_event_holder));
os::UnlinkWaitableHolder(std::addressof(request_sleep_wake_event_holder));
os::FinalizeWaitableHolder(std::addressof(request_sleep_wake_event_holder));
os::UnlinkWaitableHolder(std::addressof(detector_thread_end_holder));
os::FinalizeWaitableHolder(std::addressof(detector_thread_end_holder));
os::FinalizeWaitableManager(std::addressof(wait_manager));
/* Finalize the gpio session. */
gpio::UnbindInterrupt(std::addressof(this->gpio_pad_session));
gpio::CloseSession(std::addressof(this->gpio_pad_session));
gpio::Finalize();
}
void DeviceDetector::Initialize(CallbackInfo *ci) {
/* Transition our state from finalized to initializing. */
AMS_ABORT_UNLESS(this->state == State_Finalized);
this->state = State_Initializing;
/* Set our callback infos. */
this->callback_info = *ci;
/* Initialize our events. */
os::InitializeEvent(std::addressof(this->ready_device_status_event), false, os::EventClearMode_ManualClear);
os::InitializeEvent(std::addressof(this->request_sleep_wake_event), false, os::EventClearMode_ManualClear);
os::InitializeEvent(std::addressof(this->acknowledge_sleep_awake_event), false, os::EventClearMode_ManualClear);
os::InitializeEvent(std::addressof(this->detector_thread_end_event), false, os::EventClearMode_ManualClear);
/* Create and start the detector thread. */
os::CreateThread(std::addressof(this->detector_thread), DetectorThreadEntry, this, this->detector_thread_stack, sizeof(this->detector_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(sdmmc, DeviceDetector));
os::SetThreadNamePointer(std::addressof(this->detector_thread), AMS_GET_SYSTEM_THREAD_NAME(sdmmc, DeviceDetector));
os::StartThread(std::addressof(this->detector_thread));
}
void DeviceDetector::Finalize() {
/* Ensure we're not already finalized. */
AMS_ABORT_UNLESS(this->state != State_Finalized);
/* Signal event to end the detector thread. */
os::SignalEvent(std::addressof(this->detector_thread_end_event));
os::WaitThread(std::addressof(this->detector_thread));
/* Finalize thread and events. */
os::DestroyThread(std::addressof(this->detector_thread));
os::FinalizeEvent(std::addressof(this->ready_device_status_event));
os::FinalizeEvent(std::addressof(this->request_sleep_wake_event));
os::FinalizeEvent(std::addressof(this->acknowledge_sleep_awake_event));
os::FinalizeEvent(std::addressof(this->detector_thread_end_event));
}
void DeviceDetector::PutToSleep() {
/* Signal request, wait for acknowledgement. */
os::SignalEvent(std::addressof(this->request_sleep_wake_event));
os::WaitEvent(std::addressof(this->acknowledge_sleep_awake_event));
os::ClearEvent(std::addressof(this->acknowledge_sleep_awake_event));
}
void DeviceDetector::Awaken(bool force_det) {
/* Signal request, wait for acknowledgement. */
this->force_detection = force_det;
os::SignalEvent(std::addressof(this->request_sleep_wake_event));
os::WaitEvent(std::addressof(this->acknowledge_sleep_awake_event));
os::ClearEvent(std::addressof(this->acknowledge_sleep_awake_event));
}
bool DeviceDetector::IsInserted() {
bool inserted = false;
switch (this->state) {
case State_Initializing:
/* Wait for us to know whether the device is inserted. */
os::WaitEvent(std::addressof(this->ready_device_status_event));
[[fallthrough]];
case State_Awake:
/* Get whether the device is currently inserted. */
inserted = this->IsCurrentInserted();
break;
case State_Sleep:
case State_Finalized:
/* Get whether the device was inserted when we last knew. */
inserted = this->is_prev_inserted;
break;
}
return inserted;
}
void DeviceDetector::RegisterDetectionEventCallback(DeviceDetectionEventCallback cb, void *arg) {
this->device_detection_event_callback_arg = arg;
this->device_detection_event_callback = cb;
}
void DeviceDetector::UnregisterDetectionEventCallback() {
this->device_detection_event_callback = nullptr;
}
}
#endif

View file

@ -0,0 +1,99 @@
/*
* 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>
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
#include <stratosphere.hpp>
namespace ams::sdmmc::impl {
using InsertedCallback = void (*)(void *);
using RemovedCallback = void (*)(void *);
struct CallbackInfo {
InsertedCallback inserted_callback;
void *inserted_callback_arg;
RemovedCallback removed_callback;
void *removed_callback_arg;
};
class DeviceDetector {
private:
enum State {
State_Initializing = 0,
State_Awake = 1,
State_Sleep = 2,
State_Finalized = 3,
};
private:
alignas(os::ThreadStackAlignment) u8 detector_thread_stack[8_KB];
State state;
bool is_prev_inserted;
bool force_detection;
os::ThreadType detector_thread;
os::EventType detector_thread_end_event;
os::EventType request_sleep_wake_event;
os::EventType acknowledge_sleep_awake_event;
os::EventType ready_device_status_event;
DeviceCode gpio_device_code;
gpio::GpioValue inserted_gpio_value;
u32 gpio_debounce_ms;
gpio::GpioPadSession gpio_pad_session;
CallbackInfo callback_info;
DeviceDetectionEventCallback device_detection_event_callback;
void *device_detection_event_callback_arg;
private:
static void DetectorThreadEntry(void *arg) {
reinterpret_cast<DeviceDetector *>(arg)->DetectorThread();
}
void DetectorThread();
bool IsCurrentInserted();
void HandleDeviceStatus(bool prev_inserted, bool cur_inserted);
public:
explicit DeviceDetector(DeviceCode dc, gpio::GpioValue igv, u32 gd)
: gpio_device_code(dc), inserted_gpio_value(igv), gpio_debounce_ms(gd)
{
this->state = State_Finalized;
this->is_prev_inserted = false;
this->force_detection = false;
this->callback_info = {};
this->device_detection_event_callback = nullptr;
this->device_detection_event_callback_arg = nullptr;
}
void Initialize(CallbackInfo *ci);
void Finalize();
void PutToSleep();
void Awaken(bool force_det);
u32 GetDebounceMilliSeconds() const {
return this->gpio_debounce_ms;
}
bool IsInserted();
void RegisterDetectionEventCallback(DeviceDetectionEventCallback cb, void *arg);
void UnregisterDetectionEventCallback();
};
}
#endif

View file

@ -24,6 +24,7 @@ namespace ams::sdmmc::impl {
SdmmcControllerForPortSdCard0 g_sd_card0_host_controller; SdmmcControllerForPortSdCard0 g_sd_card0_host_controller;
} }
IHostController *GetHostControllerOfPortSdCard0() { IHostController *GetHostControllerOfPortSdCard0() {

View file

@ -17,10 +17,12 @@
#include <vapours.hpp> #include <vapours.hpp>
#include "sdmmc_i_host_controller.hpp" #include "sdmmc_i_host_controller.hpp"
#include "sdmmc_i_device_accessor.hpp" #include "sdmmc_i_device_accessor.hpp"
#include "sdmmc_sd_card_device_accessor.hpp"
namespace ams::sdmmc::impl { namespace ams::sdmmc::impl {
IHostController *GetHostControllerOfPortSdCard0(); IHostController *GetHostControllerOfPortSdCard0();
IDeviceAccessor *GetDeviceAccessorOfPortSdCard0(); IDeviceAccessor *GetDeviceAccessorOfPortSdCard0();
SdCardDeviceAccessor *GetSdCardDeviceAccessorOfPortSdCard0();
} }

View file

@ -0,0 +1,25 @@
/*
* 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 {
/* TODO */
struct SdCardDeviceAccessor;
}

View file

@ -75,7 +75,7 @@ void __appInit(void) {
R_ABORT_UNLESS(pminfoInitialize()); R_ABORT_UNLESS(pminfoInitialize());
R_ABORT_UNLESS(pmshellInitialize()); R_ABORT_UNLESS(pmshellInitialize());
R_ABORT_UNLESS(setsysInitialize()); R_ABORT_UNLESS(setsysInitialize());
R_ABORT_UNLESS(gpioInitialize()); R_ABORT_UNLESS(gpio::Initialize());
}); });
/* Mount the SD card. */ /* Mount the SD card. */
@ -86,7 +86,7 @@ void __appInit(void) {
void __appExit(void) { void __appExit(void) {
fs::Unmount("sdmc"); fs::Unmount("sdmc");
gpioExit(); gpio::Exit();
setsysExit(); setsysExit();
pmshellExit(); pmshellExit();
pminfoExit(); pminfoExit();

View file

@ -92,7 +92,7 @@ void __appInit(void) {
R_ABORT_UNLESS(psmInitialize()); R_ABORT_UNLESS(psmInitialize());
R_ABORT_UNLESS(spsmInitialize()); R_ABORT_UNLESS(spsmInitialize());
R_ABORT_UNLESS(plInitialize(::PlServiceType_User)); R_ABORT_UNLESS(plInitialize(::PlServiceType_User));
R_ABORT_UNLESS(gpioInitialize()); gpio::Initialize();
R_ABORT_UNLESS(fsInitialize()); R_ABORT_UNLESS(fsInitialize());
}); });
@ -105,7 +105,7 @@ void __appExit(void) {
/* Cleanup services. */ /* Cleanup services. */
fsExit(); fsExit();
plExit(); plExit();
gpioExit(); gpio::Finalize();
spsmExit(); spsmExit();
psmExit(); psmExit();
lblExit(); lblExit();

View file

@ -33,22 +33,21 @@ namespace ams::fatal::srv {
bool IsInRepairWithoutVolHeld() { bool IsInRepairWithoutVolHeld() {
if (IsInRepair()) { if (IsInRepair()) {
GpioPadSession vol_btn; gpio::GpioPadSession vol_btn;
if (R_FAILED(gpioOpenSession(&vol_btn, GpioPadName_ButtonVolUp))) { if (R_FAILED(gpio::OpenSession(std::addressof(vol_btn), gpio::DeviceCode_ButtonVolUp))) {
return true; return true;
} }
/* Ensure we close even on early return. */ /* Ensure we close even on early return. */
ON_SCOPE_EXIT { gpioPadClose(&vol_btn); }; ON_SCOPE_EXIT { gpio::CloseSession(std::addressof(vol_btn)); };
/* Set direction input. */ /* Set direction input. */
gpioPadSetDirection(&vol_btn, GpioDirection_Input); gpio::SetDirection(std::addressof(vol_btn), gpio::Direction_Input);
/* Ensure that we're holding the volume button for a full second. */ /* Ensure that we're holding the volume button for a full second. */
auto start = os::GetSystemTick(); auto start = os::GetSystemTick();
do { do {
GpioValue val; if (gpio::GetValue(std::addressof(vol_btn)) != gpio::GpioValue_Low) {
if (R_FAILED(gpioPadGetValue(&vol_btn, &val)) || val != GpioValue_Low) {
return true; return true;
} }

View file

@ -156,33 +156,33 @@ namespace ams::fatal::srv {
RebootTimingObserver fatal_reboot_helper(config.IsFatalRebootEnabled(), config.GetFatalRebootTimeoutInterval()); RebootTimingObserver fatal_reboot_helper(config.IsFatalRebootEnabled(), config.GetFatalRebootTimeoutInterval());
bool check_vol_up = true, check_vol_down = true; bool check_vol_up = true, check_vol_down = true;
GpioPadSession vol_up_btn, vol_down_btn; gpio::GpioPadSession vol_up_btn, vol_down_btn;
if (R_FAILED(gpioOpenSession(&vol_up_btn, GpioPadName_ButtonVolUp))) { if (R_FAILED(gpio::OpenSession(std::addressof(vol_up_btn), gpio::DeviceCode_ButtonVolUp))) {
check_vol_up = false; check_vol_up = false;
} }
if (R_FAILED(gpioOpenSession(&vol_down_btn, GpioPadName_ButtonVolDown))) { if (R_FAILED(gpio::OpenSession(std::addressof(vol_down_btn), gpio::DeviceCode_ButtonVolDn))) {
check_vol_down = false; check_vol_down = false;
} }
/* Ensure we close on early return. */ /* Ensure we close on early return. */
ON_SCOPE_EXIT { if (check_vol_up) { gpioPadClose(&vol_up_btn); } }; ON_SCOPE_EXIT { if (check_vol_up) { gpio::CloseSession(std::addressof(vol_up_btn)); } };
ON_SCOPE_EXIT { if (check_vol_down) { gpioPadClose(&vol_down_btn); } }; ON_SCOPE_EXIT { if (check_vol_down) { gpio::CloseSession(std::addressof(vol_down_btn)); } };
/* Set direction input. */ /* Set direction input. */
if (check_vol_up) { if (check_vol_up) {
gpioPadSetDirection(&vol_up_btn, GpioDirection_Input); gpio::SetDirection(std::addressof(vol_up_btn), gpio::Direction_Input);
} }
if (check_vol_down) { if (check_vol_down) {
gpioPadSetDirection(&vol_down_btn, GpioDirection_Input); gpio::SetDirection(std::addressof(vol_down_btn), gpio::Direction_Input);
} }
BpcSleepButtonState state; BpcSleepButtonState state;
GpioValue val;
while (true) { while (true) {
if (fatal_reboot_helper.IsRebootTiming() || (quest_reboot_helper.IsRebootTiming()) || if (fatal_reboot_helper.IsRebootTiming() || (quest_reboot_helper.IsRebootTiming()) ||
(check_vol_up && R_SUCCEEDED(gpioPadGetValue(&vol_up_btn, &val)) && val == GpioValue_Low) || (check_vol_up && gpio::GetValue(std::addressof(vol_up_btn)) == gpio::GpioValue_Low) ||
(check_vol_down && R_SUCCEEDED(gpioPadGetValue(&vol_down_btn, &val)) && val == GpioValue_Low) || (check_vol_down && gpio::GetValue(std::addressof(vol_down_btn)) == gpio::GpioValue_Low) ||
(R_SUCCEEDED(bpcGetSleepButtonState(&state)) && state == BpcSleepButtonState_Held)) { (R_SUCCEEDED(bpcGetSleepButtonState(&state)) && state == BpcSleepButtonState_Held))
{
/* If any of the above conditions succeeded, we should reboot. */ /* If any of the above conditions succeeded, we should reboot. */
bpcRebootSystem(); bpcRebootSystem();
return; return;

View file

@ -70,16 +70,16 @@ namespace ams::fatal::srv {
/* Talk to the ALC5639 over GPIO, and disable audio output */ /* Talk to the ALC5639 over GPIO, and disable audio output */
{ {
GpioPadSession audio; gpio::GpioPadSession audio;
if (R_SUCCEEDED(gpioOpenSession(&audio, GpioPadName_AudioCodec))) { if (R_SUCCEEDED(gpio::OpenSession(std::addressof(audio), gpio::DeviceCode_CodecLdoEnTemp))) {
ON_SCOPE_EXIT { gpioPadClose(&audio); }; ON_SCOPE_EXIT { gpio::CloseSession(std::addressof(audio)); };
/* Set direction output, sleep 200 ms so it can take effect. */ /* Set direction output, sleep 200 ms so it can take effect. */
gpioPadSetDirection(&audio, GpioDirection_Output); gpio::SetDirection(std::addressof(audio), gpio::Direction_Output);
svcSleepThread(200000000UL); os::SleepThread(TimeSpan::FromMilliSeconds(200));
/* Pull audio codec low. */ /* Pull audio codec low. */
gpioPadSetValue(&audio, GpioValue_Low); gpio::SetValue(std::addressof(audio), gpio::GpioValue_Low);
} }
} }
} }