mirror of
				https://github.com/Atmosphere-NX/Atmosphere.git
				synced 2025-10-31 03:05:48 +01:00 
			
		
		
		
	* result: try out some experimental shenanigans * result: sketch out some more shenanigans * result: see what it looks like to convert kernel to use result conds instead of guards * make rest of kernel use experimental new macro-ing
		
			
				
	
	
		
			151 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			151 lines
		
	
	
		
			4.7 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/>.
 | |
|  */
 | |
| #include <mesosphere.hpp>
 | |
| 
 | |
| namespace ams::kern {
 | |
| 
 | |
|     namespace {
 | |
| 
 | |
|         constinit KLightLock g_interrupt_event_lock;
 | |
|         constinit KInterruptEventTask *g_interrupt_event_task_table[KInterruptController::NumInterrupts] = {};
 | |
| 
 | |
|     }
 | |
| 
 | |
|     Result KInterruptEvent::Initialize(int32_t interrupt_name, ams::svc::InterruptType type) {
 | |
|         MESOSPHERE_ASSERT_THIS();
 | |
| 
 | |
|         /* Verify the interrupt is defined and global. */
 | |
|         R_UNLESS(Kernel::GetInterruptManager().IsInterruptDefined(interrupt_name), svc::ResultOutOfRange());
 | |
|         R_UNLESS(Kernel::GetInterruptManager().IsGlobal(interrupt_name),           svc::ResultOutOfRange());
 | |
| 
 | |
|         /* Set interrupt id. */
 | |
|         m_interrupt_id = interrupt_name;
 | |
| 
 | |
|         /* Set core id. */
 | |
|         m_core_id = GetCurrentCoreId();
 | |
| 
 | |
|         /* Initialize readable event base. */
 | |
|         KReadableEvent::Initialize(nullptr);
 | |
| 
 | |
|         /* Try to register the task. */
 | |
|         R_TRY(KInterruptEventTask::Register(m_interrupt_id, m_core_id, type == ams::svc::InterruptType_Level, this));
 | |
| 
 | |
|         /* Mark initialized. */
 | |
|         m_is_initialized = true;
 | |
|         R_SUCCEED();
 | |
|     }
 | |
| 
 | |
|     void KInterruptEvent::Finalize() {
 | |
|         MESOSPHERE_ASSERT_THIS();
 | |
| 
 | |
|         g_interrupt_event_task_table[m_interrupt_id]->Unregister(m_interrupt_id, m_core_id);
 | |
| 
 | |
|         /* Perform inherited finalization. */
 | |
|         KReadableEvent::Finalize();
 | |
|     }
 | |
| 
 | |
|     Result KInterruptEvent::Reset() {
 | |
|         MESOSPHERE_ASSERT_THIS();
 | |
| 
 | |
|         /* Lock the scheduler. */
 | |
|         KScopedSchedulerLock sl;
 | |
| 
 | |
|         /* Clear the event. */
 | |
|         R_TRY(KReadableEvent::Reset());
 | |
| 
 | |
|         /* Clear the interrupt. */
 | |
|         Kernel::GetInterruptManager().ClearInterrupt(m_interrupt_id, m_core_id);
 | |
| 
 | |
|         R_SUCCEED();
 | |
|     }
 | |
| 
 | |
|     Result KInterruptEventTask::Register(s32 interrupt_id, s32 core_id, bool level, KInterruptEvent *event) {
 | |
|         /* Lock the task table. */
 | |
|         KScopedLightLock lk(g_interrupt_event_lock);
 | |
| 
 | |
|         /* Get a task for the id. */
 | |
|         bool allocated = false;
 | |
|         KInterruptEventTask *task = g_interrupt_event_task_table[interrupt_id];
 | |
|         if (task != nullptr) {
 | |
|             /* Check that there's not already an event for this task. */
 | |
|             R_UNLESS(task->m_event == nullptr, svc::ResultBusy());
 | |
|         } else {
 | |
|             /* Allocate a new task. */
 | |
|             task = KInterruptEventTask::Allocate();
 | |
|             R_UNLESS(task != nullptr, svc::ResultOutOfResource());
 | |
| 
 | |
|             allocated = true;
 | |
|         }
 | |
| 
 | |
|         /* Ensure that the task is cleaned up if anything goes wrong. */
 | |
|         ON_RESULT_FAILURE { if (allocated) { KInterruptEventTask::Free(task); } };
 | |
| 
 | |
|         /* Register/bind the interrupt task. */
 | |
|         {
 | |
|             /* Lock the scheduler. */
 | |
|             KScopedSchedulerLock sl;
 | |
| 
 | |
|             /* Bind the interrupt handler. */
 | |
|             R_TRY(Kernel::GetInterruptManager().BindHandler(task, interrupt_id, core_id, KInterruptController::PriorityLevel_High, true, level));
 | |
| 
 | |
|             /* Set the event. */
 | |
|             task->m_event = event;
 | |
|         }
 | |
| 
 | |
|         /* If we allocated, set the event in the table. */
 | |
|         if (allocated) {
 | |
|             g_interrupt_event_task_table[interrupt_id] = task;
 | |
|         }
 | |
| 
 | |
|         R_SUCCEED();
 | |
|     }
 | |
| 
 | |
|     void KInterruptEventTask::Unregister(s32 interrupt_id, s32 core_id) {
 | |
|         MESOSPHERE_ASSERT_THIS();
 | |
| 
 | |
|         /* Lock the task table. */
 | |
|         KScopedLightLock lk(g_interrupt_event_lock);
 | |
| 
 | |
|         /* Lock the scheduler. */
 | |
|         KScopedSchedulerLock sl;
 | |
| 
 | |
|         /* Ensure we can unregister. */
 | |
|         MESOSPHERE_ABORT_UNLESS(g_interrupt_event_task_table[interrupt_id] == this);
 | |
|         MESOSPHERE_ABORT_UNLESS(m_event != nullptr);
 | |
| 
 | |
|         /* Unbind the interrupt. */
 | |
|         m_event = nullptr;
 | |
|         Kernel::GetInterruptManager().UnbindHandler(interrupt_id, core_id);
 | |
|     }
 | |
| 
 | |
|     KInterruptTask *KInterruptEventTask::OnInterrupt(s32 interrupt_id) {
 | |
|         MESOSPHERE_ASSERT_THIS();
 | |
|         MESOSPHERE_UNUSED(interrupt_id);
 | |
|         return this;
 | |
|     }
 | |
| 
 | |
|     void KInterruptEventTask::DoTask() {
 | |
|         MESOSPHERE_ASSERT_THIS();
 | |
| 
 | |
|         /* Lock the scheduler. */
 | |
|         KScopedSchedulerLock sl;
 | |
| 
 | |
|         if (m_event != nullptr) {
 | |
|             m_event->Signal();
 | |
|         }
 | |
|     }
 | |
| }
 |