Merge branch 'tma-add-new-functions-services' into 'master'

core/hle: Implement TMA (Target Manager Agent) service

See merge request citron/rewrite!28
This commit is contained in:
Zephyron
2025-07-01 06:27:59 +00:00
14 changed files with 1041 additions and 0 deletions

View File

@@ -134,6 +134,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Service, SSL) \ SUB(Service, SSL) \
SUB(Service, TCAP) \ SUB(Service, TCAP) \
SUB(Service, Time) \ SUB(Service, Time) \
SUB(Service, TMA) \
SUB(Service, USB) \ SUB(Service, USB) \
SUB(Service, VI) \ SUB(Service, VI) \
SUB(Service, WLAN) \ SUB(Service, WLAN) \

View File

@@ -102,6 +102,7 @@ enum class Class : u8 {
Service_SSL, ///< The SSL service Service_SSL, ///< The SSL service
Service_TCAP, ///< The TCAP service. Service_TCAP, ///< The TCAP service.
Service_Time, ///< The time service Service_Time, ///< The time service
Service_TMA, ///< The TMA (Target Manager Agent) service
Service_USB, ///< The USB (Universal Serial Bus) service Service_USB, ///< The USB (Universal Serial Bus) service
Service_VI, ///< The VI (Video interface) service Service_VI, ///< The VI (Video interface) service
Service_WLAN, ///< The WLAN (Wireless local area network) service Service_WLAN, ///< The WLAN (Wireless local area network) service

View File

@@ -1102,6 +1102,16 @@ add_library(core STATIC
hle/service/ssl/ssl.h hle/service/ssl/ssl.h
hle/service/ssl/ssl_backend.h hle/service/ssl/ssl_backend.h
hle/service/ssl/ssl_types.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.cpp
hle/service/usb/usb.h hle/service/usb/usb.h
hle/service/vi/application_display_service.cpp hle/service/vi/application_display_service.cpp

View File

@@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/services.h" #include "core/hle/service/services.h"
@@ -59,6 +60,7 @@
#include "core/hle/service/sockets/sockets.h" #include "core/hle/service/sockets/sockets.h"
#include "core/hle/service/spl/spl_module.h" #include "core/hle/service/spl/spl_module.h"
#include "core/hle/service/ssl/ssl.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/usb/usb.h"
#include "core/hle/service/vi/vi.h" #include "core/hle/service/vi/vi.h"
@@ -127,6 +129,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
kernel.RunOnGuestCoreProcess("ro", [&] { RO::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("ro", [&] { RO::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("spl", [&] { SPL::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("spl", [&] { SPL::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("ssl", [&] { SSL::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("ssl", [&] { SSL::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("tma", [&] { TMA::LoopProcess(system); });
kernel.RunOnGuestCoreProcess("usb", [&] { USB::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("usb", [&] { USB::LoopProcess(system); });
// clang-format on // clang-format on
} }

View File

@@ -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<s64> out_bytes_read, s64 offset,
OutBuffer<BufferAttr_HipcMapAlias> 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<BufferAttr_HipcMapAlias> 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<s64>(buffer.size()) > file_size) {
file_size = offset + static_cast<s64>(buffer.size());
}
R_SUCCEED();
}
Result IFileAccessor::GetFileSize(Out<s64> 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<s32> 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<s64> out_entry_count) {
LOG_WARNING(Service_TMA, "(STUBBED) called");
// Return 0 entries
*out_entry_count = 0;
R_SUCCEED();
}
Result IDirectoryAccessor::ReadDirectory(Out<s64> out_entries_read, OutBuffer<BufferAttr_HipcMapAlias> 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<s32> 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<IFileAccessor> out_file_accessor,
InBuffer<BufferAttr_HipcMapAlias> path, u32 open_mode) {
LOG_WARNING(Service_TMA, "(STUBBED) called, open_mode={}", open_mode);
*out_file_accessor = std::make_shared<IFileAccessor>(system);
R_SUCCEED();
}
Result IFileManager::FileExists(Out<bool> out_exists, InBuffer<BufferAttr_HipcMapAlias> path) {
LOG_WARNING(Service_TMA, "(STUBBED) called");
// No files exist on host
*out_exists = false;
R_SUCCEED();
}
Result IFileManager::DeleteFile(InBuffer<BufferAttr_HipcMapAlias> path) {
LOG_WARNING(Service_TMA, "(STUBBED) called");
R_SUCCEED();
}
Result IFileManager::RenameFile(InBuffer<BufferAttr_HipcMapAlias> src_path,
InBuffer<BufferAttr_HipcMapAlias> dst_path) {
LOG_WARNING(Service_TMA, "(STUBBED) called");
R_SUCCEED();
}
Result IFileManager::GetIOType(Out<s32> out_entry_type, InBuffer<BufferAttr_HipcMapAlias> path) {
LOG_WARNING(Service_TMA, "(STUBBED) called");
// Return 0 (does not exist)
*out_entry_type = 0;
R_SUCCEED();
}
Result IFileManager::OpenDirectory(OutInterface<IDirectoryAccessor> out_directory_accessor,
InBuffer<BufferAttr_HipcMapAlias> path, u32 open_mode) {
LOG_WARNING(Service_TMA, "(STUBBED) called, open_mode={}", open_mode);
*out_directory_accessor = std::make_shared<IDirectoryAccessor>(system);
R_SUCCEED();
}
Result IFileManager::DirectoryExists(Out<bool> out_exists, InBuffer<BufferAttr_HipcMapAlias> path) {
LOG_WARNING(Service_TMA, "(STUBBED) called");
// No directories exist on host
*out_exists = false;
R_SUCCEED();
}
Result IFileManager::CreateDirectory(InBuffer<BufferAttr_HipcMapAlias> path) {
LOG_WARNING(Service_TMA, "(STUBBED) called");
R_SUCCEED();
}
Result IFileManager::DeleteDirectory(InBuffer<BufferAttr_HipcMapAlias> path, bool is_recursive) {
LOG_WARNING(Service_TMA, "(STUBBED) called, is_recursive={}", is_recursive);
R_SUCCEED();
}
Result IFileManager::RenameDirectory(InBuffer<BufferAttr_HipcMapAlias> src_path,
InBuffer<BufferAttr_HipcMapAlias> dst_path) {
LOG_WARNING(Service_TMA, "(STUBBED) called");
R_SUCCEED();
}
Result IFileManager::CreateFile(InBuffer<BufferAttr_HipcMapAlias> path, s64 size) {
LOG_WARNING(Service_TMA, "(STUBBED) called, size={}", size);
R_SUCCEED();
}
Result IFileManager::GetFileTimeStamp(Out<u64> out_created_time, Out<u64> out_accessed_time,
Out<u64> out_modified_time, InBuffer<BufferAttr_HipcMapAlias> 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<BufferAttr_HipcMapAlias> out_path,
InBuffer<BufferAttr_HipcMapAlias> 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

View File

@@ -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<IFileAccessor> {
public:
explicit IFileAccessor(Core::System& system_);
~IFileAccessor() override;
private:
Result ReadFile(u32 read_option, Out<s64> out_bytes_read, s64 offset,
OutBuffer<BufferAttr_HipcMapAlias> out_buffer);
Result WriteFile(u32 write_option, s64 offset, InBuffer<BufferAttr_HipcMapAlias> buffer);
Result GetFileSize(Out<s64> out_file_size);
Result SetFileSize(s64 file_size);
Result FlushFile();
Result SetPriorityForFile(s32 priority);
Result GetPriorityForFile(Out<s32> out_priority);
s64 file_size{};
s32 priority{};
};
class IDirectoryAccessor final : public ServiceFramework<IDirectoryAccessor> {
public:
explicit IDirectoryAccessor(Core::System& system_);
~IDirectoryAccessor() override;
private:
Result GetEntryCount(Out<s64> out_entry_count);
Result ReadDirectory(Out<s64> out_entries_read, OutBuffer<BufferAttr_HipcMapAlias> out_entry_buffer);
Result SetPriorityForDirectory(s32 priority);
Result GetPriorityForDirectory(Out<s32> out_priority);
s32 priority{};
};
class IFileManager final : public ServiceFramework<IFileManager> {
public:
explicit IFileManager(Core::System& system_);
~IFileManager() override;
private:
Result OpenFile(OutInterface<IFileAccessor> out_file_accessor,
InBuffer<BufferAttr_HipcMapAlias> path, u32 open_mode);
Result FileExists(Out<bool> out_exists, InBuffer<BufferAttr_HipcMapAlias> path);
Result DeleteFile(InBuffer<BufferAttr_HipcMapAlias> path);
Result RenameFile(InBuffer<BufferAttr_HipcMapAlias> src_path,
InBuffer<BufferAttr_HipcMapAlias> dst_path);
Result GetIOType(Out<s32> out_entry_type, InBuffer<BufferAttr_HipcMapAlias> path);
Result OpenDirectory(OutInterface<IDirectoryAccessor> out_directory_accessor,
InBuffer<BufferAttr_HipcMapAlias> path, u32 open_mode);
Result DirectoryExists(Out<bool> out_exists, InBuffer<BufferAttr_HipcMapAlias> path);
Result CreateDirectory(InBuffer<BufferAttr_HipcMapAlias> path);
Result DeleteDirectory(InBuffer<BufferAttr_HipcMapAlias> path, bool is_recursive);
Result RenameDirectory(InBuffer<BufferAttr_HipcMapAlias> src_path,
InBuffer<BufferAttr_HipcMapAlias> dst_path);
Result CreateFile(InBuffer<BufferAttr_HipcMapAlias> path, s64 size);
Result GetFileTimeStamp(Out<u64> out_created_time, Out<u64> out_accessed_time,
Out<u64> out_modified_time, InBuffer<BufferAttr_HipcMapAlias> path);
Result GetCaseSensitivePath(OutBuffer<BufferAttr_HipcMapAlias> out_path,
InBuffer<BufferAttr_HipcMapAlias> path);
};
} // namespace Service::TMA

View File

@@ -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<BufferAttr_HipcMapAlias> out_value,
InBuffer<BufferAttr_HipcMapAlias> 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<u32> out_length,
InBuffer<BufferAttr_HipcMapAlias> name) {
LOG_WARNING(Service_TMA, "(STUBBED) called");
// Return 0 length for all environment variables
*out_length = 0;
R_SUCCEED();
}
Result IHtcManager::BindHostConnectionEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_DEBUG(Service_TMA, "called");
*out_event = &connection_event->GetReadableEvent();
R_SUCCEED();
}
Result IHtcManager::BindHostDisconnectionEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_DEBUG(Service_TMA, "called");
*out_event = &disconnection_event->GetReadableEvent();
R_SUCCEED();
}
Result IHtcManager::BindHostConnectionEventForSystem(OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_DEBUG(Service_TMA, "called");
*out_event = &system_connection_event->GetReadableEvent();
R_SUCCEED();
}
Result IHtcManager::BindHostDisconnectionEventForSystem(OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_DEBUG(Service_TMA, "called");
*out_event = &system_disconnection_event->GetReadableEvent();
R_SUCCEED();
}
Result IHtcManager::GetBridgeIpAddress(Out<u32> 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<u16> 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<u32> 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<BufferAttr_HipcMapAlias> out_mac_address) {
LOG_WARNING(Service_TMA, "(STUBBED) called");
// Clear MAC address buffer
std::memset(out_mac_address.data(), 0, std::min<size_t>(out_mac_address.size(), 6));
R_SUCCEED();
}
Result IHtcManager::GetWorkingDirectoryPath(OutBuffer<BufferAttr_HipcMapAlias> 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<u32> 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<u32> out_bridge_type) {
LOG_WARNING(Service_TMA, "(STUBBED) called");
*out_bridge_type = 0; // Default bridge type
R_SUCCEED();
}
} // namespace Service::TMA

View File

@@ -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<IHtcManager> {
public:
explicit IHtcManager(Core::System& system_);
~IHtcManager() override;
private:
Result GetEnvironmentVariable(OutBuffer<BufferAttr_HipcMapAlias> out_value,
InBuffer<BufferAttr_HipcMapAlias> name);
Result GetEnvironmentVariableLength(Out<u32> out_length,
InBuffer<BufferAttr_HipcMapAlias> name);
Result BindHostConnectionEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result BindHostDisconnectionEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result BindHostConnectionEventForSystem(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result BindHostDisconnectionEventForSystem(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetBridgeIpAddress(Out<u32> out_ip_address);
Result GetBridgePort(Out<u16> out_port);
Result SetCradleAttached(bool is_attached);
Result GetBridgeSubnetMask(Out<u32> out_subnet_mask);
Result GetBridgeMacAddress(OutBuffer<BufferAttr_HipcMapAlias> out_mac_address);
Result GetWorkingDirectoryPath(OutBuffer<BufferAttr_HipcMapAlias> out_path);
Result GetWorkingDirectoryPathSize(Out<u32> out_size);
Result RunOnHostStart();
Result RunOnHostResults();
Result BeginUpdateBridge();
Result ContinueUpdateBridge();
Result EndUpdateBridge();
Result GetBridgeType(Out<u32> 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

View File

@@ -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<BufferAttr_HipcMapAlias> out_value,
InBuffer<BufferAttr_HipcMapAlias> 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<u32> out_length,
InBuffer<BufferAttr_HipcMapAlias> name) {
LOG_WARNING(Service_TMA, "(STUBBED) called");
// Return 0 length for all variables
*out_length = 0;
R_SUCCEED();
}
Result IService::WaitUntilVariableAvailable(InBuffer<BufferAttr_HipcMapAlias> 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<IService> out_service) {
LOG_DEBUG(Service_TMA, "called");
*out_service = std::make_shared<IService>(system);
R_SUCCEED();
}
} // namespace Service::TMA

View File

@@ -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<IService> {
public:
explicit IService(Core::System& system_);
~IService() override;
private:
Result GetVariable(OutBuffer<BufferAttr_HipcMapAlias> out_value,
InBuffer<BufferAttr_HipcMapAlias> name);
Result GetVariableLength(Out<u32> out_length,
InBuffer<BufferAttr_HipcMapAlias> name);
Result WaitUntilVariableAvailable(InBuffer<BufferAttr_HipcMapAlias> name);
KernelHelpers::ServiceContext service_context;
Kernel::KEvent* variable_available_event{};
};
class IServiceManager final : public ServiceFramework<IServiceManager> {
public:
explicit IServiceManager(Core::System& system_);
~IServiceManager() override;
private:
Result GetServiceInterface(OutInterface<IService> out_service);
};
} // namespace Service::TMA

View File

@@ -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<BufferAttr_HipcMapAlias> address) {
LOG_WARNING(Service_TMA, "(STUBBED) called");
R_SUCCEED();
}
Result ISocket::Bind(InBuffer<BufferAttr_HipcMapAlias> 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<ISocket> out_socket) {
LOG_WARNING(Service_TMA, "(STUBBED) called");
*out_socket = std::make_shared<ISocket>(system);
R_SUCCEED();
}
Result ISocket::Recv(Out<s32> out_size, OutBuffer<BufferAttr_HipcMapAlias> out_buffer, s32 flags) {
LOG_WARNING(Service_TMA, "(STUBBED) called, flags={}", flags);
*out_size = 0;
R_SUCCEED();
}
Result ISocket::Send(Out<s32> out_size, InBuffer<BufferAttr_HipcMapAlias> buffer, s32 flags) {
LOG_WARNING(Service_TMA, "(STUBBED) called, flags={}", flags);
*out_size = static_cast<s32>(buffer.size());
R_SUCCEED();
}
Result ISocket::Shutdown(s32 how) {
LOG_WARNING(Service_TMA, "(STUBBED) called, how={}", how);
R_SUCCEED();
}
Result ISocket::Fcntl(Out<s32> 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<s32> 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<s32> out_result, s32 socket, InBuffer<BufferAttr_HipcMapAlias> address) {
LOG_WARNING(Service_TMA, "(STUBBED) called, socket={}", socket);
*out_result = 0;
R_SUCCEED();
}
Result IHtcsManager::Bind(Out<s32> out_result, s32 socket, InBuffer<BufferAttr_HipcMapAlias> address) {
LOG_WARNING(Service_TMA, "(STUBBED) called, socket={}", socket);
*out_result = 0;
R_SUCCEED();
}
Result IHtcsManager::Listen(Out<s32> 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<s32> out_socket, s32 socket, OutBuffer<BufferAttr_HipcMapAlias> out_address) {
LOG_WARNING(Service_TMA, "(STUBBED) called, socket={}", socket);
*out_socket = 2; // Return dummy accepted socket
R_SUCCEED();
}
Result IHtcsManager::Recv(Out<s32> out_size, s32 socket, OutBuffer<BufferAttr_HipcMapAlias> out_buffer, s32 flags) {
LOG_WARNING(Service_TMA, "(STUBBED) called, socket={}, flags={}", socket, flags);
*out_size = 0;
R_SUCCEED();
}
Result IHtcsManager::Send(Out<s32> out_size, s32 socket, InBuffer<BufferAttr_HipcMapAlias> buffer, s32 flags) {
LOG_WARNING(Service_TMA, "(STUBBED) called, socket={}, flags={}", socket, flags);
*out_size = static_cast<s32>(buffer.size());
R_SUCCEED();
}
Result IHtcsManager::Shutdown(Out<s32> 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<s32> 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<s32> out_result, s32 socket, OutBuffer<BufferAttr_HipcMapAlias> out_address) {
LOG_WARNING(Service_TMA, "(STUBBED) called, socket={}", socket);
*out_result = 0;
R_SUCCEED();
}
Result IHtcsManager::GetDefaultHostName(OutBuffer<BufferAttr_HipcMapAlias> 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<ISocket> 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<ISocket>(system);
R_SUCCEED();
}
Result IHtcsManager::CreateSocket(OutInterface<ISocket> 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<ISocket>(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

View File

@@ -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<ISocket> {
public:
explicit ISocket(Core::System& system_);
~ISocket() override;
private:
Result Close();
Result Connect(InBuffer<BufferAttr_HipcMapAlias> address);
Result Bind(InBuffer<BufferAttr_HipcMapAlias> address);
Result Listen(s32 backlog);
Result Accept(OutInterface<ISocket> out_socket);
Result Recv(Out<s32> out_size, OutBuffer<BufferAttr_HipcMapAlias> out_buffer, s32 flags);
Result Send(Out<s32> out_size, InBuffer<BufferAttr_HipcMapAlias> buffer, s32 flags);
Result Shutdown(s32 how);
Result Fcntl(Out<s32> out_result, s32 cmd, s32 arg);
};
class IHtcsManager final : public ServiceFramework<IHtcsManager> {
public:
explicit IHtcsManager(Core::System& system_);
~IHtcsManager() override;
private:
Result Socket(Out<s32> out_socket, s32 domain, s32 type, s32 protocol);
Result Close(s32 socket);
Result Connect(Out<s32> out_result, s32 socket, InBuffer<BufferAttr_HipcMapAlias> address);
Result Bind(Out<s32> out_result, s32 socket, InBuffer<BufferAttr_HipcMapAlias> address);
Result Listen(Out<s32> out_result, s32 socket, s32 backlog);
Result Accept(Out<s32> out_socket, s32 socket, OutBuffer<BufferAttr_HipcMapAlias> out_address);
Result Recv(Out<s32> out_size, s32 socket, OutBuffer<BufferAttr_HipcMapAlias> out_buffer, s32 flags);
Result Send(Out<s32> out_size, s32 socket, InBuffer<BufferAttr_HipcMapAlias> buffer, s32 flags);
Result Shutdown(Out<s32> out_result, s32 socket, s32 how);
Result Fcntl(Out<s32> out_result, s32 socket, s32 cmd, s32 arg);
Result GetPeerNameAny(Out<s32> out_result, s32 socket, OutBuffer<BufferAttr_HipcMapAlias> out_address);
Result GetDefaultHostName(OutBuffer<BufferAttr_HipcMapAlias> out_hostname);
Result CreateSocketOld(OutInterface<ISocket> out_socket, s32 domain, s32 type, s32 protocol);
Result CreateSocket(OutInterface<ISocket> out_socket, s32 domain, s32 type, s32 protocol);
Result RegisterProcessId(ClientProcessId process_id);
Result MonitorManager();
};
} // namespace Service::TMA

View File

@@ -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<ServerManager>(system);
// Register HTC (Host Target Connection) service
server_manager->RegisterNamedService("htc", std::make_shared<IHtcManager>(system));
// Register HTCS (Host Target Connection Sockets) service
server_manager->RegisterNamedService("htcs", std::make_shared<IHtcsManager>(system));
// Register HTC:TENV (Target Environment) service
server_manager->RegisterNamedService("htc:tenv", std::make_shared<IServiceManager>(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<IFileManager>(system));
ServerManager::RunServer(std::move(server_manager));
}
} // namespace Service::TMA

View File

@@ -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