/* * 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 . */ #include #include "lm_log_server_proxy.hpp" #include "lm_sd_card_logger.hpp" #include "lm_log_buffer.hpp" #include "lm_event_log_transmitter.hpp" namespace ams::lm::srv { bool IsSleeping(); namespace { alignas(os::ThreadStackAlignment) u8 g_flush_thread_stack[8_KB]; constinit u8 g_fs_heap[32_KB]; constinit lmem::HeapHandle g_fs_heap_handle; constinit os::ThreadType g_flush_thread; os::Event g_stop_event(os::EventClearMode_ManualClear); os::Event g_sd_logging_event(os::EventClearMode_ManualClear); os::Event g_host_connection_event(os::EventClearMode_ManualClear); constinit std::unique_ptr g_sd_card_detection_event_notifier; os::SystemEvent g_sd_card_detection_event; void *AllocateForFs(size_t size) { return lmem::AllocateFromExpHeap(g_fs_heap_handle, size); } void DeallocateForFs(void *ptr, size_t size) { AMS_UNUSED(size); return lmem::FreeToExpHeap(g_fs_heap_handle, ptr); } void HostConnectionObserver(bool is_connected) { /* Update the host connection event. */ if (is_connected) { g_host_connection_event.Signal(); } else { g_host_connection_event.Clear(); /* Potentially cancel the log buffer push. */ if (!g_sd_logging_event.TryWait()) { LogBuffer::GetDefaultInstance().CancelPush(); } } } void SdLoggingObserver(bool is_available) { /* Update the SD card logging event. */ if (is_available) { g_sd_logging_event.Signal(); } else { g_sd_logging_event.Clear(); /* Potentially cancel the log buffer push. */ if (!g_host_connection_event.TryWait()) { LogBuffer::GetDefaultInstance().CancelPush(); } } } bool WaitForFlush() { while (true) { /* Wait for something to be signaled. */ os::WaitAny(g_stop_event.GetBase(), g_host_connection_event.GetBase(), g_sd_logging_event.GetBase(), g_sd_card_detection_event.GetBase()); /* If we're stopping, no flush. */ if (g_stop_event.TryWait()) { return false; } /* If host is connected/we're logging to sd, flush. */ if (g_host_connection_event.TryWait() || g_sd_logging_event.TryWait()) { return true; } /* If the sd card is newly inserted, flush. */ if (g_sd_card_detection_event.TryWait()) { g_sd_card_detection_event.Clear(); if (fs::IsSdCardInserted()) { return true; } } } } void FlushThreadFunction(void *) { /* Disable abort. */ fs::SetEnabledAutoAbort(false); /* Create fs heap. */ g_fs_heap_handle = lmem::CreateExpHeap(g_fs_heap, sizeof(g_fs_heap), lmem::CreateOption_None); AMS_ABORT_UNLESS(g_fs_heap_handle != nullptr); /* Set fs allocation functions. */ fs::SetAllocator(AllocateForFs, DeallocateForFs); /* Create SD card detection event notifier. */ R_ABORT_UNLESS(fs::OpenSdCardDetectionEventNotifier(std::addressof(g_sd_card_detection_event_notifier))); R_ABORT_UNLESS(g_sd_card_detection_event_notifier->BindEvent(g_sd_card_detection_event.GetBase(), os::EventClearMode_ManualClear)); /* Set connection observers. */ SdCardLogger::GetInstance().SetLoggingObserver(SdLoggingObserver); LogServerProxy::GetInstance().SetConnectionObserver(HostConnectionObserver); /* Do flush loop. */ do { if (LogBuffer::GetDefaultInstance().Flush()) { EventLogTransmitter::GetDefaultInstance().PushLogPacketDropCountIfExists(); } } while (WaitForFlush()); /* Clear connection observer. */ LogServerProxy::GetInstance().SetConnectionObserver(nullptr); /* Finalize the SD card logger. */ SdCardLogger::GetInstance().Finalize(); SdCardLogger::GetInstance().SetLoggingObserver(nullptr); /* Destroy the fs heap. */ lmem::DestroyExpHeap(g_fs_heap_handle); } } bool IsFlushAvailable() { /* If we're sleeping, we can't flush. */ if (IsSleeping()) { return false; } /* Try to wait for an event. */ if (os::TryWaitAny(g_stop_event.GetBase(), g_host_connection_event.GetBase(), g_sd_logging_event.GetBase()) < 0) { return false; } /* Return whether we're not stopping. */ return !os::TryWaitEvent(g_stop_event.GetBase()); } void InitializeFlushThread() { /* Create the flush thread. */ R_ABORT_UNLESS(os::CreateThread(std::addressof(g_flush_thread), FlushThreadFunction, nullptr, g_flush_thread_stack, sizeof(g_flush_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(lm, Flush))); os::SetThreadNamePointer(std::addressof(g_flush_thread), AMS_GET_SYSTEM_THREAD_NAME(lm, Flush)); /* Clear the stop event. */ g_stop_event.Clear(); /* Start the flush thread. */ os::StartThread(std::addressof(g_flush_thread)); } void FinalizeFlushThread() { /* Signal the flush thread to stop. */ g_stop_event.Signal(); /* Wait for the flush thread to stop. */ os::WaitThread(std::addressof(g_flush_thread)); os::DestroyThread(std::addressof(g_flush_thread)); } }