diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 4e3a614a4..de52a0e46 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -134,6 +134,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Service, SSL) \ SUB(Service, TCAP) \ SUB(Service, Time) \ + SUB(Service, TMA) \ SUB(Service, USB) \ SUB(Service, VI) \ SUB(Service, WLAN) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 9e2f42c5d..80b66c686 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -102,6 +102,7 @@ enum class Class : u8 { Service_SSL, ///< The SSL service Service_TCAP, ///< The TCAP service. Service_Time, ///< The time service + Service_TMA, ///< The TMA (Target Manager Agent) service Service_USB, ///< The USB (Universal Serial Bus) service Service_VI, ///< The VI (Video interface) service Service_WLAN, ///< The WLAN (Wireless local area network) service diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 4c83aef78..378c17fdc 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1102,6 +1102,16 @@ add_library(core STATIC hle/service/ssl/ssl.h hle/service/ssl/ssl_backend.h hle/service/ssl/ssl_types.h + hle/service/tma/file_io.cpp + hle/service/tma/file_io.h + hle/service/tma/htc.cpp + hle/service/tma/htc.h + hle/service/tma/htc_tenv.cpp + hle/service/tma/htc_tenv.h + hle/service/tma/htcs.cpp + hle/service/tma/htcs.h + hle/service/tma/tma.cpp + hle/service/tma/tma.h hle/service/usb/usb.cpp hle/service/usb/usb.h hle/service/vi/application_display_service.cpp diff --git a/src/core/hle/service/services.cpp b/src/core/hle/service/services.cpp index 3defa4b31..9dbf0cc34 100644 --- a/src/core/hle/service/services.cpp +++ b/src/core/hle/service/services.cpp @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/services.h" @@ -59,6 +60,7 @@ #include "core/hle/service/sockets/sockets.h" #include "core/hle/service/spl/spl_module.h" #include "core/hle/service/ssl/ssl.h" +#include "core/hle/service/tma/tma.h" #include "core/hle/service/usb/usb.h" #include "core/hle/service/vi/vi.h" @@ -127,6 +129,7 @@ Services::Services(std::shared_ptr& sm, Core::System& system kernel.RunOnGuestCoreProcess("ro", [&] { RO::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("spl", [&] { SPL::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("ssl", [&] { SSL::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("tma", [&] { TMA::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("usb", [&] { USB::LoopProcess(system); }); // clang-format on } diff --git a/src/core/hle/service/tma/file_io.cpp b/src/core/hle/service/tma/file_io.cpp new file mode 100644 index 000000000..12bb74af5 --- /dev/null +++ b/src/core/hle/service/tma/file_io.cpp @@ -0,0 +1,271 @@ +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/tma/file_io.h" + +namespace Service::TMA { + +IFileAccessor::IFileAccessor(Core::System& system_) : ServiceFramework{system_, "IFileAccessor"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&IFileAccessor::ReadFile>, "ReadFile"}, + {1, D<&IFileAccessor::WriteFile>, "WriteFile"}, + {2, D<&IFileAccessor::GetFileSize>, "GetFileSize"}, + {3, D<&IFileAccessor::SetFileSize>, "SetFileSize"}, + {4, D<&IFileAccessor::FlushFile>, "FlushFile"}, + {5, D<&IFileAccessor::SetPriorityForFile>, "SetPriorityForFile"}, + {6, D<&IFileAccessor::GetPriorityForFile>, "GetPriorityForFile"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IFileAccessor::~IFileAccessor() = default; + +Result IFileAccessor::ReadFile(u32 read_option, Out out_bytes_read, s64 offset, + OutBuffer out_buffer) { + LOG_WARNING(Service_TMA, "(STUBBED) called, read_option={}, offset={}", read_option, offset); + + // No actual file data to read + *out_bytes_read = 0; + + R_SUCCEED(); +} + +Result IFileAccessor::WriteFile(u32 write_option, s64 offset, InBuffer buffer) { + LOG_WARNING(Service_TMA, "(STUBBED) called, write_option={}, offset={}, size={}", + write_option, offset, buffer.size()); + + // Update file size if writing beyond current size + if (offset + static_cast(buffer.size()) > file_size) { + file_size = offset + static_cast(buffer.size()); + } + + R_SUCCEED(); +} + +Result IFileAccessor::GetFileSize(Out out_file_size) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + *out_file_size = file_size; + + R_SUCCEED(); +} + +Result IFileAccessor::SetFileSize(s64 file_size_new) { + LOG_WARNING(Service_TMA, "(STUBBED) called, file_size={}", file_size_new); + + file_size = file_size_new; + + R_SUCCEED(); +} + +Result IFileAccessor::FlushFile() { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + R_SUCCEED(); +} + +Result IFileAccessor::SetPriorityForFile(s32 priority_new) { + LOG_WARNING(Service_TMA, "(STUBBED) called, priority={}", priority_new); + + priority = priority_new; + + R_SUCCEED(); +} + +Result IFileAccessor::GetPriorityForFile(Out out_priority) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + *out_priority = priority; + + R_SUCCEED(); +} + +IDirectoryAccessor::IDirectoryAccessor(Core::System& system_) : ServiceFramework{system_, "IDirectoryAccessor"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&IDirectoryAccessor::GetEntryCount>, "GetEntryCount"}, + {1, D<&IDirectoryAccessor::ReadDirectory>, "ReadDirectory"}, + {2, D<&IDirectoryAccessor::SetPriorityForDirectory>, "SetPriorityForDirectory"}, + {3, D<&IDirectoryAccessor::GetPriorityForDirectory>, "GetPriorityForDirectory"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IDirectoryAccessor::~IDirectoryAccessor() = default; + +Result IDirectoryAccessor::GetEntryCount(Out out_entry_count) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + // Return 0 entries + *out_entry_count = 0; + + R_SUCCEED(); +} + +Result IDirectoryAccessor::ReadDirectory(Out out_entries_read, OutBuffer out_entry_buffer) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + // No entries to read + *out_entries_read = 0; + + R_SUCCEED(); +} + +Result IDirectoryAccessor::SetPriorityForDirectory(s32 priority_new) { + LOG_WARNING(Service_TMA, "(STUBBED) called, priority={}", priority_new); + + priority = priority_new; + + R_SUCCEED(); +} + +Result IDirectoryAccessor::GetPriorityForDirectory(Out out_priority) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + *out_priority = priority; + + R_SUCCEED(); +} + +IFileManager::IFileManager(Core::System& system_) : ServiceFramework{system_, "file_io"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&IFileManager::OpenFile>, "OpenFile"}, + {1, D<&IFileManager::FileExists>, "FileExists"}, + {2, D<&IFileManager::DeleteFile>, "DeleteFile"}, + {3, D<&IFileManager::RenameFile>, "RenameFile"}, + {4, D<&IFileManager::GetIOType>, "GetIOType"}, + {5, D<&IFileManager::OpenDirectory>, "OpenDirectory"}, + {6, D<&IFileManager::DirectoryExists>, "DirectoryExists"}, + {7, D<&IFileManager::CreateDirectory>, "CreateDirectory"}, + {8, D<&IFileManager::DeleteDirectory>, "DeleteDirectory"}, + {9, D<&IFileManager::RenameDirectory>, "RenameDirectory"}, + {10, D<&IFileManager::CreateFile>, "CreateFile"}, + {11, D<&IFileManager::GetFileTimeStamp>, "GetFileTimeStamp"}, + {12, D<&IFileManager::GetCaseSensitivePath>, "GetCaseSensitivePath"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IFileManager::~IFileManager() = default; + +Result IFileManager::OpenFile(OutInterface out_file_accessor, + InBuffer path, u32 open_mode) { + LOG_WARNING(Service_TMA, "(STUBBED) called, open_mode={}", open_mode); + + *out_file_accessor = std::make_shared(system); + + R_SUCCEED(); +} + +Result IFileManager::FileExists(Out out_exists, InBuffer path) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + // No files exist on host + *out_exists = false; + + R_SUCCEED(); +} + +Result IFileManager::DeleteFile(InBuffer path) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + R_SUCCEED(); +} + +Result IFileManager::RenameFile(InBuffer src_path, + InBuffer dst_path) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + R_SUCCEED(); +} + +Result IFileManager::GetIOType(Out out_entry_type, InBuffer path) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + // Return 0 (does not exist) + *out_entry_type = 0; + + R_SUCCEED(); +} + +Result IFileManager::OpenDirectory(OutInterface out_directory_accessor, + InBuffer path, u32 open_mode) { + LOG_WARNING(Service_TMA, "(STUBBED) called, open_mode={}", open_mode); + + *out_directory_accessor = std::make_shared(system); + + R_SUCCEED(); +} + +Result IFileManager::DirectoryExists(Out out_exists, InBuffer path) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + // No directories exist on host + *out_exists = false; + + R_SUCCEED(); +} + +Result IFileManager::CreateDirectory(InBuffer path) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + R_SUCCEED(); +} + +Result IFileManager::DeleteDirectory(InBuffer path, bool is_recursive) { + LOG_WARNING(Service_TMA, "(STUBBED) called, is_recursive={}", is_recursive); + + R_SUCCEED(); +} + +Result IFileManager::RenameDirectory(InBuffer src_path, + InBuffer dst_path) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + R_SUCCEED(); +} + +Result IFileManager::CreateFile(InBuffer path, s64 size) { + LOG_WARNING(Service_TMA, "(STUBBED) called, size={}", size); + + R_SUCCEED(); +} + +Result IFileManager::GetFileTimeStamp(Out out_created_time, Out out_accessed_time, + Out out_modified_time, InBuffer path) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + // Return 0 timestamps + *out_created_time = 0; + *out_accessed_time = 0; + *out_modified_time = 0; + + R_SUCCEED(); +} + +Result IFileManager::GetCaseSensitivePath(OutBuffer out_path, + InBuffer path) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + // Return the input path as-is + size_t copy_size = std::min(out_path.size(), path.size()); + std::memcpy(out_path.data(), path.data(), copy_size); + + // Null terminate if there's space + if (copy_size < out_path.size()) { + out_path[copy_size] = 0; + } + + R_SUCCEED(); +} + +} // namespace Service::TMA \ No newline at end of file diff --git a/src/core/hle/service/tma/file_io.h b/src/core/hle/service/tma/file_io.h new file mode 100644 index 000000000..29d5388d2 --- /dev/null +++ b/src/core/hle/service/tma/file_io.h @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::TMA { + +class IFileAccessor final : public ServiceFramework { +public: + explicit IFileAccessor(Core::System& system_); + ~IFileAccessor() override; + +private: + Result ReadFile(u32 read_option, Out out_bytes_read, s64 offset, + OutBuffer out_buffer); + Result WriteFile(u32 write_option, s64 offset, InBuffer buffer); + Result GetFileSize(Out out_file_size); + Result SetFileSize(s64 file_size); + Result FlushFile(); + Result SetPriorityForFile(s32 priority); + Result GetPriorityForFile(Out out_priority); + + s64 file_size{}; + s32 priority{}; +}; + +class IDirectoryAccessor final : public ServiceFramework { +public: + explicit IDirectoryAccessor(Core::System& system_); + ~IDirectoryAccessor() override; + +private: + Result GetEntryCount(Out out_entry_count); + Result ReadDirectory(Out out_entries_read, OutBuffer out_entry_buffer); + Result SetPriorityForDirectory(s32 priority); + Result GetPriorityForDirectory(Out out_priority); + + s32 priority{}; +}; + +class IFileManager final : public ServiceFramework { +public: + explicit IFileManager(Core::System& system_); + ~IFileManager() override; + +private: + Result OpenFile(OutInterface out_file_accessor, + InBuffer path, u32 open_mode); + Result FileExists(Out out_exists, InBuffer path); + Result DeleteFile(InBuffer path); + Result RenameFile(InBuffer src_path, + InBuffer dst_path); + Result GetIOType(Out out_entry_type, InBuffer path); + Result OpenDirectory(OutInterface out_directory_accessor, + InBuffer path, u32 open_mode); + Result DirectoryExists(Out out_exists, InBuffer path); + Result CreateDirectory(InBuffer path); + Result DeleteDirectory(InBuffer path, bool is_recursive); + Result RenameDirectory(InBuffer src_path, + InBuffer dst_path); + Result CreateFile(InBuffer path, s64 size); + Result GetFileTimeStamp(Out out_created_time, Out out_accessed_time, + Out out_modified_time, InBuffer path); + Result GetCaseSensitivePath(OutBuffer out_path, + InBuffer path); +}; + +} // namespace Service::TMA \ No newline at end of file diff --git a/src/core/hle/service/tma/htc.cpp b/src/core/hle/service/tma/htc.cpp new file mode 100644 index 000000000..2722b0a18 --- /dev/null +++ b/src/core/hle/service/tma/htc.cpp @@ -0,0 +1,206 @@ +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/tma/htc.h" + +// This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent +#undef CreateEvent + +namespace Service::TMA { + +IHtcManager::IHtcManager(Core::System& system_) + : ServiceFramework{system_, "htc"}, service_context{system_, "htc"} { + + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&IHtcManager::GetEnvironmentVariable>, "GetEnvironmentVariable"}, + {1, D<&IHtcManager::GetEnvironmentVariableLength>, "GetEnvironmentVariableLength"}, + {2, D<&IHtcManager::BindHostConnectionEvent>, "BindHostConnectionEvent"}, + {3, D<&IHtcManager::BindHostDisconnectionEvent>, "BindHostDisconnectionEvent"}, + {4, D<&IHtcManager::BindHostConnectionEventForSystem>, "BindHostConnectionEventForSystem"}, + {5, D<&IHtcManager::BindHostDisconnectionEventForSystem>, "BindHostDisconnectionEventForSystem"}, + {6, D<&IHtcManager::GetBridgeIpAddress>, "GetBridgeIpAddress"}, + {7, D<&IHtcManager::GetBridgePort>, "GetBridgePort"}, + {8, D<&IHtcManager::SetCradleAttached>, "SetCradleAttached"}, + {9, D<&IHtcManager::GetBridgeSubnetMask>, "GetBridgeSubnetMask"}, + {10, D<&IHtcManager::GetBridgeMacAddress>, "GetBridgeMacAddress"}, + {11, D<&IHtcManager::GetWorkingDirectoryPath>, "GetWorkingDirectoryPath"}, + {12, D<&IHtcManager::GetWorkingDirectoryPathSize>, "GetWorkingDirectoryPathSize"}, + {13, D<&IHtcManager::RunOnHostStart>, "RunOnHostStart"}, + {14, D<&IHtcManager::RunOnHostResults>, "RunOnHostResults"}, + {21, D<&IHtcManager::BeginUpdateBridge>, "BeginUpdateBridge"}, + {22, D<&IHtcManager::ContinueUpdateBridge>, "ContinueUpdateBridge"}, + {23, D<&IHtcManager::EndUpdateBridge>, "EndUpdateBridge"}, + {24, D<&IHtcManager::GetBridgeType>, "GetBridgeType"}, + }; + // clang-format on + + RegisterHandlers(functions); + + // Create events for host connection management + connection_event = service_context.CreateEvent("htc:connection"); + disconnection_event = service_context.CreateEvent("htc:disconnection"); + system_connection_event = service_context.CreateEvent("htc:system_connection"); + system_disconnection_event = service_context.CreateEvent("htc:system_disconnection"); +} + +IHtcManager::~IHtcManager() { + service_context.CloseEvent(connection_event); + service_context.CloseEvent(disconnection_event); + service_context.CloseEvent(system_connection_event); + service_context.CloseEvent(system_disconnection_event); +} + +Result IHtcManager::GetEnvironmentVariable(OutBuffer out_value, + InBuffer name) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + // Clear output buffer as no environment variables are set + std::memset(out_value.data(), 0, out_value.size()); + + R_SUCCEED(); +} + +Result IHtcManager::GetEnvironmentVariableLength(Out out_length, + InBuffer name) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + // Return 0 length for all environment variables + *out_length = 0; + + R_SUCCEED(); +} + +Result IHtcManager::BindHostConnectionEvent(OutCopyHandle out_event) { + LOG_DEBUG(Service_TMA, "called"); + + *out_event = &connection_event->GetReadableEvent(); + + R_SUCCEED(); +} + +Result IHtcManager::BindHostDisconnectionEvent(OutCopyHandle out_event) { + LOG_DEBUG(Service_TMA, "called"); + + *out_event = &disconnection_event->GetReadableEvent(); + + R_SUCCEED(); +} + +Result IHtcManager::BindHostConnectionEventForSystem(OutCopyHandle out_event) { + LOG_DEBUG(Service_TMA, "called"); + + *out_event = &system_connection_event->GetReadableEvent(); + + R_SUCCEED(); +} + +Result IHtcManager::BindHostDisconnectionEventForSystem(OutCopyHandle out_event) { + LOG_DEBUG(Service_TMA, "called"); + + *out_event = &system_disconnection_event->GetReadableEvent(); + + R_SUCCEED(); +} + +Result IHtcManager::GetBridgeIpAddress(Out out_ip_address) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + // Return localhost IP address as default + *out_ip_address = 0x7F000001; // 127.0.0.1 + + R_SUCCEED(); +} + +Result IHtcManager::GetBridgePort(Out out_port) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + // Return default debug port + *out_port = 19999; + + R_SUCCEED(); +} + +Result IHtcManager::SetCradleAttached(bool is_attached) { + LOG_WARNING(Service_TMA, "(STUBBED) called, is_attached={}", is_attached); + + R_SUCCEED(); +} + +Result IHtcManager::GetBridgeSubnetMask(Out out_subnet_mask) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + // Return default subnet mask + *out_subnet_mask = 0xFF000000; // 255.0.0.0 + + R_SUCCEED(); +} + +Result IHtcManager::GetBridgeMacAddress(OutBuffer out_mac_address) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + // Clear MAC address buffer + std::memset(out_mac_address.data(), 0, std::min(out_mac_address.size(), 6)); + + R_SUCCEED(); +} + +Result IHtcManager::GetWorkingDirectoryPath(OutBuffer out_path) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + // Return empty path + if (out_path.size() > 0) { + out_path[0] = 0; + } + + R_SUCCEED(); +} + +Result IHtcManager::GetWorkingDirectoryPathSize(Out out_size) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + *out_size = 1; // Just null terminator + + R_SUCCEED(); +} + +Result IHtcManager::RunOnHostStart() { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + R_SUCCEED(); +} + +Result IHtcManager::RunOnHostResults() { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + R_SUCCEED(); +} + +Result IHtcManager::BeginUpdateBridge() { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + R_SUCCEED(); +} + +Result IHtcManager::ContinueUpdateBridge() { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + R_SUCCEED(); +} + +Result IHtcManager::EndUpdateBridge() { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + R_SUCCEED(); +} + +Result IHtcManager::GetBridgeType(Out out_bridge_type) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + *out_bridge_type = 0; // Default bridge type + + R_SUCCEED(); +} + +} // namespace Service::TMA \ No newline at end of file diff --git a/src/core/hle/service/tma/htc.h b/src/core/hle/service/tma/htc.h new file mode 100644 index 000000000..d917d40b0 --- /dev/null +++ b/src/core/hle/service/tma/htc.h @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" + +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + +namespace Service::TMA { + +class IHtcManager final : public ServiceFramework { +public: + explicit IHtcManager(Core::System& system_); + ~IHtcManager() override; + +private: + Result GetEnvironmentVariable(OutBuffer out_value, + InBuffer name); + Result GetEnvironmentVariableLength(Out out_length, + InBuffer name); + Result BindHostConnectionEvent(OutCopyHandle out_event); + Result BindHostDisconnectionEvent(OutCopyHandle out_event); + Result BindHostConnectionEventForSystem(OutCopyHandle out_event); + Result BindHostDisconnectionEventForSystem(OutCopyHandle out_event); + Result GetBridgeIpAddress(Out out_ip_address); + Result GetBridgePort(Out out_port); + Result SetCradleAttached(bool is_attached); + Result GetBridgeSubnetMask(Out out_subnet_mask); + Result GetBridgeMacAddress(OutBuffer out_mac_address); + Result GetWorkingDirectoryPath(OutBuffer out_path); + Result GetWorkingDirectoryPathSize(Out out_size); + Result RunOnHostStart(); + Result RunOnHostResults(); + Result BeginUpdateBridge(); + Result ContinueUpdateBridge(); + Result EndUpdateBridge(); + Result GetBridgeType(Out out_bridge_type); + + KernelHelpers::ServiceContext service_context; + Kernel::KEvent* connection_event{}; + Kernel::KEvent* disconnection_event{}; + Kernel::KEvent* system_connection_event{}; + Kernel::KEvent* system_disconnection_event{}; +}; + +} // namespace Service::TMA \ No newline at end of file diff --git a/src/core/hle/service/tma/htc_tenv.cpp b/src/core/hle/service/tma/htc_tenv.cpp new file mode 100644 index 000000000..ed442213b --- /dev/null +++ b/src/core/hle/service/tma/htc_tenv.cpp @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/tma/htc_tenv.h" + +// This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent +#undef CreateEvent + +namespace Service::TMA { + +IService::IService(Core::System& system_) + : ServiceFramework{system_, "IService"}, service_context{system_, "htc:tenv:IService"} { + + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&IService::GetVariable>, "GetVariable"}, + {1, D<&IService::GetVariableLength>, "GetVariableLength"}, + {2, D<&IService::WaitUntilVariableAvailable>, "WaitUntilVariableAvailable"}, + }; + // clang-format on + + RegisterHandlers(functions); + + // Create event for variable availability notification + variable_available_event = service_context.CreateEvent("htc:tenv:variable_available"); +} + +IService::~IService() { + service_context.CloseEvent(variable_available_event); +} + +Result IService::GetVariable(OutBuffer out_value, + InBuffer name) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + // Clear output buffer as no variables are set + std::memset(out_value.data(), 0, out_value.size()); + + R_SUCCEED(); +} + +Result IService::GetVariableLength(Out out_length, + InBuffer name) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + // Return 0 length for all variables + *out_length = 0; + + R_SUCCEED(); +} + +Result IService::WaitUntilVariableAvailable(InBuffer name) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + + // Variables are never available, so this would block indefinitely + // For now, just return success + R_SUCCEED(); +} + +IServiceManager::IServiceManager(Core::System& system_) : ServiceFramework{system_, "htc:tenv"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&IServiceManager::GetServiceInterface>, "GetServiceInterface"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IServiceManager::~IServiceManager() = default; + +Result IServiceManager::GetServiceInterface(OutInterface out_service) { + LOG_DEBUG(Service_TMA, "called"); + + *out_service = std::make_shared(system); + + R_SUCCEED(); +} + +} // namespace Service::TMA \ No newline at end of file diff --git a/src/core/hle/service/tma/htc_tenv.h b/src/core/hle/service/tma/htc_tenv.h new file mode 100644 index 000000000..ecfcec246 --- /dev/null +++ b/src/core/hle/service/tma/htc_tenv.h @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" + +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + +namespace Service::TMA { + +class IService final : public ServiceFramework { +public: + explicit IService(Core::System& system_); + ~IService() override; + +private: + Result GetVariable(OutBuffer out_value, + InBuffer name); + Result GetVariableLength(Out out_length, + InBuffer name); + Result WaitUntilVariableAvailable(InBuffer name); + + KernelHelpers::ServiceContext service_context; + Kernel::KEvent* variable_available_event{}; +}; + +class IServiceManager final : public ServiceFramework { +public: + explicit IServiceManager(Core::System& system_); + ~IServiceManager() override; + +private: + Result GetServiceInterface(OutInterface out_service); +}; + +} // namespace Service::TMA \ No newline at end of file diff --git a/src/core/hle/service/tma/htcs.cpp b/src/core/hle/service/tma/htcs.cpp new file mode 100644 index 000000000..2ab11d77c --- /dev/null +++ b/src/core/hle/service/tma/htcs.cpp @@ -0,0 +1,200 @@ +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/tma/htcs.h" + +namespace Service::TMA { + +ISocket::ISocket(Core::System& system_) : ServiceFramework{system_, "ISocket"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&ISocket::Close>, "Close"}, + {1, D<&ISocket::Connect>, "Connect"}, + {2, D<&ISocket::Bind>, "Bind"}, + {3, D<&ISocket::Listen>, "Listen"}, + {4, D<&ISocket::Accept>, "Accept"}, + {5, D<&ISocket::Recv>, "Recv"}, + {6, D<&ISocket::Send>, "Send"}, + {7, D<&ISocket::Shutdown>, "Shutdown"}, + {8, D<&ISocket::Fcntl>, "Fcntl"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +ISocket::~ISocket() = default; + +Result ISocket::Close() { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + R_SUCCEED(); +} + +Result ISocket::Connect(InBuffer address) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + R_SUCCEED(); +} + +Result ISocket::Bind(InBuffer address) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + R_SUCCEED(); +} + +Result ISocket::Listen(s32 backlog) { + LOG_WARNING(Service_TMA, "(STUBBED) called, backlog={}", backlog); + R_SUCCEED(); +} + +Result ISocket::Accept(OutInterface out_socket) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + *out_socket = std::make_shared(system); + R_SUCCEED(); +} + +Result ISocket::Recv(Out out_size, OutBuffer out_buffer, s32 flags) { + LOG_WARNING(Service_TMA, "(STUBBED) called, flags={}", flags); + *out_size = 0; + R_SUCCEED(); +} + +Result ISocket::Send(Out out_size, InBuffer buffer, s32 flags) { + LOG_WARNING(Service_TMA, "(STUBBED) called, flags={}", flags); + *out_size = static_cast(buffer.size()); + R_SUCCEED(); +} + +Result ISocket::Shutdown(s32 how) { + LOG_WARNING(Service_TMA, "(STUBBED) called, how={}", how); + R_SUCCEED(); +} + +Result ISocket::Fcntl(Out out_result, s32 cmd, s32 arg) { + LOG_WARNING(Service_TMA, "(STUBBED) called, cmd={}, arg={}", cmd, arg); + *out_result = 0; + R_SUCCEED(); +} + +IHtcsManager::IHtcsManager(Core::System& system_) : ServiceFramework{system_, "htcs"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&IHtcsManager::Socket>, "Socket"}, + {1, D<&IHtcsManager::Close>, "Close"}, + {2, D<&IHtcsManager::Connect>, "Connect"}, + {3, D<&IHtcsManager::Bind>, "Bind"}, + {4, D<&IHtcsManager::Listen>, "Listen"}, + {5, D<&IHtcsManager::Accept>, "Accept"}, + {6, D<&IHtcsManager::Recv>, "Recv"}, + {7, D<&IHtcsManager::Send>, "Send"}, + {8, D<&IHtcsManager::Shutdown>, "Shutdown"}, + {9, D<&IHtcsManager::Fcntl>, "Fcntl"}, + {10, D<&IHtcsManager::GetPeerNameAny>, "GetPeerNameAny"}, + {11, D<&IHtcsManager::GetDefaultHostName>, "GetDefaultHostName"}, + {12, D<&IHtcsManager::CreateSocketOld>, "CreateSocketOld"}, + {13, D<&IHtcsManager::CreateSocket>, "CreateSocket"}, + {100, D<&IHtcsManager::RegisterProcessId>, "RegisterProcessId"}, + {101, D<&IHtcsManager::MonitorManager>, "MonitorManager"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IHtcsManager::~IHtcsManager() = default; + +Result IHtcsManager::Socket(Out out_socket, s32 domain, s32 type, s32 protocol) { + LOG_WARNING(Service_TMA, "(STUBBED) called, domain={}, type={}, protocol={}", domain, type, protocol); + *out_socket = 1; // Return dummy socket descriptor + R_SUCCEED(); +} + +Result IHtcsManager::Close(s32 socket) { + LOG_WARNING(Service_TMA, "(STUBBED) called, socket={}", socket); + R_SUCCEED(); +} + +Result IHtcsManager::Connect(Out out_result, s32 socket, InBuffer address) { + LOG_WARNING(Service_TMA, "(STUBBED) called, socket={}", socket); + *out_result = 0; + R_SUCCEED(); +} + +Result IHtcsManager::Bind(Out out_result, s32 socket, InBuffer address) { + LOG_WARNING(Service_TMA, "(STUBBED) called, socket={}", socket); + *out_result = 0; + R_SUCCEED(); +} + +Result IHtcsManager::Listen(Out out_result, s32 socket, s32 backlog) { + LOG_WARNING(Service_TMA, "(STUBBED) called, socket={}, backlog={}", socket, backlog); + *out_result = 0; + R_SUCCEED(); +} + +Result IHtcsManager::Accept(Out out_socket, s32 socket, OutBuffer out_address) { + LOG_WARNING(Service_TMA, "(STUBBED) called, socket={}", socket); + *out_socket = 2; // Return dummy accepted socket + R_SUCCEED(); +} + +Result IHtcsManager::Recv(Out out_size, s32 socket, OutBuffer out_buffer, s32 flags) { + LOG_WARNING(Service_TMA, "(STUBBED) called, socket={}, flags={}", socket, flags); + *out_size = 0; + R_SUCCEED(); +} + +Result IHtcsManager::Send(Out out_size, s32 socket, InBuffer buffer, s32 flags) { + LOG_WARNING(Service_TMA, "(STUBBED) called, socket={}, flags={}", socket, flags); + *out_size = static_cast(buffer.size()); + R_SUCCEED(); +} + +Result IHtcsManager::Shutdown(Out out_result, s32 socket, s32 how) { + LOG_WARNING(Service_TMA, "(STUBBED) called, socket={}, how={}", socket, how); + *out_result = 0; + R_SUCCEED(); +} + +Result IHtcsManager::Fcntl(Out out_result, s32 socket, s32 cmd, s32 arg) { + LOG_WARNING(Service_TMA, "(STUBBED) called, socket={}, cmd={}, arg={}", socket, cmd, arg); + *out_result = 0; + R_SUCCEED(); +} + +Result IHtcsManager::GetPeerNameAny(Out out_result, s32 socket, OutBuffer out_address) { + LOG_WARNING(Service_TMA, "(STUBBED) called, socket={}", socket); + *out_result = 0; + R_SUCCEED(); +} + +Result IHtcsManager::GetDefaultHostName(OutBuffer out_hostname) { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + if (out_hostname.size() > 0) { + out_hostname[0] = 0; // Empty hostname + } + R_SUCCEED(); +} + +Result IHtcsManager::CreateSocketOld(OutInterface out_socket, s32 domain, s32 type, s32 protocol) { + LOG_WARNING(Service_TMA, "(STUBBED) called, domain={}, type={}, protocol={}", domain, type, protocol); + *out_socket = std::make_shared(system); + R_SUCCEED(); +} + +Result IHtcsManager::CreateSocket(OutInterface out_socket, s32 domain, s32 type, s32 protocol) { + LOG_WARNING(Service_TMA, "(STUBBED) called, domain={}, type={}, protocol={}", domain, type, protocol); + *out_socket = std::make_shared(system); + R_SUCCEED(); +} + +Result IHtcsManager::RegisterProcessId(ClientProcessId process_id) { + LOG_WARNING(Service_TMA, "(STUBBED) called, process_id={}", process_id.pid); + R_SUCCEED(); +} + +Result IHtcsManager::MonitorManager() { + LOG_WARNING(Service_TMA, "(STUBBED) called"); + R_SUCCEED(); +} + +} // namespace Service::TMA \ No newline at end of file diff --git a/src/core/hle/service/tma/htcs.h b/src/core/hle/service/tma/htcs.h new file mode 100644 index 000000000..918b1713e --- /dev/null +++ b/src/core/hle/service/tma/htcs.h @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::TMA { + +class ISocket final : public ServiceFramework { +public: + explicit ISocket(Core::System& system_); + ~ISocket() override; + +private: + Result Close(); + Result Connect(InBuffer address); + Result Bind(InBuffer address); + Result Listen(s32 backlog); + Result Accept(OutInterface out_socket); + Result Recv(Out out_size, OutBuffer out_buffer, s32 flags); + Result Send(Out out_size, InBuffer buffer, s32 flags); + Result Shutdown(s32 how); + Result Fcntl(Out out_result, s32 cmd, s32 arg); +}; + +class IHtcsManager final : public ServiceFramework { +public: + explicit IHtcsManager(Core::System& system_); + ~IHtcsManager() override; + +private: + Result Socket(Out out_socket, s32 domain, s32 type, s32 protocol); + Result Close(s32 socket); + Result Connect(Out out_result, s32 socket, InBuffer address); + Result Bind(Out out_result, s32 socket, InBuffer address); + Result Listen(Out out_result, s32 socket, s32 backlog); + Result Accept(Out out_socket, s32 socket, OutBuffer out_address); + Result Recv(Out out_size, s32 socket, OutBuffer out_buffer, s32 flags); + Result Send(Out out_size, s32 socket, InBuffer buffer, s32 flags); + Result Shutdown(Out out_result, s32 socket, s32 how); + Result Fcntl(Out out_result, s32 socket, s32 cmd, s32 arg); + Result GetPeerNameAny(Out out_result, s32 socket, OutBuffer out_address); + Result GetDefaultHostName(OutBuffer out_hostname); + Result CreateSocketOld(OutInterface out_socket, s32 domain, s32 type, s32 protocol); + Result CreateSocket(OutInterface out_socket, s32 domain, s32 type, s32 protocol); + Result RegisterProcessId(ClientProcessId process_id); + Result MonitorManager(); +}; + +} // namespace Service::TMA \ No newline at end of file diff --git a/src/core/hle/service/tma/tma.cpp b/src/core/hle/service/tma/tma.cpp new file mode 100644 index 000000000..b5d55469d --- /dev/null +++ b/src/core/hle/service/tma/tma.cpp @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/server_manager.h" +#include "core/hle/service/tma/file_io.h" +#include "core/hle/service/tma/htc.h" +#include "core/hle/service/tma/htc_tenv.h" +#include "core/hle/service/tma/htcs.h" +#include "core/hle/service/tma/tma.h" + +namespace Service::TMA { + +void LoopProcess(Core::System& system) { + auto server_manager = std::make_unique(system); + + // Register HTC (Host Target Connection) service + server_manager->RegisterNamedService("htc", std::make_shared(system)); + + // Register HTCS (Host Target Connection Sockets) service + server_manager->RegisterNamedService("htcs", std::make_shared(system)); + + // Register HTC:TENV (Target Environment) service + server_manager->RegisterNamedService("htc:tenv", std::make_shared(system)); + + // Register file_io (Host File I/O) service + // Note: This service is not present on retail units + server_manager->RegisterNamedService("file_io", std::make_shared(system)); + + ServerManager::RunServer(std::move(server_manager)); +} + +} // namespace Service::TMA \ No newline at end of file diff --git a/src/core/hle/service/tma/tma.h b/src/core/hle/service/tma/tma.h new file mode 100644 index 000000000..0184ed946 --- /dev/null +++ b/src/core/hle/service/tma/tma.h @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +namespace Core { +class System; +} + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::TMA { + +/// Registers all TMA services with the specified service manager. +void LoopProcess(Core::System& system); + +} // namespace Service::TMA \ No newline at end of file