mirror of
				https://github.com/Atmosphere-NX/Atmosphere.git
				synced 2025-10-31 11:15:51 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			273 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			273 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) Atmosphère-NX
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify it
 | |
|  * under the terms and conditions of the GNU General Public License,
 | |
|  * version 2, as published by the Free Software Foundation.
 | |
|  *
 | |
|  * This program is distributed in the hope it will be useful, but WITHOUT
 | |
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 | |
|  * more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License
 | |
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | |
|  */
 | |
| #if defined(ATMOSPHERE_IS_STRATOSPHERE)
 | |
| #include <stratosphere.hpp>
 | |
| #elif defined(ATMOSPHERE_IS_MESOSPHERE)
 | |
| #include <mesosphere.hpp>
 | |
| #elif defined(ATMOSPHERE_IS_EXOSPHERE)
 | |
| #include <exosphere.hpp>
 | |
| #else
 | |
| #include <vapours.hpp>
 | |
| #endif
 | |
| #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(m_gpio_pad_session)) == m_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 (m_callback_info.inserted_callback != nullptr) {
 | |
|                 m_callback_info.inserted_callback(m_callback_info.inserted_callback_arg);
 | |
|             }
 | |
|         } else if (prev_inserted && !cur_inserted) {
 | |
|             /* Card was removed. */
 | |
|             if (m_callback_info.removed_callback != nullptr) {
 | |
|                 m_callback_info.removed_callback(m_callback_info.removed_callback_arg);
 | |
|             }
 | |
|         } else /* if (prev_inserted && cur_inserted) */ {
 | |
|             /* Card was removed, and then inserted. */
 | |
|             if (m_callback_info.removed_callback != nullptr) {
 | |
|                 m_callback_info.removed_callback(m_callback_info.removed_callback_arg);
 | |
|             }
 | |
| 
 | |
|             if (m_callback_info.inserted_callback != nullptr) {
 | |
|                 m_callback_info.inserted_callback(m_callback_info.inserted_callback_arg);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void DeviceDetector::DetectorThread() {
 | |
|         /* Initialize the gpio session. */
 | |
|         gpio::Initialize();
 | |
| 
 | |
|         /* Open and configure the pad session. */
 | |
|         gpio::OpenSession(std::addressof(m_gpio_pad_session), m_gpio_device_code);
 | |
|         gpio::SetDirection(std::addressof(m_gpio_pad_session), gpio::Direction_Input);
 | |
|         gpio::SetDebounceTime(std::addressof(m_gpio_pad_session), m_gpio_debounce_ms);
 | |
|         gpio::SetDebounceEnabled(std::addressof(m_gpio_pad_session), true);
 | |
|         gpio::SetInterruptMode(std::addressof(m_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(m_gpio_pad_session)));
 | |
| 
 | |
|         /* Initialize and link multi wait/holders. */
 | |
|         os::MultiWaitType multi_wait;
 | |
|         os::MultiWaitHolderType detector_thread_end_holder;
 | |
|         os::MultiWaitHolderType request_sleep_wake_event_holder;
 | |
|         os::MultiWaitHolderType gpio_event_holder;
 | |
|         os::InitializeMultiWait(std::addressof(multi_wait));
 | |
|         os::InitializeMultiWaitHolder(std::addressof(detector_thread_end_holder), std::addressof(m_detector_thread_end_event));
 | |
|         os::LinkMultiWaitHolder(std::addressof(multi_wait), std::addressof(detector_thread_end_holder));
 | |
|         os::InitializeMultiWaitHolder(std::addressof(request_sleep_wake_event_holder), std::addressof(m_request_sleep_wake_event));
 | |
|         os::LinkMultiWaitHolder(std::addressof(multi_wait), std::addressof(request_sleep_wake_event_holder));
 | |
|         os::InitializeMultiWaitHolder(std::addressof(gpio_event_holder), std::addressof(gpio_event));
 | |
|         os::LinkMultiWaitHolder(std::addressof(multi_wait), std::addressof(gpio_event_holder));
 | |
| 
 | |
|         /* Wait before detecting the initial state of the card. */
 | |
|         os::SleepThread(TimeSpan::FromMilliSeconds(m_gpio_debounce_ms));
 | |
|         bool cur_inserted = this->IsCurrentInserted();
 | |
|         m_is_prev_inserted = cur_inserted;
 | |
| 
 | |
|         /* Set state as awake. */
 | |
|         m_state = State_Awake;
 | |
|         os::SignalEvent(std::addressof(m_ready_device_status_event));
 | |
| 
 | |
|         /* Enable interrupts to be informed of device status. */
 | |
|         gpio::SetInterruptEnable(std::addressof(m_gpio_pad_session), true);
 | |
| 
 | |
|         /* Wait, servicing our events. */
 | |
|         while (true) {
 | |
|             /* Get the signaled holder. */
 | |
|             os::MultiWaitHolderType *signaled_holder = os::WaitAny(std::addressof(multi_wait));
 | |
| 
 | |
|             /* Process the holder. */
 | |
|             bool insert_change = false;
 | |
|             if (signaled_holder == std::addressof(detector_thread_end_holder)) {
 | |
|                 /* We should kill ourselves. */
 | |
|                 os::ClearEvent(std::addressof(m_detector_thread_end_event));
 | |
|                 m_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(m_request_sleep_wake_event));
 | |
|                 m_state = State_Sleep;
 | |
|                 os::SignalEvent(std::addressof(m_acknowledge_sleep_awake_event));
 | |
| 
 | |
|                 /* Temporarily unlink our interrupt event. */
 | |
|                 os::UnlinkMultiWaitHolder(std::addressof(gpio_event_holder));
 | |
| 
 | |
|                 /* Wait to be signaled. */
 | |
|                 signaled_holder = os::WaitAny(std::addressof(multi_wait));
 | |
| 
 | |
|                 /* Link our interrupt event back in. */
 | |
|                 os::LinkMultiWaitHolder(std::addressof(multi_wait), 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(m_request_sleep_wake_event));
 | |
|                 m_state = State_Awake;
 | |
|                 os::SignalEvent(std::addressof(m_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(m_detector_thread_end_event));
 | |
|                     m_state = State_Finalized;
 | |
|                     break;
 | |
|                 } else /* if (signaled_holder == std::addressof(request_sleep_wake_event_holder)) */ {
 | |
|                     if ((m_force_detection) ||
 | |
|                         (({ bool active; R_SUCCEEDED(gpio::IsWakeEventActive(std::addressof(active), m_gpio_device_code)) && active; })) ||
 | |
|                         (os::TryWaitSystemEvent(std::addressof(gpio_event))) ||
 | |
|                         (m_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 (m_device_detection_event_callback != nullptr) {
 | |
|                     m_device_detection_event_callback(m_device_detection_event_callback_arg);
 | |
|                 }
 | |
| 
 | |
|                 /* Clear the interrupt event. */
 | |
|                 os::ClearSystemEvent(std::addressof(gpio_event));
 | |
|                 gpio::ClearInterruptStatus(std::addressof(m_gpio_pad_session));
 | |
|                 gpio::SetInterruptEnable(std::addressof(m_gpio_pad_session), true);
 | |
| 
 | |
|                 /* Update insertion status. */
 | |
|                 cur_inserted = this->IsCurrentInserted();
 | |
|                 this->HandleDeviceStatus(m_is_prev_inserted, cur_inserted);
 | |
|                 m_is_prev_inserted = cur_inserted;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /* Disable interrupts to our gpio event. */
 | |
|         gpio::SetInterruptEnable(std::addressof(m_gpio_pad_session), false);
 | |
| 
 | |
|         /* Finalize and unlink multi wait/holders. */
 | |
|         os::UnlinkMultiWaitHolder(std::addressof(gpio_event_holder));
 | |
|         os::FinalizeMultiWaitHolder(std::addressof(gpio_event_holder));
 | |
|         os::UnlinkMultiWaitHolder(std::addressof(request_sleep_wake_event_holder));
 | |
|         os::FinalizeMultiWaitHolder(std::addressof(request_sleep_wake_event_holder));
 | |
|         os::UnlinkMultiWaitHolder(std::addressof(detector_thread_end_holder));
 | |
|         os::FinalizeMultiWaitHolder(std::addressof(detector_thread_end_holder));
 | |
|         os::FinalizeMultiWait(std::addressof(multi_wait));
 | |
| 
 | |
|         /* Finalize the gpio session. */
 | |
|         gpio::UnbindInterrupt(std::addressof(m_gpio_pad_session));
 | |
|         gpio::CloseSession(std::addressof(m_gpio_pad_session));
 | |
|         gpio::Finalize();
 | |
|     }
 | |
| 
 | |
|     void DeviceDetector::Initialize(CallbackInfo *ci) {
 | |
|         /* Transition our state from finalized to initializing. */
 | |
|         AMS_ABORT_UNLESS(m_state == State_Finalized);
 | |
|         m_state = State_Initializing;
 | |
| 
 | |
|         /* Set our callback infos. */
 | |
|         m_callback_info = *ci;
 | |
| 
 | |
|         /* Initialize our events. */
 | |
|         os::InitializeEvent(std::addressof(m_ready_device_status_event), false, os::EventClearMode_ManualClear);
 | |
|         os::InitializeEvent(std::addressof(m_request_sleep_wake_event), false, os::EventClearMode_ManualClear);
 | |
|         os::InitializeEvent(std::addressof(m_acknowledge_sleep_awake_event), false, os::EventClearMode_ManualClear);
 | |
|         os::InitializeEvent(std::addressof(m_detector_thread_end_event), false, os::EventClearMode_ManualClear);
 | |
| 
 | |
|         /* Create and start the detector thread. */
 | |
|         os::CreateThread(std::addressof(m_detector_thread), DetectorThreadEntry, this, m_detector_thread_stack, sizeof(m_detector_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(sdmmc, DeviceDetector));
 | |
|         os::SetThreadNamePointer(std::addressof(m_detector_thread), AMS_GET_SYSTEM_THREAD_NAME(sdmmc, DeviceDetector));
 | |
|         os::StartThread(std::addressof(m_detector_thread));
 | |
|     }
 | |
| 
 | |
|     void DeviceDetector::Finalize() {
 | |
|         /* Ensure we're not already finalized. */
 | |
|         AMS_ABORT_UNLESS(m_state != State_Finalized);
 | |
| 
 | |
|         /* Signal event to end the detector thread. */
 | |
|         os::SignalEvent(std::addressof(m_detector_thread_end_event));
 | |
|         os::WaitThread(std::addressof(m_detector_thread));
 | |
| 
 | |
|         /* Finalize thread and events. */
 | |
|         os::DestroyThread(std::addressof(m_detector_thread));
 | |
|         os::FinalizeEvent(std::addressof(m_ready_device_status_event));
 | |
|         os::FinalizeEvent(std::addressof(m_request_sleep_wake_event));
 | |
|         os::FinalizeEvent(std::addressof(m_acknowledge_sleep_awake_event));
 | |
|         os::FinalizeEvent(std::addressof(m_detector_thread_end_event));
 | |
|     }
 | |
| 
 | |
|     void DeviceDetector::PutToSleep() {
 | |
|         /* Signal request, wait for acknowledgement. */
 | |
|         os::SignalEvent(std::addressof(m_request_sleep_wake_event));
 | |
|         os::WaitEvent(std::addressof(m_acknowledge_sleep_awake_event));
 | |
|         os::ClearEvent(std::addressof(m_acknowledge_sleep_awake_event));
 | |
|     }
 | |
| 
 | |
|     void DeviceDetector::Awaken(bool force_det) {
 | |
|         /* Signal request, wait for acknowledgement. */
 | |
|         m_force_detection = force_det;
 | |
|         os::SignalEvent(std::addressof(m_request_sleep_wake_event));
 | |
|         os::WaitEvent(std::addressof(m_acknowledge_sleep_awake_event));
 | |
|         os::ClearEvent(std::addressof(m_acknowledge_sleep_awake_event));
 | |
|     }
 | |
| 
 | |
|     bool DeviceDetector::IsInserted() {
 | |
|         bool inserted = false;
 | |
| 
 | |
|         switch (m_state) {
 | |
|             case State_Initializing:
 | |
|                 /* Wait for us to know whether the device is inserted. */
 | |
|                 os::WaitEvent(std::addressof(m_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 = m_is_prev_inserted;
 | |
|                 break;
 | |
|         }
 | |
| 
 | |
|         return inserted;
 | |
|     }
 | |
| 
 | |
|     void DeviceDetector::RegisterDetectionEventCallback(DeviceDetectionEventCallback cb, void *arg) {
 | |
|         m_device_detection_event_callback_arg = arg;
 | |
|         m_device_detection_event_callback     = cb;
 | |
|     }
 | |
| 
 | |
|     void DeviceDetector::UnregisterDetectionEventCallback() {
 | |
|         m_device_detection_event_callback = nullptr;
 | |
|     }
 | |
| 
 | |
| }
 | |
| 
 | |
| #endif
 |