mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-23 04:41:12 +00:00
sdmmc: add DeviceDetector, gpio: implement client api
This commit is contained in:
parent
9a41e19004
commit
22fb4ff095
25 changed files with 950 additions and 42 deletions
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
|
|
19
libraries/libstratosphere/include/stratosphere/ddsf.hpp
Normal file
19
libraries/libstratosphere/include/stratosphere/ddsf.hpp
Normal 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>
|
|
@ -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,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
20
libraries/libstratosphere/include/stratosphere/gpio.hpp
Normal file
20
libraries/libstratosphere/include/stratosphere/gpio.hpp
Normal 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>
|
|
@ -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();
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
|
@ -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,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
190
libraries/libstratosphere/source/gpio/gpio_client_api.cpp
Normal file
190
libraries/libstratosphere/source/gpio/gpio_client_api.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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>
|
||||||
|
|
||||||
|
|
49
libraries/libvapours/include/vapours/device_code.hpp
Normal file
49
libraries/libvapours/include/vapours/device_code.hpp
Normal 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);
|
||||||
|
|
||||||
|
}
|
|
@ -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!"
|
||||||
|
|
262
libraries/libvapours/source/sdmmc/impl/sdmmc_device_detector.cpp
Normal file
262
libraries/libvapours/source/sdmmc/impl/sdmmc_device_detector.cpp
Normal 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
|
|
@ -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
|
|
@ -24,6 +24,7 @@ namespace ams::sdmmc::impl {
|
||||||
|
|
||||||
SdmmcControllerForPortSdCard0 g_sd_card0_host_controller;
|
SdmmcControllerForPortSdCard0 g_sd_card0_host_controller;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IHostController *GetHostControllerOfPortSdCard0() {
|
IHostController *GetHostControllerOfPortSdCard0() {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue