diff options
Diffstat (limited to 'src/core/hle/service')
122 files changed, 6346 insertions, 1123 deletions
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index 147f4e62e..52c3491d5 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp @@ -9,15 +9,113 @@ namespace Service { namespace Account { +using Uid = std::array<u64, 2>; +static constexpr Uid DEFAULT_USER_ID{0x10ull, 0x20ull}; + +class IProfile final : public ServiceFramework<IProfile> { +public: + IProfile() : ServiceFramework("IProfile") { + static const FunctionInfo functions[] = { + {1, &IProfile::GetBase, "GetBase"}, + }; + RegisterHandlers(functions); + } + +private: + void GetBase(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + ProfileBase profile_base{}; + IPC::ResponseBuilder rb{ctx, 16}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw(profile_base); + } +}; + +class IManagerForApplication final : public ServiceFramework<IManagerForApplication> { +public: + IManagerForApplication() : ServiceFramework("IManagerForApplication") { + static const FunctionInfo functions[] = { + {0, &IManagerForApplication::CheckAvailability, "CheckAvailability"}, + {1, &IManagerForApplication::GetAccountId, "GetAccountId"}, + }; + RegisterHandlers(functions); + } + +private: + void CheckAvailability(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(true); // TODO: Check when this is supposed to return true and when not + } + + void GetAccountId(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(0x12345678ABCDEF); + } +}; + +void ACC_U0::GetUserExistence(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(true); // TODO: Check when this is supposed to return true and when not +} + +void ACC_U0::ListAllUsers(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + constexpr std::array<u128, 10> user_ids{DEFAULT_USER_ID}; + ctx.WriteBuffer(user_ids.data(), user_ids.size()); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void ACC_U0::ListOpenUsers(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + constexpr std::array<u128, 10> user_ids{DEFAULT_USER_ID}; + ctx.WriteBuffer(user_ids.data(), user_ids.size()); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void ACC_U0::GetProfile(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IProfile>(); + LOG_DEBUG(Service_ACC, "called"); +} + void ACC_U0::InitializeApplicationInfo(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - IPC::RequestBuilder rb{ctx, 2}; + LOG_WARNING(Service_ACC, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void ACC_U0::GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IManagerForApplication>(); + LOG_DEBUG(Service_ACC, "called"); +} + +void ACC_U0::GetLastOpenedUser(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 6}; rb.Push(RESULT_SUCCESS); + rb.PushRaw(DEFAULT_USER_ID); } ACC_U0::ACC_U0() : ServiceFramework("acc:u0") { static const FunctionInfo functions[] = { + {1, &ACC_U0::GetUserExistence, "GetUserExistence"}, + {2, &ACC_U0::ListAllUsers, "ListAllUsers"}, + {3, &ACC_U0::ListOpenUsers, "ListOpenUsers"}, + {4, &ACC_U0::GetLastOpenedUser, "GetLastOpenedUser"}, + {5, &ACC_U0::GetProfile, "GetProfile"}, {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, + {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, }; RegisterHandlers(functions); } diff --git a/src/core/hle/service/acc/acc_u0.h b/src/core/hle/service/acc/acc_u0.h index ac243d5b8..222f37282 100644 --- a/src/core/hle/service/acc/acc_u0.h +++ b/src/core/hle/service/acc/acc_u0.h @@ -9,13 +9,31 @@ namespace Service { namespace Account { +// TODO: RE this structure +struct UserData { + INSERT_PADDING_BYTES(0x80); +}; +static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size"); + +// TODO: RE this structure +struct ProfileBase { + INSERT_PADDING_BYTES(0x38); +}; +static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase structure has incorrect size"); + class ACC_U0 final : public ServiceFramework<ACC_U0> { public: ACC_U0(); ~ACC_U0() = default; private: + void GetUserExistence(Kernel::HLERequestContext& ctx); + void ListAllUsers(Kernel::HLERequestContext& ctx); + void ListOpenUsers(Kernel::HLERequestContext& ctx); + void GetLastOpenedUser(Kernel::HLERequestContext& ctx); + void GetProfile(Kernel::HLERequestContext& ctx); void InitializeApplicationInfo(Kernel::HLERequestContext& ctx); + void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx); }; } // namespace Account diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index a761bea65..d9f003ed4 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -2,14 +2,489 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cinttypes> +#include "core/file_sys/filesystem.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/event.h" #include "core/hle/service/am/am.h" +#include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" +#include "core/hle/service/apm/apm.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/nvflinger/nvflinger.h" namespace Service { namespace AM { -void InstallInterfaces(SM::ServiceManager& service_manager) { - std::make_shared<AppletOE>()->InstallAsService(service_manager); +IWindowController::IWindowController() : ServiceFramework("IWindowController") { + static const FunctionInfo functions[] = { + {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, + {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, + }; + RegisterHandlers(functions); +} + +void IWindowController::GetAppletResourceUserId(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(0); +} + +void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +IAudioController::IAudioController() : ServiceFramework("IAudioController") { + static const FunctionInfo functions[] = { + {0, &IAudioController::SetExpectedMasterVolume, "SetExpectedMasterVolume"}, + {1, &IAudioController::GetMainAppletExpectedMasterVolume, + "GetMainAppletExpectedMasterVolume"}, + {2, &IAudioController::GetLibraryAppletExpectedMasterVolume, + "GetLibraryAppletExpectedMasterVolume"}, + {3, nullptr, "ChangeMainAppletMasterVolume"}, + {4, nullptr, "SetTransparentVolumeRate"}, + }; + RegisterHandlers(functions); +} + +void IAudioController::SetExpectedMasterVolume(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void IAudioController::GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(volume); +} + +void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(volume); +} + +IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") {} + +IDebugFunctions::IDebugFunctions() : ServiceFramework("IDebugFunctions") {} + +ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) + : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) { + static const FunctionInfo functions[] = { + {1, &ISelfController::LockExit, "LockExit"}, + {2, &ISelfController::UnlockExit, "UnlockExit"}, + {9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"}, + {10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"}, + {11, &ISelfController::SetOperationModeChangedNotification, + "SetOperationModeChangedNotification"}, + {12, &ISelfController::SetPerformanceModeChangedNotification, + "SetPerformanceModeChangedNotification"}, + {13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"}, + {14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"}, + {16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"}, + {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"}, + }; + RegisterHandlers(functions); + + launchable_event = + Kernel::Event::Create(Kernel::ResetType::OneShot, "ISelfController:LaunchableEvent"); +} + +void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { + // Takes 3 input u8s with each field located immediately after the previous u8, these are + // bool flags. No output. + + IPC::RequestParser rp{ctx}; + + struct FocusHandlingModeParams { + u8 unknown0; + u8 unknown1; + u8 unknown2; + }; + auto flags = rp.PopRaw<FocusHandlingModeParams>(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void ISelfController::SetRestartMessageEnabled(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void ISelfController::SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + bool flag = rp.Pop<bool>(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AM, "(STUBBED) called flag=%u", static_cast<u32>(flag)); +} + +void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + bool flag = rp.Pop<bool>(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AM, "(STUBBED) called flag=%u", static_cast<u32>(flag)); +} + +void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) { + // Takes 3 input u8s with each field located immediately after the previous u8, these are + // bool flags. No output. + IPC::RequestParser rp{ctx}; + + bool enabled = rp.Pop<bool>(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AM, "(STUBBED) called enabled=%u", static_cast<u32>(enabled)); +} + +void ISelfController::LockExit(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) { + launchable_event->Signal(); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(launchable_event); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) { + // TODO(Subv): Find out how AM determines the display to use, for now just create the layer + // in the Default display. + u64 display_id = nvflinger->OpenDisplay("Default"); + u64 layer_id = nvflinger->CreateLayer(display_id); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(layer_id); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") { + static const FunctionInfo functions[] = { + {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"}, + {1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"}, + {5, &ICommonStateGetter::GetOperationMode, "GetOperationMode"}, + {6, &ICommonStateGetter::GetPerformanceMode, "GetPerformanceMode"}, + {9, &ICommonStateGetter::GetCurrentFocusState, "GetCurrentFocusState"}, + }; + RegisterHandlers(functions); + + event = Kernel::Event::Create(Kernel::ResetType::OneShot, "ICommonStateGetter:Event"); +} + +void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) { + event->Signal(); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(event); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(15); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast<u8>(FocusState::InFocus)); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast<u8>(OperationMode::Handheld)); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast<u32>(APM::PerformanceMode::Handheld)); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { +public: + explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") { + static const FunctionInfo functions[] = { + {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, + {1, nullptr, "IsCompleted"}, + {10, nullptr, "Start"}, + {20, nullptr, "RequestExit"}, + {25, nullptr, "Terminate"}, + {30, nullptr, "GetResult"}, + {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"}, + {100, nullptr, "PushInData"}, + {101, nullptr, "PopOutData"}, + {102, nullptr, "PushExtraStorage"}, + {103, nullptr, "PushInteractiveInData"}, + {104, nullptr, "PopInteractiveOutData"}, + {105, nullptr, "GetPopOutDataEvent"}, + {106, nullptr, "GetPopInteractiveOutDataEvent"}, + {120, nullptr, "NeedsToExitProcess"}, + {120, nullptr, "GetLibraryAppletInfo"}, + {150, nullptr, "RequestForAppletToGetForeground"}, + {160, nullptr, "GetIndirectLayerConsumerHandle"}, + }; + RegisterHandlers(functions); + + state_changed_event = Kernel::Event::Create(Kernel::ResetType::OneShot, + "ILibraryAppletAccessor:StateChangedEvent"); + } + +private: + void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) { + state_changed_event->Signal(); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(state_changed_event); + + LOG_WARNING(Service_AM, "(STUBBED) called"); + } + + Kernel::SharedPtr<Kernel::Event> state_changed_event; +}; + +ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") { + static const FunctionInfo functions[] = { + {0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"}, + {1, nullptr, "TerminateAllLibraryApplets"}, + {2, nullptr, "AreAnyLibraryAppletsLeft"}, + {10, nullptr, "CreateStorage"}, + {11, nullptr, "CreateTransferMemoryStorage"}, + {12, nullptr, "CreateHandleStorage"}, + }; + RegisterHandlers(functions); +} + +void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<AM::ILibraryAppletAccessor>(); + + LOG_DEBUG(Service_AM, "called"); +} + +class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { +public: + explicit IStorageAccessor(std::vector<u8> buffer) + : ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) { + static const FunctionInfo functions[] = { + {0, &IStorageAccessor::GetSize, "GetSize"}, + {11, &IStorageAccessor::Read, "Read"}, + }; + RegisterHandlers(functions); + } + +private: + std::vector<u8> buffer; + + void GetSize(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast<u64>(buffer.size())); + + LOG_DEBUG(Service_AM, "called"); + } + + void Read(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + u64 offset = rp.Pop<u64>(); + + const size_t size{ctx.GetWriteBufferSize()}; + + ASSERT(offset + size <= buffer.size()); + + ctx.WriteBuffer(buffer.data() + offset, size); + + IPC::ResponseBuilder rb{ctx, 2}; + + rb.Push(RESULT_SUCCESS); + + LOG_DEBUG(Service_AM, "called"); + } +}; + +class IStorage final : public ServiceFramework<IStorage> { +public: + explicit IStorage(std::vector<u8> buffer) + : ServiceFramework("IStorage"), buffer(std::move(buffer)) { + static const FunctionInfo functions[] = { + {0, &IStorage::Open, "Open"}, + }; + RegisterHandlers(functions); + } + +private: + std::vector<u8> buffer; + + void Open(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<AM::IStorageAccessor>(buffer); + + LOG_DEBUG(Service_AM, "called"); + } +}; + +IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") { + static const FunctionInfo functions[] = { + {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"}, + {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"}, + {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"}, + {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"}, + {66, &IApplicationFunctions::InitializeGamePlayRecording, "InitializeGamePlayRecording"}, + {67, &IApplicationFunctions::SetGamePlayRecordingState, "SetGamePlayRecordingState"}, + {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, + }; + RegisterHandlers(functions); +} + +void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { + constexpr u8 data[0x88] = { + 0xca, 0x97, 0x94, 0xc7, // Magic + 1, 0, 0, 0, // IsAccountSelected (bool) + 1, 0, 0, 0, // User Id (word 0) + 0, 0, 0, 0, // User Id (word 1) + 0, 0, 0, 0, // User Id (word 2) + 0, 0, 0, 0 // User Id (word 3) + }; + + std::vector<u8> buffer(data, data + sizeof(data)); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<AM::IStorage>(buffer); + + LOG_DEBUG(Service_AM, "called"); +} + +void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + u128 uid = rp.PopRaw<u128>(); + + LOG_WARNING(Service, "(STUBBED) called uid = %016" PRIX64 "%016" PRIX64, uid[1], uid[0]); + + IPC::ResponseBuilder rb{ctx, 4}; + + FileSys::Path unused; + auto savedata = FileSystem::OpenFileSystem(FileSystem::Type::SaveData, unused); + if (savedata.Failed()) { + // Create the save data and return an error indicating that the operation was performed. + FileSystem::FormatFileSystem(FileSystem::Type::SaveData); + // TODO(Subv): Find out the correct error code for this. + rb.Push(ResultCode(ErrorModule::FS, 40)); + } else { + rb.Push(RESULT_SUCCESS); + } + + rb.Push<u64>(0); +} + +void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) { + // Takes an input u32 Result, no output. + // For example, in some cases official apps use this with error 0x2A2 then uses svcBreak. + + IPC::RequestParser rp{ctx}; + u32 result = rp.Pop<u32>(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AM, "(STUBBED) called, result=0x%08X", result); +} + +void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(SystemLanguage::English); + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void IApplicationFunctions::InitializeGamePlayRecording(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void IApplicationFunctions::SetGamePlayRecordingState(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void IApplicationFunctions::NotifyRunning(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u8>(0); // Unknown, seems to be ignored by official processes + + LOG_WARNING(Service_AM, "(STUBBED) called"); +} + +void InstallInterfaces(SM::ServiceManager& service_manager, + std::shared_ptr<NVFlinger::NVFlinger> nvflinger) { + std::make_shared<AppletAE>(nvflinger)->InstallAsService(service_manager); + std::make_shared<AppletOE>(nvflinger)->InstallAsService(service_manager); } } // namespace AM diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 63d86cd41..27dbd8c95 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -4,13 +4,126 @@ #pragma once +#include <memory> #include "core/hle/service/service.h" +namespace Kernel { +class Event; +} + namespace Service { +namespace NVFlinger { +class NVFlinger; +} + namespace AM { +// TODO: Add more languages +enum SystemLanguage { + Japanese = 0, + English = 1, +}; + +class IWindowController final : public ServiceFramework<IWindowController> { +public: + IWindowController(); + +private: + void GetAppletResourceUserId(Kernel::HLERequestContext& ctx); + void AcquireForegroundRights(Kernel::HLERequestContext& ctx); +}; + +class IAudioController final : public ServiceFramework<IAudioController> { +public: + IAudioController(); + +private: + void SetExpectedMasterVolume(Kernel::HLERequestContext& ctx); + void GetMainAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx); + void GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestContext& ctx); + + u32 volume{100}; +}; + +class IDisplayController final : public ServiceFramework<IDisplayController> { +public: + IDisplayController(); +}; + +class IDebugFunctions final : public ServiceFramework<IDebugFunctions> { +public: + IDebugFunctions(); +}; + +class ISelfController final : public ServiceFramework<ISelfController> { +public: + ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); + +private: + void SetFocusHandlingMode(Kernel::HLERequestContext& ctx); + void SetRestartMessageEnabled(Kernel::HLERequestContext& ctx); + void SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx); + void SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx); + void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx); + void LockExit(Kernel::HLERequestContext& ctx); + void UnlockExit(Kernel::HLERequestContext& ctx); + void GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx); + void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx); + void SetScreenShotPermission(Kernel::HLERequestContext& ctx); + + std::shared_ptr<NVFlinger::NVFlinger> nvflinger; + Kernel::SharedPtr<Kernel::Event> launchable_event; +}; + +class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { +public: + ICommonStateGetter(); + +private: + enum class FocusState : u8 { + InFocus = 1, + NotInFocus = 2, + }; + + enum class OperationMode : u8 { + Handheld = 0, + Docked = 1, + }; + + void GetEventHandle(Kernel::HLERequestContext& ctx); + void ReceiveMessage(Kernel::HLERequestContext& ctx); + void GetCurrentFocusState(Kernel::HLERequestContext& ctx); + void GetOperationMode(Kernel::HLERequestContext& ctx); + void GetPerformanceMode(Kernel::HLERequestContext& ctx); + + Kernel::SharedPtr<Kernel::Event> event; +}; + +class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> { +public: + ILibraryAppletCreator(); + +private: + void CreateLibraryApplet(Kernel::HLERequestContext& ctx); +}; + +class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> { +public: + IApplicationFunctions(); + +private: + void PopLaunchParameter(Kernel::HLERequestContext& ctx); + void EnsureSaveData(Kernel::HLERequestContext& ctx); + void SetTerminateResult(Kernel::HLERequestContext& ctx); + void GetDesiredLanguage(Kernel::HLERequestContext& ctx); + void InitializeGamePlayRecording(Kernel::HLERequestContext& ctx); + void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx); + void NotifyRunning(Kernel::HLERequestContext& ctx); +}; + /// Registers all AM services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(SM::ServiceManager& service_manager, + std::shared_ptr<NVFlinger::NVFlinger> nvflinger); } // namespace AM } // namespace Service diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp new file mode 100644 index 000000000..0e51caa70 --- /dev/null +++ b/src/core/hle/service/am/applet_ae.cpp @@ -0,0 +1,112 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applet_ae.h" +#include "core/hle/service/nvflinger/nvflinger.h" + +namespace Service { +namespace AM { + +class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> { +public: + ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) + : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)) { + static const FunctionInfo functions[] = { + {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, + {1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"}, + {2, &ILibraryAppletProxy::GetWindowController, "GetWindowController"}, + {3, &ILibraryAppletProxy::GetAudioController, "GetAudioController"}, + {4, &ILibraryAppletProxy::GetDisplayController, "GetDisplayController"}, + {11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"}, + {20, &ILibraryAppletProxy::GetApplicationFunctions, "GetApplicationFunctions"}, + {1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"}, + }; + RegisterHandlers(functions); + } + +private: + void GetCommonStateGetter(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ICommonStateGetter>(); + LOG_DEBUG(Service_AM, "called"); + } + + void GetSelfController(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ISelfController>(nvflinger); + LOG_DEBUG(Service_AM, "called"); + } + + void GetWindowController(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IWindowController>(); + LOG_DEBUG(Service_AM, "called"); + } + + void GetAudioController(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IAudioController>(); + LOG_DEBUG(Service_AM, "called"); + } + + void GetDisplayController(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IDisplayController>(); + LOG_DEBUG(Service_AM, "called"); + } + + void GetDebugFunctions(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IDebugFunctions>(); + LOG_DEBUG(Service_AM, "called"); + } + + void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ILibraryAppletCreator>(); + LOG_DEBUG(Service_AM, "called"); + } + + void GetApplicationFunctions(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IApplicationFunctions>(); + LOG_DEBUG(Service_AM, "called"); + } + + std::shared_ptr<NVFlinger::NVFlinger> nvflinger; +}; + +void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger); + LOG_DEBUG(Service_AM, "called"); +} + +AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) + : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)) { + static const FunctionInfo functions[] = { + {100, nullptr, "OpenSystemAppletProxy"}, + {200, &AppletAE::OpenLibraryAppletProxyOld, "OpenLibraryAppletProxyOld"}, + {201, nullptr, "OpenLibraryAppletProxy"}, + {300, nullptr, "OpenOverlayAppletProxy"}, + {350, nullptr, "OpenSystemApplicationProxy"}, + {400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"}, + }; + RegisterHandlers(functions); +} + +} // namespace AM +} // namespace Service diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h new file mode 100644 index 000000000..38fc428fb --- /dev/null +++ b/src/core/hle/service/am/applet_ae.h @@ -0,0 +1,30 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/service.h" + +namespace Service { +namespace NVFlinger { +class NVFlinger; +} + +namespace AM { + +class AppletAE final : public ServiceFramework<AppletAE> { +public: + AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); + ~AppletAE() = default; + +private: + void OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx); + + std::shared_ptr<NVFlinger::NVFlinger> nvflinger; +}; + +} // namespace AM +} // namespace Service diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp index b360e7e5f..bdcebe689 100644 --- a/src/core/hle/service/am/applet_oe.cpp +++ b/src/core/hle/service/am/applet_oe.cpp @@ -4,260 +4,17 @@ #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/event.h" +#include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_oe.h" -#include "core/hle/service/apm/apm.h" +#include "core/hle/service/nvflinger/nvflinger.h" namespace Service { namespace AM { -class IWindowController final : public ServiceFramework<IWindowController> { -public: - IWindowController() : ServiceFramework("IWindowController") { - static const FunctionInfo functions[] = { - {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, - {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, - }; - RegisterHandlers(functions); - } - -private: - void GetAppletResourceUserId(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - IPC::RequestBuilder rb{ctx, 4}; - rb.Push(RESULT_SUCCESS); - rb.Push<u64>(0); - } - - void AcquireForegroundRights(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - IPC::RequestBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - } -}; - -class IAudioController final : public ServiceFramework<IAudioController> { -public: - IAudioController() : ServiceFramework("IAudioController") {} -}; - -class IDisplayController final : public ServiceFramework<IDisplayController> { -public: - IDisplayController() : ServiceFramework("IDisplayController") {} -}; - -class IDebugFunctions final : public ServiceFramework<IDebugFunctions> { -public: - IDebugFunctions() : ServiceFramework("IDebugFunctions") {} -}; - -class ISelfController final : public ServiceFramework<ISelfController> { -public: - ISelfController() : ServiceFramework("ISelfController") { - static const FunctionInfo functions[] = { - {11, &ISelfController::SetOperationModeChangedNotification, - "SetOperationModeChangedNotification"}, - {12, &ISelfController::SetPerformanceModeChangedNotification, - "SetPerformanceModeChangedNotification"}, - {13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"}, - {14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"}, - {16, &ISelfController::SetOutOfFocusSuspendingEnabled, - "SetOutOfFocusSuspendingEnabled"}, - }; - RegisterHandlers(functions); - } - -private: - void SetFocusHandlingMode(Kernel::HLERequestContext& ctx) { - // Takes 3 input u8s with each field located immediately after the previous u8, these are - // bool flags. No output. - - IPC::RequestParser rp{ctx}; - - struct FocusHandlingModeParams { - u8 unknown0; - u8 unknown1; - u8 unknown2; - }; - auto flags = rp.PopRaw<FocusHandlingModeParams>(); - - IPC::RequestBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service, "(STUBBED) called"); - } - - void SetRestartMessageEnabled(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service, "(STUBBED) called"); - } - - void SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - bool flag = rp.Pop<bool>(); - - IPC::RequestBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service, "(STUBBED) called flag=%u", static_cast<u32>(flag)); - } - - void SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - bool flag = rp.Pop<bool>(); - - IPC::RequestBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service, "(STUBBED) called flag=%u", static_cast<u32>(flag)); - } - - void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) { - // Takes 3 input u8s with each field located immediately after the previous u8, these are - // bool flags. No output. - IPC::RequestParser rp{ctx}; - - bool enabled = rp.Pop<bool>(); - - IPC::RequestBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service, "(STUBBED) called enabled=%u", static_cast<u32>(enabled)); - } -}; - -class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { -public: - ICommonStateGetter() : ServiceFramework("ICommonStateGetter") { - static const FunctionInfo functions[] = { - {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"}, - {1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"}, - {5, &ICommonStateGetter::GetOperationMode, "GetOperationMode"}, - {6, &ICommonStateGetter::GetPerformanceMode, "GetPerformanceMode"}, - {9, &ICommonStateGetter::GetCurrentFocusState, "GetCurrentFocusState"}, - }; - RegisterHandlers(functions); - - event = Kernel::Event::Create(Kernel::ResetType::OneShot, "ICommonStateGetter:Event"); - } - -private: - enum class FocusState : u8 { - InFocus = 1, - NotInFocus = 2, - }; - - enum class OperationMode : u8 { - Handheld = 0, - Docked = 1, - }; - - void GetEventHandle(Kernel::HLERequestContext& ctx) { - event->Signal(); - - IPC::RequestBuilder rb{ctx, 2, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(event); - - LOG_WARNING(Service, "(STUBBED) called"); - } - - void ReceiveMessage(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 3}; - rb.Push(RESULT_SUCCESS); - rb.Push<u32>(15); - - LOG_WARNING(Service, "(STUBBED) called"); - } - - void GetCurrentFocusState(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 3}; - rb.Push(RESULT_SUCCESS); - rb.Push(static_cast<u8>(FocusState::InFocus)); - - LOG_WARNING(Service, "(STUBBED) called"); - } - - void GetOperationMode(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 3}; - rb.Push(RESULT_SUCCESS); - rb.Push(static_cast<u8>(OperationMode::Handheld)); - - LOG_WARNING(Service, "(STUBBED) called"); - } - - void GetPerformanceMode(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 3}; - rb.Push(RESULT_SUCCESS); - rb.Push(static_cast<u32>(APM::PerformanceMode::Handheld)); - - LOG_WARNING(Service, "(STUBBED) called"); - } - - Kernel::SharedPtr<Kernel::Event> event; -}; - -class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> { -public: - IApplicationFunctions() : ServiceFramework("IApplicationFunctions") { - static const FunctionInfo functions[] = { - {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"}, - {66, &IApplicationFunctions::InitializeGamePlayRecording, - "InitializeGamePlayRecording"}, - {67, &IApplicationFunctions::SetGamePlayRecordingState, "SetGamePlayRecordingState"}, - {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, - }; - RegisterHandlers(functions); - } - -private: - void SetTerminateResult(Kernel::HLERequestContext& ctx) { - // Takes an input u32 Result, no output. - // For example, in some cases official apps use this with error 0x2A2 then uses svcBreak. - - IPC::RequestParser rp{ctx}; - u32 result = rp.Pop<u32>(); - - IPC::RequestBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service, "(STUBBED) called, result=0x%08X", result); - } - - void InitializeGamePlayRecording(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service, "(STUBBED) called"); - } - - void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service, "(STUBBED) called"); - } - - void NotifyRunning(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 3}; - rb.Push(RESULT_SUCCESS); - rb.Push<u8>(0); // Unknown, seems to be ignored by official processes - - LOG_WARNING(Service, "(STUBBED) called"); - } -}; - -class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> { -public: - ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") {} -}; - class IApplicationProxy final : public ServiceFramework<IApplicationProxy> { public: - IApplicationProxy() : ServiceFramework("IApplicationProxy") { + IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) + : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) { static const FunctionInfo functions[] = { {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"}, {1, &IApplicationProxy::GetSelfController, "GetSelfController"}, @@ -273,70 +30,73 @@ public: private: void GetAudioController(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IAudioController>(); - LOG_DEBUG(Service, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetDisplayController(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IDisplayController>(); - LOG_DEBUG(Service, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetDebugFunctions(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IDebugFunctions>(); - LOG_DEBUG(Service, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetWindowController(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IWindowController>(); - LOG_DEBUG(Service, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetSelfController(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISelfController>(); - LOG_DEBUG(Service, "called"); + rb.PushIpcInterface<ISelfController>(nvflinger); + LOG_DEBUG(Service_AM, "called"); } void GetCommonStateGetter(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ICommonStateGetter>(); - LOG_DEBUG(Service, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ILibraryAppletCreator>(); - LOG_DEBUG(Service, "called"); + LOG_DEBUG(Service_AM, "called"); } void GetApplicationFunctions(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IApplicationFunctions>(); - LOG_DEBUG(Service, "called"); + LOG_DEBUG(Service_AM, "called"); } + + std::shared_ptr<NVFlinger::NVFlinger> nvflinger; }; void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IApplicationProxy>(); - LOG_DEBUG(Service, "called"); + rb.PushIpcInterface<IApplicationProxy>(nvflinger); + LOG_DEBUG(Service_AM, "called"); } -AppletOE::AppletOE() : ServiceFramework("appletOE") { +AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) + : ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)) { static const FunctionInfo functions[] = { {0x00000000, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"}, }; diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h index beb75bf2a..d2ab44c67 100644 --- a/src/core/hle/service/am/applet_oe.h +++ b/src/core/hle/service/am/applet_oe.h @@ -4,19 +4,26 @@ #pragma once +#include <memory> #include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/service.h" namespace Service { +namespace NVFlinger { +class NVFlinger; +} + namespace AM { class AppletOE final : public ServiceFramework<AppletOE> { public: - AppletOE(); + AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); ~AppletOE() = default; private: void OpenApplicationProxy(Kernel::HLERequestContext& ctx); + + std::shared_ptr<NVFlinger::NVFlinger> nvflinger; }; } // namespace AM diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 260683201..8b55d2fcb 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -2,16 +2,44 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/service/aoc/aoc_u.h" namespace Service { namespace AOC { +AOC_U::AOC_U() : ServiceFramework("aoc:u") { + static const FunctionInfo functions[] = { + {0, nullptr, "CountAddOnContentByApplicationId"}, + {1, nullptr, "ListAddOnContentByApplicationId"}, + {2, &AOC_U::CountAddOnContent, "CountAddOnContent"}, + {3, &AOC_U::ListAddOnContent, "ListAddOnContent"}, + {4, nullptr, "GetAddOnContentBaseIdByApplicationId"}, + {5, nullptr, "GetAddOnContentBaseId"}, + {6, nullptr, "PrepareAddOnContentByApplicationId"}, + {7, nullptr, "PrepareAddOnContent"}, + }; + RegisterHandlers(functions); +} + +void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(0); + LOG_WARNING(Service_AOC, "(STUBBED) called"); +} + +void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(0); + LOG_WARNING(Service_AOC, "(STUBBED) called"); +} + void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<AOC_U>()->InstallAsService(service_manager); } -AOC_U::AOC_U() : ServiceFramework("aoc:u") {} - } // namespace AOC } // namespace Service diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h index 0cbbf1e5d..6e0ba15a5 100644 --- a/src/core/hle/service/aoc/aoc_u.h +++ b/src/core/hle/service/aoc/aoc_u.h @@ -13,6 +13,10 @@ class AOC_U final : public ServiceFramework<AOC_U> { public: AOC_U(); ~AOC_U() = default; + +private: + void CountAddOnContent(Kernel::HLERequestContext& ctx); + void ListAddOnContent(Kernel::HLERequestContext& ctx); }; /// Registers all AOC services with the specified service manager. diff --git a/src/core/hle/service/apm/apm.cpp b/src/core/hle/service/apm/apm.cpp index 66d94ff52..c4b09b435 100644 --- a/src/core/hle/service/apm/apm.cpp +++ b/src/core/hle/service/apm/apm.cpp @@ -5,61 +5,15 @@ #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/apm/apm.h" +#include "core/hle/service/apm/interface.h" namespace Service { namespace APM { void InstallInterfaces(SM::ServiceManager& service_manager) { - std::make_shared<APM>()->InstallAsService(service_manager); -} - -class ISession final : public ServiceFramework<ISession> { -public: - ISession() : ServiceFramework("ISession") { - static const FunctionInfo functions[] = { - {0, &ISession::SetPerformanceConfiguration, "SetPerformanceConfiguration"}, - {1, &ISession::GetPerformanceConfiguration, "GetPerformanceConfiguration"}, - }; - RegisterHandlers(functions); - } - -private: - void SetPerformanceConfiguration(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - auto mode = static_cast<PerformanceMode>(rp.Pop<u32>()); - u32 config = rp.Pop<u32>(); - - IPC::RequestBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); - - LOG_WARNING(Service, "(STUBBED) called mode=%u config=%u", static_cast<u32>(mode), config); - } - - void GetPerformanceConfiguration(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - auto mode = static_cast<PerformanceMode>(rp.Pop<u32>()); - - IPC::RequestBuilder rb{ctx, 3}; - rb.Push(RESULT_SUCCESS); - rb.Push<u32>(0); // Performance configuration - - LOG_WARNING(Service, "(STUBBED) called mode=%u", static_cast<u32>(mode)); - } -}; - -APM::APM() : ServiceFramework("apm") { - static const FunctionInfo functions[] = { - {0x00000000, &APM::OpenSession, "OpenSession"}, {0x00000001, nullptr, "GetPerformanceMode"}, - }; - RegisterHandlers(functions); -} - -void APM::OpenSession(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISession>(); + auto module_ = std::make_shared<Module>(); + std::make_shared<APM>(module_, "apm")->InstallAsService(service_manager); + std::make_shared<APM>(module_, "apm:p")->InstallAsService(service_manager); } } // namespace APM diff --git a/src/core/hle/service/apm/apm.h b/src/core/hle/service/apm/apm.h index 90a1afbbc..070ab21f8 100644 --- a/src/core/hle/service/apm/apm.h +++ b/src/core/hle/service/apm/apm.h @@ -14,13 +14,10 @@ enum class PerformanceMode : u8 { Docked = 1, }; -class APM final : public ServiceFramework<APM> { +class Module final { public: - APM(); - ~APM() = default; - -private: - void OpenSession(Kernel::HLERequestContext& ctx); + Module() = default; + ~Module() = default; }; /// Registers all AM services with the specified service manager. diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp new file mode 100644 index 000000000..0179351ba --- /dev/null +++ b/src/core/hle/service/apm/interface.cpp @@ -0,0 +1,66 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/apm/apm.h" +#include "core/hle/service/apm/interface.h" + +namespace Service { +namespace APM { + +class ISession final : public ServiceFramework<ISession> { +public: + ISession() : ServiceFramework("ISession") { + static const FunctionInfo functions[] = { + {0, &ISession::SetPerformanceConfiguration, "SetPerformanceConfiguration"}, + {1, &ISession::GetPerformanceConfiguration, "GetPerformanceConfiguration"}, + }; + RegisterHandlers(functions); + } + +private: + void SetPerformanceConfiguration(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto mode = static_cast<PerformanceMode>(rp.Pop<u32>()); + u32 config = rp.Pop<u32>(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_APM, "(STUBBED) called mode=%u config=%u", static_cast<u32>(mode), + config); + } + + void GetPerformanceConfiguration(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto mode = static_cast<PerformanceMode>(rp.Pop<u32>()); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); // Performance configuration + + LOG_WARNING(Service_APM, "(STUBBED) called mode=%u", static_cast<u32>(mode)); + } +}; + +APM::APM(std::shared_ptr<Module> apm, const char* name) + : ServiceFramework(name), apm(std::move(apm)) { + static const FunctionInfo functions[] = { + {0, &APM::OpenSession, "OpenSession"}, + {1, nullptr, "GetPerformanceMode"}, + }; + RegisterHandlers(functions); +} + +void APM::OpenSession(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ISession>(); +} + +} // namespace APM +} // namespace Service diff --git a/src/core/hle/service/apm/interface.h b/src/core/hle/service/apm/interface.h new file mode 100644 index 000000000..7d53721de --- /dev/null +++ b/src/core/hle/service/apm/interface.h @@ -0,0 +1,27 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace APM { + +class APM final : public ServiceFramework<APM> { +public: + APM(std::shared_ptr<Module> apm, const char* name); + ~APM() = default; + +private: + void OpenSession(Kernel::HLERequestContext& ctx); + + std::shared_ptr<Module> apm; +}; + +/// Registers all AM services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace APM +} // namespace Service diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp new file mode 100644 index 000000000..ee749fddd --- /dev/null +++ b/src/core/hle/service/audio/audin_u.cpp @@ -0,0 +1,41 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/audio/audin_u.h" + +namespace Service { +namespace Audio { + +class IAudioIn final : public ServiceFramework<IAudioIn> { +public: + IAudioIn() : ServiceFramework("IAudioIn") { + static const FunctionInfo functions[] = { + {0x0, nullptr, "GetAudioInState"}, + {0x1, nullptr, "StartAudioIn"}, + {0x2, nullptr, "StopAudioIn"}, + {0x3, nullptr, "AppendAudioInBuffer_1"}, + {0x4, nullptr, "RegisterBufferEvent"}, + {0x5, nullptr, "GetReleasedAudioInBuffer_1"}, + {0x6, nullptr, "ContainsAudioInBuffer"}, + {0x7, nullptr, "AppendAudioInBuffer_2"}, + {0x8, nullptr, "GetReleasedAudioInBuffer_2"}, + }; + RegisterHandlers(functions); + } + ~IAudioIn() = default; +}; + +AudInU::AudInU() : ServiceFramework("audin:u") { + static const FunctionInfo functions[] = { + {0x00000000, nullptr, "ListAudioIns"}, + {0x00000001, nullptr, "OpenAudioIn"}, + }; + RegisterHandlers(functions); +} + +} // namespace Audio +} // namespace Service diff --git a/src/core/hle/service/audio/audin_u.h b/src/core/hle/service/audio/audin_u.h new file mode 100644 index 000000000..2b8576756 --- /dev/null +++ b/src/core/hle/service/audio/audin_u.h @@ -0,0 +1,23 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Kernel { +class HLERequestContext; +} + +namespace Service { +namespace Audio { + +class AudInU final : public ServiceFramework<AudInU> { +public: + explicit AudInU(); + ~AudInU() = default; +}; + +} // namespace Audio +} // namespace Service diff --git a/src/core/hle/service/audio/audio.cpp b/src/core/hle/service/audio/audio.cpp index 2b4c6c5d0..3f7fb44eb 100644 --- a/src/core/hle/service/audio/audio.cpp +++ b/src/core/hle/service/audio/audio.cpp @@ -2,14 +2,22 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/hle/service/audio/audin_u.h" #include "core/hle/service/audio/audio.h" #include "core/hle/service/audio/audout_u.h" +#include "core/hle/service/audio/audrec_u.h" +#include "core/hle/service/audio/audren_u.h" +#include "core/hle/service/audio/codecctl.h" namespace Service { namespace Audio { void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<AudOutU>()->InstallAsService(service_manager); + std::make_shared<AudInU>()->InstallAsService(service_manager); + std::make_shared<AudRecU>()->InstallAsService(service_manager); + std::make_shared<AudRenU>()->InstallAsService(service_manager); + std::make_shared<CodecCtl>()->InstallAsService(service_manager); } } // namespace Audio diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index c028262c6..e873d768f 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -2,23 +2,195 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <vector> #include "common/logging/log.h" +#include "core/core_timing.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/event.h" +#include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/audio/audout_u.h" namespace Service { namespace Audio { +/// Switch sample rate frequency +constexpr u32 sample_rate{48000}; +/// TODO(st4rk): dynamic number of channels, as I think Switch has support +/// to more audio channels (probably when Docked I guess) +constexpr u32 audio_channels{2}; +/// TODO(st4rk): find a proper value for the audio_ticks +constexpr u64 audio_ticks{static_cast<u64>(BASE_CLOCK_RATE / 500)}; + +class IAudioOut final : public ServiceFramework<IAudioOut> { +public: + IAudioOut() : ServiceFramework("IAudioOut"), audio_out_state(AudioState::Stopped) { + static const FunctionInfo functions[] = { + {0x0, nullptr, "GetAudioOutState"}, + {0x1, &IAudioOut::StartAudioOut, "StartAudioOut"}, + {0x2, &IAudioOut::StopAudioOut, "StopAudioOut"}, + {0x3, &IAudioOut::AppendAudioOutBuffer_1, "AppendAudioOutBuffer_1"}, + {0x4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"}, + {0x5, &IAudioOut::GetReleasedAudioOutBuffer_1, "GetReleasedAudioOutBuffer_1"}, + {0x6, nullptr, "ContainsAudioOutBuffer"}, + {0x7, nullptr, "AppendAudioOutBuffer_2"}, + {0x8, nullptr, "GetReleasedAudioOutBuffer_2"}, + }; + RegisterHandlers(functions); + + // This is the event handle used to check if the audio buffer was released + buffer_event = + Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioOutBufferReleasedEvent"); + + // Register event callback to update the Audio Buffer + audio_event = CoreTiming::RegisterEvent( + "IAudioOut::UpdateAudioBuffersCallback", [this](u64 userdata, int cycles_late) { + UpdateAudioBuffersCallback(); + CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event); + }); + + // Start the audio event + CoreTiming::ScheduleEvent(audio_ticks, audio_event); + } + + ~IAudioOut() { + CoreTiming::UnscheduleEvent(audio_event, 0); + } + +private: + void StartAudioOut(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + + // Start audio + audio_out_state = AudioState::Started; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void StopAudioOut(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + + // Stop audio + audio_out_state = AudioState::Stopped; + + queue_keys.clear(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void RegisterBufferEvent(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(buffer_event); + } + + void AppendAudioOutBuffer_1(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + + const u64 key{rp.Pop<u64>()}; + queue_keys.insert(queue_keys.begin(), key); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetReleasedAudioOutBuffer_1(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + + // TODO(st4rk): This is how libtransistor currently implements the + // GetReleasedAudioOutBuffer, it should return the key (a VAddr) to the app and this address + // is used to know which buffer should be filled with data and send again to the service + // through AppendAudioOutBuffer. Check if this is the proper way to do it. + u64 key{0}; + + if (queue_keys.size()) { + key = queue_keys.back(); + queue_keys.pop_back(); + } + + ctx.WriteBuffer(&key, sizeof(u64)); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + // TODO(st4rk): This might be the total of released buffers, needs to be verified on + // hardware + rb.Push<u32>(static_cast<u32>(queue_keys.size())); + } + + void UpdateAudioBuffersCallback() { + if (audio_out_state != AudioState::Started) { + return; + } + + if (queue_keys.empty()) { + return; + } + + buffer_event->Signal(); + } + + enum class AudioState : u32 { + Started, + Stopped, + }; + + /// This is used to trigger the audio event callback that is going to read the samples from the + /// audio_buffer list and enqueue the samples using the sink (audio_core). + CoreTiming::EventType* audio_event; + + /// This is the evend handle used to check if the audio buffer was released + Kernel::SharedPtr<Kernel::Event> buffer_event; + + /// (st4rk): This is just a temporary workaround for the future implementation. Libtransistor + /// uses the key as an address in the App, so we need to return when the + /// GetReleasedAudioOutBuffer_1 is called, otherwise we'll run in problems, because + /// libtransistor uses the key returned as an pointer. + std::vector<u64> queue_keys; + + AudioState audio_out_state; +}; + void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - IPC::RequestBuilder rb{ctx, 2}; + LOG_WARNING(Service_Audio, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + + const std::string audio_interface = "AudioInterface"; + ctx.WriteBuffer(audio_interface.c_str(), audio_interface.size()); + + IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0); + + rb.Push(RESULT_SUCCESS); + // TODO(st4rk): We're currently returning only one audio interface (stringlist size). However, + // it's highly possible to have more than one interface (despite that libtransistor requires + // only one). + rb.Push<u32>(1); +} + +void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + + if (!audio_out_interface) { + audio_out_interface = std::make_shared<IAudioOut>(); + } + + IPC::ResponseBuilder rb{ctx, 6, 0, 1}; rb.Push(RESULT_SUCCESS); + rb.Push<u32>(sample_rate); + rb.Push<u32>(audio_channels); + rb.Push<u32>(static_cast<u32>(PcmFormat::Int16)); + rb.Push<u32>(0); // This field is unknown + rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface); } AudOutU::AudOutU() : ServiceFramework("audout:u") { - static const FunctionInfo functions[] = { - {0x00000000, &AudOutU::ListAudioOuts, "ListAudioOuts"}, - }; + static const FunctionInfo functions[] = {{0x00000000, &AudOutU::ListAudioOuts, "ListAudioOuts"}, + {0x00000001, &AudOutU::OpenAudioOut, "OpenAudioOut"}, + {0x00000002, nullptr, "Unknown2"}, + {0x00000003, nullptr, "Unknown3"}}; RegisterHandlers(functions); } diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h index 42680af94..7fbce2225 100644 --- a/src/core/hle/service/audio/audout_u.h +++ b/src/core/hle/service/audio/audout_u.h @@ -4,19 +4,37 @@ #pragma once -#include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/service.h" +namespace Kernel { +class HLERequestContext; +} + namespace Service { namespace Audio { +class IAudioOut; + class AudOutU final : public ServiceFramework<AudOutU> { public: AudOutU(); ~AudOutU() = default; private: + std::shared_ptr<IAudioOut> audio_out_interface; + void ListAudioOuts(Kernel::HLERequestContext& ctx); + void OpenAudioOut(Kernel::HLERequestContext& ctx); + + enum class PcmFormat : u32 { + Invalid = 0, + Int8 = 1, + Int16 = 2, + Int24 = 3, + Int32 = 4, + PcmFloat = 5, + Adpcm = 6, + }; }; } // namespace Audio diff --git a/src/core/hle/service/audio/audrec_u.cpp b/src/core/hle/service/audio/audrec_u.cpp new file mode 100644 index 000000000..f2626ec70 --- /dev/null +++ b/src/core/hle/service/audio/audrec_u.cpp @@ -0,0 +1,38 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/audio/audrec_u.h" + +namespace Service { +namespace Audio { + +class IFinalOutputRecorder final : public ServiceFramework<IFinalOutputRecorder> { +public: + IFinalOutputRecorder() : ServiceFramework("IFinalOutputRecorder") { + static const FunctionInfo functions[] = { + {0x0, nullptr, "GetFinalOutputRecorderState"}, + {0x1, nullptr, "StartFinalOutputRecorder"}, + {0x2, nullptr, "StopFinalOutputRecorder"}, + {0x3, nullptr, "AppendFinalOutputRecorderBuffer"}, + {0x4, nullptr, "RegisterBufferEvent"}, + {0x5, nullptr, "GetReleasedFinalOutputRecorderBuffer"}, + {0x6, nullptr, "ContainsFinalOutputRecorderBuffer"}, + }; + RegisterHandlers(functions); + } + ~IFinalOutputRecorder() = default; +}; + +AudRecU::AudRecU() : ServiceFramework("audrec:u") { + static const FunctionInfo functions[] = { + {0x00000000, nullptr, "OpenFinalOutputRecorder"}, + }; + RegisterHandlers(functions); +} + +} // namespace Audio +} // namespace Service diff --git a/src/core/hle/service/audio/audrec_u.h b/src/core/hle/service/audio/audrec_u.h new file mode 100644 index 000000000..c31e412c1 --- /dev/null +++ b/src/core/hle/service/audio/audrec_u.h @@ -0,0 +1,23 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Kernel { +class HLERequestContext; +} + +namespace Service { +namespace Audio { + +class AudRecU final : public ServiceFramework<AudRecU> { +public: + explicit AudRecU(); + ~AudRecU() = default; +}; + +} // namespace Audio +} // namespace Service diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp new file mode 100644 index 000000000..f52cd7d90 --- /dev/null +++ b/src/core/hle/service/audio/audren_u.cpp @@ -0,0 +1,232 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/core_timing.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/event.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/audio/audren_u.h" + +namespace Service { +namespace Audio { + +/// TODO(bunnei): Find a proper value for the audio_ticks +constexpr u64 audio_ticks{static_cast<u64>(BASE_CLOCK_RATE / 200)}; + +class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { +public: + IAudioRenderer() : ServiceFramework("IAudioRenderer") { + static const FunctionInfo functions[] = { + {0x0, nullptr, "GetAudioRendererSampleRate"}, + {0x1, nullptr, "GetAudioRendererSampleCount"}, + {0x2, nullptr, "GetAudioRendererMixBufferCount"}, + {0x3, nullptr, "GetAudioRendererState"}, + {0x4, &IAudioRenderer::RequestUpdateAudioRenderer, "RequestUpdateAudioRenderer"}, + {0x5, &IAudioRenderer::StartAudioRenderer, "StartAudioRenderer"}, + {0x6, &IAudioRenderer::StopAudioRenderer, "StopAudioRenderer"}, + {0x7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"}, + {0x8, nullptr, "SetAudioRendererRenderingTimeLimit"}, + {0x9, nullptr, "GetAudioRendererRenderingTimeLimit"}, + }; + RegisterHandlers(functions); + + system_event = + Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioRenderer:SystemEvent"); + + // Register event callback to update the Audio Buffer + audio_event = CoreTiming::RegisterEvent( + "IAudioRenderer::UpdateAudioCallback", [this](u64 userdata, int cycles_late) { + UpdateAudioCallback(); + CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event); + }); + + // Start the audio event + CoreTiming::ScheduleEvent(audio_ticks, audio_event); + } + ~IAudioRenderer() { + CoreTiming::UnscheduleEvent(audio_event, 0); + } + +private: + void UpdateAudioCallback() { + system_event->Signal(); + } + + void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "%s", ctx.Description().c_str()); + AudioRendererResponseData response_data{}; + + response_data.section_0_size = + response_data.state_entries.size() * sizeof(AudioRendererStateEntry); + response_data.section_1_size = response_data.section_1.size(); + response_data.section_2_size = response_data.section_2.size(); + response_data.section_3_size = response_data.section_3.size(); + response_data.section_4_size = response_data.section_4.size(); + response_data.section_5_size = response_data.section_5.size(); + response_data.total_size = sizeof(AudioRendererResponseData); + + for (unsigned i = 0; i < response_data.state_entries.size(); i++) { + // 4 = Busy and 5 = Ready? + response_data.state_entries[i].state = 5; + } + + ctx.WriteBuffer(&response_data, response_data.total_size); + + IPC::ResponseBuilder rb{ctx, 2}; + + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_Audio, "(STUBBED) called"); + } + + void StartAudioRenderer(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_Audio, "(STUBBED) called"); + } + + void StopAudioRenderer(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_Audio, "(STUBBED) called"); + } + + void QuerySystemEvent(Kernel::HLERequestContext& ctx) { + // system_event->Signal(); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(system_event); + + LOG_WARNING(Service_Audio, "(STUBBED) called"); + } + + struct AudioRendererStateEntry { + u32_le state; + u32_le unknown_4; + u32_le unknown_8; + u32_le unknown_c; + }; + static_assert(sizeof(AudioRendererStateEntry) == 0x10, + "AudioRendererStateEntry has wrong size"); + + struct AudioRendererResponseData { + u32_le unknown_0; + u32_le section_5_size; + u32_le section_0_size; + u32_le section_1_size; + u32_le unknown_10; + u32_le section_2_size; + u32_le unknown_18; + u32_le section_3_size; + u32_le section_4_size; + u32_le unknown_24; + u32_le unknown_28; + u32_le unknown_2c; + u32_le unknown_30; + u32_le unknown_34; + u32_le unknown_38; + u32_le total_size; + + std::array<AudioRendererStateEntry, 0x18e> state_entries; + + std::array<u8, 0x600> section_1; + std::array<u8, 0xe0> section_2; + std::array<u8, 0x20> section_3; + std::array<u8, 0x10> section_4; + std::array<u8, 0xb0> section_5; + }; + static_assert(sizeof(AudioRendererResponseData) == 0x20e0, + "AudioRendererResponseData has wrong size"); + + /// This is used to trigger the audio event callback. + CoreTiming::EventType* audio_event; + + Kernel::SharedPtr<Kernel::Event> system_event; +}; + +class IAudioDevice final : public ServiceFramework<IAudioDevice> { +public: + IAudioDevice() : ServiceFramework("IAudioDevice") { + static const FunctionInfo functions[] = { + {0x0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"}, + {0x1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"}}; + RegisterHandlers(functions); + + buffer_event = + Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioOutBufferReleasedEvent"); + } + +private: + void ListAudioDeviceName(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + + const std::string audio_interface = "AudioInterface"; + ctx.WriteBuffer(audio_interface.c_str(), audio_interface.size()); + + IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0); + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(1); + } + + void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + + IPC::RequestParser rp{ctx}; + f32 volume = static_cast<f32>(rp.Pop<u32>()); + + auto file_buffer = ctx.ReadBuffer(); + auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0'); + + IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); + rb.Push(RESULT_SUCCESS); + } + + Kernel::SharedPtr<Kernel::Event> buffer_event; +}; + +AudRenU::AudRenU() : ServiceFramework("audren:u") { + static const FunctionInfo functions[] = { + {0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"}, + {1, &AudRenU::GetAudioRendererWorkBufferSize, "GetAudioRendererWorkBufferSize"}, + {2, &AudRenU::GetAudioDevice, "GetAudioDevice"}, + }; + RegisterHandlers(functions); +} + +void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<Audio::IAudioRenderer>(); + + LOG_DEBUG(Service_Audio, "called"); +} + +void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(0x400); + + LOG_WARNING(Service_Audio, "(STUBBED) called"); +} + +void AudRenU::GetAudioDevice(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<Audio::IAudioDevice>(); + + LOG_DEBUG(Service_Audio, "called"); +} + +} // namespace Audio +} // namespace Service diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h new file mode 100644 index 000000000..f59d1627d --- /dev/null +++ b/src/core/hle/service/audio/audren_u.h @@ -0,0 +1,28 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Kernel { +class HLERequestContext; +} + +namespace Service { +namespace Audio { + +class AudRenU final : public ServiceFramework<AudRenU> { +public: + explicit AudRenU(); + ~AudRenU() = default; + +private: + void OpenAudioRenderer(Kernel::HLERequestContext& ctx); + void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx); + void GetAudioDevice(Kernel::HLERequestContext& ctx); +}; + +} // namespace Audio +} // namespace Service diff --git a/src/core/hle/service/audio/codecctl.cpp b/src/core/hle/service/audio/codecctl.cpp new file mode 100644 index 000000000..d2a7f4cd0 --- /dev/null +++ b/src/core/hle/service/audio/codecctl.cpp @@ -0,0 +1,33 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/audio/codecctl.h" + +namespace Service { +namespace Audio { + +CodecCtl::CodecCtl() : ServiceFramework("codecctl") { + static const FunctionInfo functions[] = { + {0x00000000, nullptr, "InitializeCodecController"}, + {0x00000001, nullptr, "FinalizeCodecController"}, + {0x00000002, nullptr, "SleepCodecController"}, + {0x00000003, nullptr, "WakeCodecController"}, + {0x00000004, nullptr, "SetCodecVolume"}, + {0x00000005, nullptr, "GetCodecVolumeMax"}, + {0x00000006, nullptr, "GetCodecVolumeMin"}, + {0x00000007, nullptr, "SetCodecActiveTarget"}, + {0x00000008, nullptr, "Unknown"}, + {0x00000009, nullptr, "BindCodecHeadphoneMicJackInterrupt"}, + {0x0000000A, nullptr, "IsCodecHeadphoneMicJackInserted"}, + {0x0000000B, nullptr, "ClearCodecHeadphoneMicJackInterrupt"}, + {0x0000000C, nullptr, "IsCodecDeviceRequested"}, + }; + RegisterHandlers(functions); +} + +} // namespace Audio +} // namespace Service diff --git a/src/core/hle/service/audio/codecctl.h b/src/core/hle/service/audio/codecctl.h new file mode 100644 index 000000000..1121ab0b1 --- /dev/null +++ b/src/core/hle/service/audio/codecctl.h @@ -0,0 +1,23 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Kernel { +class HLERequestContext; +} + +namespace Service { +namespace Audio { + +class CodecCtl final : public ServiceFramework<CodecCtl> { +public: + explicit CodecCtl(); + ~CodecCtl() = default; +}; + +} // namespace Audio +} // namespace Service diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp new file mode 100644 index 000000000..1a18e0051 --- /dev/null +++ b/src/core/hle/service/fatal/fatal.cpp @@ -0,0 +1,38 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/fatal/fatal.h" +#include "core/hle/service/fatal/fatal_p.h" +#include "core/hle/service/fatal/fatal_u.h" + +namespace Service { +namespace Fatal { + +Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) + : ServiceFramework(name), module(std::move(module)) {} + +void Module::Interface::FatalSimple(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx); + u32 error_code = rp.Pop<u32>(); + LOG_WARNING(Service_Fatal, "(STUBBED) called, error_code=0x%X", error_code); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void Module::Interface::TransitionToFatalError(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Fatal, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void InstallInterfaces(SM::ServiceManager& service_manager) { + auto module = std::make_shared<Module>(); + std::make_shared<Fatal_P>(module)->InstallAsService(service_manager); + std::make_shared<Fatal_U>(module)->InstallAsService(service_manager); +} + +} // namespace Fatal +} // namespace Service diff --git a/src/core/hle/service/fatal/fatal.h b/src/core/hle/service/fatal/fatal.h new file mode 100644 index 000000000..85272b4be --- /dev/null +++ b/src/core/hle/service/fatal/fatal.h @@ -0,0 +1,29 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace Fatal { + +class Module final { +public: + class Interface : public ServiceFramework<Interface> { + public: + Interface(std::shared_ptr<Module> module, const char* name); + + void FatalSimple(Kernel::HLERequestContext& ctx); + void TransitionToFatalError(Kernel::HLERequestContext& ctx); + + protected: + std::shared_ptr<Module> module; + }; +}; + +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace Fatal +} // namespace Service diff --git a/src/core/hle/service/fatal/fatal_p.cpp b/src/core/hle/service/fatal/fatal_p.cpp new file mode 100644 index 000000000..ba194e340 --- /dev/null +++ b/src/core/hle/service/fatal/fatal_p.cpp @@ -0,0 +1,14 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/fatal/fatal_p.h" + +namespace Service { +namespace Fatal { + +Fatal_P::Fatal_P(std::shared_ptr<Module> module) + : Module::Interface(std::move(module), "fatal:p") {} + +} // namespace Fatal +} // namespace Service diff --git a/src/core/hle/service/fatal/fatal_p.h b/src/core/hle/service/fatal/fatal_p.h new file mode 100644 index 000000000..d77b24bc4 --- /dev/null +++ b/src/core/hle/service/fatal/fatal_p.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/fatal/fatal.h" + +namespace Service { +namespace Fatal { + +class Fatal_P final : public Module::Interface { +public: + explicit Fatal_P(std::shared_ptr<Module> module); +}; + +} // namespace Fatal +} // namespace Service diff --git a/src/core/hle/service/fatal/fatal_u.cpp b/src/core/hle/service/fatal/fatal_u.cpp new file mode 100644 index 000000000..065cc868d --- /dev/null +++ b/src/core/hle/service/fatal/fatal_u.cpp @@ -0,0 +1,19 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/fatal/fatal_u.h" + +namespace Service { +namespace Fatal { + +Fatal_U::Fatal_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "fatal:u") { + static const FunctionInfo functions[] = { + {1, &Fatal_U::FatalSimple, "FatalSimple"}, + {2, &Fatal_U::TransitionToFatalError, "TransitionToFatalError"}, + }; + RegisterHandlers(functions); +} + +} // namespace Fatal +} // namespace Service diff --git a/src/core/hle/service/fatal/fatal_u.h b/src/core/hle/service/fatal/fatal_u.h new file mode 100644 index 000000000..22374755e --- /dev/null +++ b/src/core/hle/service/fatal/fatal_u.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/fatal/fatal.h" + +namespace Service { +namespace Fatal { + +class Fatal_U final : public Module::Interface { +public: + explicit Fatal_U(std::shared_ptr<Module> module); +}; + +} // namespace Fatal +} // namespace Service diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp new file mode 100644 index 000000000..945832e98 --- /dev/null +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -0,0 +1,79 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <boost/container/flat_map.hpp> +#include "common/file_util.h" +#include "core/file_sys/filesystem.h" +#include "core/file_sys/savedata_factory.h" +#include "core/file_sys/sdmc_factory.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/filesystem/fsp_srv.h" + +namespace Service { +namespace FileSystem { + +/** + * Map of registered file systems, identified by type. Once an file system is registered here, it + * is never removed until UnregisterFileSystems is called. + */ +static boost::container::flat_map<Type, std::unique_ptr<FileSys::FileSystemFactory>> filesystem_map; + +ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type) { + auto result = filesystem_map.emplace(type, std::move(factory)); + + bool inserted = result.second; + ASSERT_MSG(inserted, "Tried to register more than one system with same id code"); + + auto& filesystem = result.first->second; + LOG_DEBUG(Service_FS, "Registered file system %s with id code 0x%08X", + filesystem->GetName().c_str(), static_cast<u32>(type)); + return RESULT_SUCCESS; +} + +ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type, + FileSys::Path& path) { + LOG_TRACE(Service_FS, "Opening FileSystem with type=%d", type); + + auto itr = filesystem_map.find(type); + if (itr == filesystem_map.end()) { + // TODO(bunnei): Find a better error code for this + return ResultCode(-1); + } + + return itr->second->Open(path); +} + +ResultCode FormatFileSystem(Type type) { + LOG_TRACE(Service_FS, "Formatting FileSystem with type=%d", type); + + auto itr = filesystem_map.find(type); + if (itr == filesystem_map.end()) { + // TODO(bunnei): Find a better error code for this + return ResultCode(-1); + } + + FileSys::Path unused; + return itr->second->Format(unused); +} + +void RegisterFileSystems() { + filesystem_map.clear(); + + std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX); + std::string sd_directory = FileUtil::GetUserPath(D_SDMC_IDX); + + auto savedata = std::make_unique<FileSys::SaveData_Factory>(std::move(nand_directory)); + RegisterFileSystem(std::move(savedata), Type::SaveData); + + auto sdcard = std::make_unique<FileSys::SDMC_Factory>(std::move(sd_directory)); + RegisterFileSystem(std::move(sdcard), Type::SDMC); +} + +void InstallInterfaces(SM::ServiceManager& service_manager) { + RegisterFileSystems(); + std::make_shared<FSP_SRV>()->InstallAsService(service_manager); +} + +} // namespace FileSystem +} // namespace Service diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h new file mode 100644 index 000000000..56d26146e --- /dev/null +++ b/src/core/hle/service/filesystem/filesystem.h @@ -0,0 +1,59 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include "common/common_types.h" +#include "core/hle/result.h" + +namespace FileSys { +class FileSystemBackend; +class FileSystemFactory; +class Path; +} // namespace FileSys + +namespace Service { + +namespace SM { +class ServiceManager; +} // namespace SM + +namespace FileSystem { + +/// Supported FileSystem types +enum class Type { + RomFS = 1, + SaveData = 2, + SDMC = 3, +}; + +/** + * Registers a FileSystem, instances of which can later be opened using its IdCode. + * @param factory FileSystem backend interface to use + * @param type Type used to access this type of FileSystem + */ +ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type); + +/** + * Opens a file system + * @param type Type of the file system to open + * @param path Path to the file system, used with Binary paths + * @return FileSys::FileSystemBackend interface to the file system + */ +ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type, + FileSys::Path& path); + +/** + * Formats a file system + * @param type Type of the file system to format + * @return ResultCode of the operation + */ +ResultCode FormatFileSystem(Type type); + +/// Registers all Filesystem services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace FileSystem +} // namespace Service diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp new file mode 100644 index 000000000..41b8cbfd2 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -0,0 +1,445 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cinttypes> +#include "common/logging/log.h" +#include "core/core.h" +#include "core/file_sys/directory.h" +#include "core/file_sys/filesystem.h" +#include "core/file_sys/storage.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/client_port.h" +#include "core/hle/kernel/client_session.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/filesystem/fsp_srv.h" + +namespace Service { +namespace FileSystem { + +class IStorage final : public ServiceFramework<IStorage> { +public: + IStorage(std::unique_ptr<FileSys::StorageBackend>&& backend) + : ServiceFramework("IStorage"), backend(std::move(backend)) { + static const FunctionInfo functions[] = { + {0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, {2, nullptr, "Flush"}, + {3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, + }; + RegisterHandlers(functions); + } + +private: + std::unique_ptr<FileSys::StorageBackend> backend; + + void Read(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const s64 offset = rp.Pop<s64>(); + const s64 length = rp.Pop<s64>(); + + LOG_DEBUG(Service_FS, "called, offset=0x%ld, length=0x%ld", offset, length); + + // Error checking + if (length < 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); + return; + } + if (offset < 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); + return; + } + + // Read the data from the Storage backend + std::vector<u8> output(length); + ResultVal<size_t> res = backend->Read(offset, length, output.data()); + if (res.Failed()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res.Code()); + return; + } + + // Write the data to memory + ctx.WriteBuffer(output); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } +}; + +class IFile final : public ServiceFramework<IFile> { +public: + explicit IFile(std::unique_ptr<FileSys::StorageBackend>&& backend) + : ServiceFramework("IFile"), backend(std::move(backend)) { + static const FunctionInfo functions[] = { + {0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"}, {2, nullptr, "Flush"}, + {3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, + }; + RegisterHandlers(functions); + } + +private: + std::unique_ptr<FileSys::StorageBackend> backend; + + void Read(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 unk = rp.Pop<u64>(); + const s64 offset = rp.Pop<s64>(); + const s64 length = rp.Pop<s64>(); + + LOG_DEBUG(Service_FS, "called, offset=0x%ld, length=0x%ld", offset, length); + + // Error checking + if (length < 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); + return; + } + if (offset < 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); + return; + } + + // Read the data from the Storage backend + std::vector<u8> output(length); + ResultVal<size_t> res = backend->Read(offset, length, output.data()); + if (res.Failed()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res.Code()); + return; + } + + // Write the data to memory + ctx.WriteBuffer(output); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast<u64>(*res)); + } + + void Write(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 unk = rp.Pop<u64>(); + const s64 offset = rp.Pop<s64>(); + const s64 length = rp.Pop<s64>(); + + LOG_DEBUG(Service_FS, "called, offset=0x%ld, length=0x%ld", offset, length); + + // Error checking + if (length < 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); + return; + } + if (offset < 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); + return; + } + + // Write the data to the Storage backend + std::vector<u8> data = ctx.ReadBuffer(); + ResultVal<size_t> res = backend->Write(offset, length, true, data.data()); + if (res.Failed()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res.Code()); + return; + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } +}; + +class IDirectory final : public ServiceFramework<IDirectory> { +public: + explicit IDirectory(std::unique_ptr<FileSys::DirectoryBackend>&& backend) + : ServiceFramework("IDirectory"), backend(std::move(backend)) { + static const FunctionInfo functions[] = { + {0, &IDirectory::Read, "Read"}, + {1, &IDirectory::GetEntryCount, "GetEntryCount"}, + }; + RegisterHandlers(functions); + } + +private: + std::unique_ptr<FileSys::DirectoryBackend> backend; + + void Read(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 unk = rp.Pop<u64>(); + + LOG_DEBUG(Service_FS, "called, unk=0x%llx", unk); + + // Calculate how many entries we can fit in the output buffer + u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry); + + // Read the data from the Directory backend + std::vector<FileSys::Entry> entries(count_entries); + u64 read_entries = backend->Read(count_entries, entries.data()); + + // Convert the data into a byte array + std::vector<u8> output(entries.size() * sizeof(FileSys::Entry)); + std::memcpy(output.data(), entries.data(), output.size()); + + // Write the data to memory + ctx.WriteBuffer(output); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(read_entries); + } + + void GetEntryCount(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + u64 count = backend->GetEntryCount(); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(count); + } +}; + +class IFileSystem final : public ServiceFramework<IFileSystem> { +public: + explicit IFileSystem(std::unique_ptr<FileSys::FileSystemBackend>&& backend) + : ServiceFramework("IFileSystem"), backend(std::move(backend)) { + static const FunctionInfo functions[] = { + {0, &IFileSystem::CreateFile, "CreateFile"}, + {2, &IFileSystem::CreateDirectory, "CreateDirectory"}, + {7, &IFileSystem::GetEntryType, "GetEntryType"}, + {8, &IFileSystem::OpenFile, "OpenFile"}, + {9, &IFileSystem::OpenDirectory, "OpenDirectory"}, + {10, &IFileSystem::Commit, "Commit"}, + }; + RegisterHandlers(functions); + } + + void CreateFile(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto file_buffer = ctx.ReadBuffer(); + auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0'); + + std::string name(file_buffer.begin(), end); + + u64 mode = rp.Pop<u64>(); + u32 size = rp.Pop<u32>(); + + LOG_DEBUG(Service_FS, "called file %s mode 0x%" PRIX64 " size 0x%08X", name.c_str(), mode, + size); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend->CreateFile(name, size)); + } + + void CreateDirectory(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto file_buffer = ctx.ReadBuffer(); + auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0'); + + std::string name(file_buffer.begin(), end); + + LOG_DEBUG(Service_FS, "called directory %s", name.c_str()); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend->CreateDirectory(name)); + } + + void OpenFile(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto file_buffer = ctx.ReadBuffer(); + auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0'); + + std::string name(file_buffer.begin(), end); + + auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>()); + + LOG_DEBUG(Service_FS, "called file %s mode %u", name.c_str(), static_cast<u32>(mode)); + + auto result = backend->OpenFile(name, mode); + if (result.Failed()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result.Code()); + return; + } + + auto file = std::move(result.Unwrap()); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IFile>(std::move(file)); + } + + void OpenDirectory(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto file_buffer = ctx.ReadBuffer(); + auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0'); + + std::string name(file_buffer.begin(), end); + + // TODO(Subv): Implement this filter. + u32 filter_flags = rp.Pop<u32>(); + + LOG_DEBUG(Service_FS, "called directory %s filter %u", name.c_str(), filter_flags); + + auto result = backend->OpenDirectory(name); + if (result.Failed()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result.Code()); + return; + } + + auto directory = std::move(result.Unwrap()); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IDirectory>(std::move(directory)); + } + + void GetEntryType(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto file_buffer = ctx.ReadBuffer(); + auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0'); + + std::string name(file_buffer.begin(), end); + + LOG_DEBUG(Service_FS, "called file %s", name.c_str()); + + auto result = backend->GetEntryType(name); + if (result.Failed()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result.Code()); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(static_cast<u32>(*result)); + } + + void Commit(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + +private: + std::unique_ptr<FileSys::FileSystemBackend> backend; +}; + +FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { + static const FunctionInfo functions[] = { + {1, &FSP_SRV::Initalize, "Initalize"}, + {18, &FSP_SRV::MountSdCard, "MountSdCard"}, + {22, &FSP_SRV::CreateSaveData, "CreateSaveData"}, + {51, &FSP_SRV::MountSaveData, "MountSaveData"}, + {200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"}, + {202, nullptr, "OpenDataStorageByDataId"}, + {203, &FSP_SRV::OpenRomStorage, "OpenRomStorage"}, + {1005, &FSP_SRV::GetGlobalAccessLogMode, "GetGlobalAccessLogMode"}, + }; + RegisterHandlers(functions); +} + +void FSP_SRV::TryLoadRomFS() { + if (romfs) { + return; + } + FileSys::Path unused; + auto res = OpenFileSystem(Type::RomFS, unused); + if (res.Succeeded()) { + romfs = std::move(res.Unwrap()); + } +} + +void FSP_SRV::Initalize(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + FileSys::Path unused; + auto filesystem = OpenFileSystem(Type::SDMC, unused).Unwrap(); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IFileSystem>(std::move(filesystem)); +} + +void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + auto save_struct = rp.PopRaw<std::array<u8, 0x40>>(); + auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>(); + u128 uid = rp.PopRaw<u128>(); + + LOG_WARNING(Service_FS, "(STUBBED) called uid = %016" PRIX64 "%016" PRIX64, uid[1], uid[0]); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called"); + + FileSys::Path unused; + auto filesystem = OpenFileSystem(Type::SaveData, unused).Unwrap(); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IFileSystem>(std::move(filesystem)); +} + +void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(5); +} + +void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + TryLoadRomFS(); + if (!romfs) { + // TODO (bunnei): Find the right error code to use here + LOG_CRITICAL(Service_FS, "no file system interface available!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(-1)); + return; + } + + // Attempt to open a StorageBackend interface to the RomFS + auto storage = romfs->OpenFile({}, {}); + if (storage.Failed()) { + LOG_CRITICAL(Service_FS, "no storage interface available!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(storage.Code()); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IStorage>(std::move(storage.Unwrap())); +} + +void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called, using OpenDataStorageByCurrentProcess"); + OpenDataStorageByCurrentProcess(ctx); +} + +} // namespace FileSystem +} // namespace Service diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h new file mode 100644 index 000000000..e15ba4375 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp_srv.h @@ -0,0 +1,37 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include "core/hle/service/service.h" + +namespace FileSys { +class FileSystemBackend; +} + +namespace Service { +namespace FileSystem { + +class FSP_SRV final : public ServiceFramework<FSP_SRV> { +public: + explicit FSP_SRV(); + ~FSP_SRV() = default; + +private: + void TryLoadRomFS(); + + void Initalize(Kernel::HLERequestContext& ctx); + void MountSdCard(Kernel::HLERequestContext& ctx); + void CreateSaveData(Kernel::HLERequestContext& ctx); + void MountSaveData(Kernel::HLERequestContext& ctx); + void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); + void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); + void OpenRomStorage(Kernel::HLERequestContext& ctx); + + std::unique_ptr<FileSys::FileSystemBackend> romfs; +}; + +} // namespace FileSystem +} // namespace Service diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp new file mode 100644 index 000000000..26593bb0c --- /dev/null +++ b/src/core/hle/service/friend/friend.cpp @@ -0,0 +1,28 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/friend/friend.h" +#include "core/hle/service/friend/friend_a.h" + +namespace Service { +namespace Friend { + +void Module::Interface::Unknown(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_Friend, "(STUBBED) called"); +} + +Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) + : ServiceFramework(name), module(std::move(module)) {} + +void InstallInterfaces(SM::ServiceManager& service_manager) { + auto module = std::make_shared<Module>(); + std::make_shared<Friend_A>(module)->InstallAsService(service_manager); +} + +} // namespace Friend +} // namespace Service diff --git a/src/core/hle/service/friend/friend.h b/src/core/hle/service/friend/friend.h new file mode 100644 index 000000000..ffa498397 --- /dev/null +++ b/src/core/hle/service/friend/friend.h @@ -0,0 +1,29 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace Friend { + +class Module final { +public: + class Interface : public ServiceFramework<Interface> { + public: + Interface(std::shared_ptr<Module> module, const char* name); + + void Unknown(Kernel::HLERequestContext& ctx); + + protected: + std::shared_ptr<Module> module; + }; +}; + +/// Registers all Friend services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace Friend +} // namespace Service diff --git a/src/core/hle/service/friend/friend_a.cpp b/src/core/hle/service/friend/friend_a.cpp new file mode 100644 index 000000000..e1f2397c2 --- /dev/null +++ b/src/core/hle/service/friend/friend_a.cpp @@ -0,0 +1,19 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/friend/friend_a.h" + +namespace Service { +namespace Friend { + +Friend_A::Friend_A(std::shared_ptr<Module> module) + : Module::Interface(std::move(module), "friend:a") { + static const FunctionInfo functions[] = { + {0, &Friend_A::Unknown, "Unknown"}, + }; + RegisterHandlers(functions); +} + +} // namespace Friend +} // namespace Service diff --git a/src/core/hle/service/friend/friend_a.h b/src/core/hle/service/friend/friend_a.h new file mode 100644 index 000000000..68fa58297 --- /dev/null +++ b/src/core/hle/service/friend/friend_a.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/friend/friend.h" + +namespace Service { +namespace Friend { + +class Friend_A final : public Module::Interface { +public: + explicit Friend_A(std::shared_ptr<Module> module); +}; + +} // namespace Friend +} // namespace Service diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index be7a6ff65..a0b8c6243 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -9,6 +9,7 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/service.h" @@ -44,12 +45,16 @@ public: CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event); } + ~IAppletResource() { + CoreTiming::UnscheduleEvent(pad_update_event, 0); + } + private: void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 1}; + IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(shared_mem); - LOG_DEBUG(Service, "called"); + LOG_DEBUG(Service_HID, "called"); } void LoadInputDevices() { @@ -151,29 +156,195 @@ private: buttons; }; +class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { +public: + IActiveVibrationDeviceList() : ServiceFramework("IActiveVibrationDeviceList") { + static const FunctionInfo functions[] = { + {0, &IActiveVibrationDeviceList::ActivateVibrationDevice, "ActivateVibrationDevice"}, + }; + RegisterHandlers(functions); + } + +private: + void ActivateVibrationDevice(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } +}; + class Hid final : public ServiceFramework<Hid> { public: Hid() : ServiceFramework("hid") { static const FunctionInfo functions[] = { - {0x00000000, &Hid::CreateAppletResource, "CreateAppletResource"}, + {0, &Hid::CreateAppletResource, "CreateAppletResource"}, + {1, &Hid::ActivateDebugPad, "ActivateDebugPad"}, + {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"}, + {21, &Hid::ActivateMouse, "ActivateMouse"}, + {31, &Hid::ActivateKeyboard, "ActivateKeyboard"}, + {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"}, + {79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"}, + {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, + {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"}, + {103, &Hid::ActivateNpad, "ActivateNpad"}, + {106, &Hid::AcquireNpadStyleSetUpdateEventHandle, + "AcquireNpadStyleSetUpdateEventHandle"}, + {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"}, + {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"}, + {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, + "SetNpadJoyAssignmentModeSingleByDefault"}, + {124, &Hid::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"}, + {128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"}, + {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"}, + {201, &Hid::SendVibrationValue, "SendVibrationValue"}, + {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"}, + {203, &Hid::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"}, + {206, &Hid::SendVibrationValues, "SendVibrationValues"}, }; RegisterHandlers(functions); + + event = Kernel::Event::Create(Kernel::ResetType::OneShot, "hid:EventHandle"); } ~Hid() = default; private: + std::shared_ptr<IAppletResource> applet_resource; + u32 joy_hold_type{0}; + Kernel::SharedPtr<Kernel::Event> event; + void CreateAppletResource(Kernel::HLERequestContext& ctx) { - auto client_port = std::make_shared<IAppletResource>()->CreatePort(); - auto session = client_port->Connect(); - if (session.Succeeded()) { - LOG_DEBUG(Service, "called, initialized IAppletResource -> session=%u", - (*session)->GetObjectId()); - IPC::RequestBuilder rb{ctx, 2, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushMoveObjects(std::move(session).Unwrap()); - } else { - UNIMPLEMENTED(); + if (applet_resource == nullptr) { + applet_resource = std::make_shared<IAppletResource>(); } + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IAppletResource>(applet_resource); + LOG_DEBUG(Service_HID, "called"); + } + + void ActivateDebugPad(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void ActivateTouchScreen(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void ActivateMouse(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void ActivateKeyboard(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void StartSixAxisSensor(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void ActivateNpad(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(event); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(joy_hold_type); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void SendVibrationValue(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void GetActualVibrationValue(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(0); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + + void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IActiveVibrationDeviceList>(); + LOG_DEBUG(Service_HID, "called"); + } + + void SendVibrationValues(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); } }; diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index 2d0d2fb65..b8e53d2c7 100644 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <sstream> #include <string> #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" @@ -28,25 +29,35 @@ private: IsHead = 1, IsTail = 2, }; + enum Severity : u32_le { + Trace, + Info, + Warning, + Error, + Critical, + }; u64_le pid; u64_le threadContext; union { BitField<0, 16, Flags> flags; - BitField<16, 8, u32_le> severity; + BitField<16, 8, Severity> severity; BitField<24, 8, u32_le> verbosity; }; u32_le payload_size; - /// Returns true if this is part of a single log message - bool IsSingleMessage() const { - return (flags & Flags::IsHead) && (flags & Flags::IsTail); + bool IsHeadLog() const { + return flags & Flags::IsHead; + } + bool IsTailLog() const { + return flags & Flags::IsTail; } }; static_assert(sizeof(MessageHeader) == 0x18, "MessageHeader is incorrect size"); /// Log field type enum class Field : u8 { + Skip = 1, Message = 2, Line = 3, Filename = 4, @@ -56,7 +67,7 @@ private: }; /** - * LM::Initialize service function + * LM::Log service function * Inputs: * 0: 0x00000000 * Outputs: @@ -64,7 +75,7 @@ private: */ void Log(Kernel::HLERequestContext& ctx) { // This function only succeeds - Get that out of the way - IPC::RequestBuilder rb{ctx, 1}; + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); // Read MessageHeader, despite not doing anything with it right now @@ -74,9 +85,9 @@ private: Memory::ReadBlock(addr, &header, sizeof(MessageHeader)); addr += sizeof(MessageHeader); - if (!header.IsSingleMessage()) { - LOG_WARNING(Service, "Multi message logs are unimplemeneted"); - return; + if (header.IsHeadLog()) { + log_stream.str(""); + log_stream.clear(); } // Parse out log metadata @@ -84,7 +95,12 @@ private: std::string message, filename, function; while (addr < end_addr) { const Field field{static_cast<Field>(Memory::Read8(addr++))}; - size_t length{Memory::Read8(addr++)}; + const size_t length{Memory::Read8(addr++)}; + + if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) { + ++addr; + } + switch (field) { case Field::Message: message = Memory::ReadCString(addr, length); @@ -99,32 +115,52 @@ private: function = Memory::ReadCString(addr, length); break; } + addr += length; } // Empty log - nothing to do here - if (message.empty()) { + if (log_stream.str().empty() && message.empty()) { return; } // Format a nicely printable string out of the log metadata - std::string output; - if (filename.size()) { - output += filename + ':'; + if (!filename.empty()) { + log_stream << filename << ':'; } - if (function.size()) { - output += function + ':'; + if (!function.empty()) { + log_stream << function << ':'; } if (line) { - output += std::to_string(line) + ':'; + log_stream << std::to_string(line) << ':'; } - if (output.back() == ':') { - output += ' '; + if (log_stream.str().length() > 0 && log_stream.str().back() == ':') { + log_stream << ' '; } - output += message; + log_stream << message; - LOG_DEBUG(Debug_Emulated, "%s", output.c_str()); + if (header.IsTailLog()) { + switch (header.severity) { + case MessageHeader::Severity::Trace: + LOG_TRACE(Debug_Emulated, "%s", log_stream.str().c_str()); + break; + case MessageHeader::Severity::Info: + LOG_INFO(Debug_Emulated, "%s", log_stream.str().c_str()); + break; + case MessageHeader::Severity::Warning: + LOG_WARNING(Debug_Emulated, "%s", log_stream.str().c_str()); + break; + case MessageHeader::Severity::Error: + LOG_ERROR(Debug_Emulated, "%s", log_stream.str().c_str()); + break; + case MessageHeader::Severity::Critical: + LOG_CRITICAL(Debug_Emulated, "%s", log_stream.str().c_str()); + break; + } + } } + + std::ostringstream log_stream; }; void InstallInterfaces(SM::ServiceManager& service_manager) { @@ -139,20 +175,11 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { * 0: ResultCode */ void LM::Initialize(Kernel::HLERequestContext& ctx) { - auto client_port = std::make_shared<Logger>()->CreatePort(); - auto session = client_port->Connect(); - if (session.Succeeded()) { - LOG_DEBUG(Service_SM, "called, initialized logger -> session=%u", - (*session)->GetObjectId()); - IPC::RequestBuilder rb{ctx, 2, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushMoveObjects(std::move(session).Unwrap()); - registered_loggers.emplace_back(std::move(client_port)); - } else { - UNIMPLEMENTED(); - } + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<Logger>(); - LOG_INFO(Service_SM, "called"); + LOG_DEBUG(Service_LM, "called"); } LM::LM() : ServiceFramework("lm") { diff --git a/src/core/hle/service/lm/lm.h b/src/core/hle/service/lm/lm.h index 4b954bdb2..371135057 100644 --- a/src/core/hle/service/lm/lm.h +++ b/src/core/hle/service/lm/lm.h @@ -5,7 +5,6 @@ #pragma once #include <vector> -#include "core/hle/kernel/client_port.h" #include "core/hle/kernel/kernel.h" #include "core/hle/service/service.h" @@ -19,8 +18,6 @@ public: private: void Initialize(Kernel::HLERequestContext& ctx); - - std::vector<Kernel::SharedPtr<Kernel::ClientPort>> registered_loggers; }; /// Registers all LM services with the specified service manager. diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp new file mode 100644 index 000000000..dd2d5fe63 --- /dev/null +++ b/src/core/hle/service/nifm/nifm.cpp @@ -0,0 +1,213 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/event.h" +#include "core/hle/service/nifm/nifm.h" +#include "core/hle/service/nifm/nifm_a.h" +#include "core/hle/service/nifm/nifm_s.h" +#include "core/hle/service/nifm/nifm_u.h" + +namespace Service { +namespace NIFM { + +class IScanRequest final : public ServiceFramework<IScanRequest> { +public: + explicit IScanRequest() : ServiceFramework("IScanRequest") { + static const FunctionInfo functions[] = { + {0, nullptr, "Submit"}, + {1, nullptr, "IsProcessing"}, + {2, nullptr, "GetResult"}, + {3, nullptr, "GetSystemEventReadableHandle"}, + }; + RegisterHandlers(functions); + } +}; + +class IRequest final : public ServiceFramework<IRequest> { +public: + explicit IRequest() : ServiceFramework("IRequest") { + static const FunctionInfo functions[] = { + {0, &IRequest::GetRequestState, "GetRequestState"}, + {1, &IRequest::GetResult, "GetResult"}, + {2, &IRequest::GetSystemEventReadableHandles, "GetSystemEventReadableHandles"}, + {3, &IRequest::Cancel, "Cancel"}, + {4, nullptr, "Submit"}, + {5, nullptr, "SetRequirement"}, + {6, nullptr, "SetRequirementPreset"}, + {8, nullptr, "SetPriority"}, + {9, nullptr, "SetNetworkProfileId"}, + {10, nullptr, "SetRejectable"}, + {11, nullptr, "SetConnectionConfirmationOption"}, + {12, nullptr, "SetPersistent"}, + {13, nullptr, "SetInstant"}, + {14, nullptr, "SetSustainable"}, + {15, nullptr, "SetRawPriority"}, + {16, nullptr, "SetGreedy"}, + {17, nullptr, "SetSharable"}, + {18, nullptr, "SetRequirementByRevision"}, + {19, nullptr, "GetRequirement"}, + {20, nullptr, "GetRevision"}, + {21, nullptr, "GetAppletInfo"}, + {22, nullptr, "GetAdditionalInfo"}, + {23, nullptr, "SetKeptInSleep"}, + {24, nullptr, "RegisterSocketDescriptor"}, + {25, nullptr, "UnregisterSocketDescriptor"}, + }; + RegisterHandlers(functions); + + event1 = Kernel::Event::Create(Kernel::ResetType::OneShot, "IRequest:Event1"); + event2 = Kernel::Event::Create(Kernel::ResetType::OneShot, "IRequest:Event2"); + } + +private: + void GetRequestState(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); + } + void GetResult(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); + } + void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2, 2}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(event1, event2); + } + void Cancel(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + Kernel::SharedPtr<Kernel::Event> event1, event2; +}; + +class INetworkProfile final : public ServiceFramework<INetworkProfile> { +public: + explicit INetworkProfile() : ServiceFramework("INetworkProfile") { + static const FunctionInfo functions[] = { + {0, nullptr, "Update"}, + {1, nullptr, "PersistOld"}, + {2, nullptr, "Persist"}, + }; + RegisterHandlers(functions); + } +}; + +class IGeneralService final : public ServiceFramework<IGeneralService> { +public: + IGeneralService(); + +private: + void GetClientId(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(0); + } + void CreateScanRequest(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IScanRequest>(); + + LOG_DEBUG(Service_NIFM, "called"); + } + void CreateRequest(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IRequest>(); + + LOG_DEBUG(Service_NIFM, "called"); + } + void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<INetworkProfile>(); + + LOG_DEBUG(Service_NIFM, "called"); + } +}; + +IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") { + static const FunctionInfo functions[] = { + {1, &IGeneralService::GetClientId, "GetClientId"}, + {2, &IGeneralService::CreateScanRequest, "CreateScanRequest"}, + {4, &IGeneralService::CreateRequest, "CreateRequest"}, + {5, nullptr, "GetCurrentNetworkProfile"}, + {6, nullptr, "EnumerateNetworkInterfaces"}, + {7, nullptr, "EnumerateNetworkProfiles"}, + {8, nullptr, "GetNetworkProfile"}, + {9, nullptr, "SetNetworkProfile"}, + {10, &IGeneralService::RemoveNetworkProfile, "RemoveNetworkProfile"}, + {11, nullptr, "GetScanDataOld"}, + {12, nullptr, "GetCurrentIpAddress"}, + {13, nullptr, "GetCurrentAccessPointOld"}, + {14, &IGeneralService::CreateTemporaryNetworkProfile, "CreateTemporaryNetworkProfile"}, + {15, nullptr, "GetCurrentIpConfigInfo"}, + {16, nullptr, "SetWirelessCommunicationEnabled"}, + {17, nullptr, "IsWirelessCommunicationEnabled"}, + {18, nullptr, "GetInternetConnectionStatus"}, + {19, nullptr, "SetEthernetCommunicationEnabled"}, + {20, nullptr, "IsEthernetCommunicationEnabled"}, + {21, nullptr, "IsAnyInternetRequestAccepted"}, + {22, nullptr, "IsAnyForegroundRequestAccepted"}, + {23, nullptr, "PutToSleep"}, + {24, nullptr, "WakeUp"}, + {25, nullptr, "GetSsidListVersion"}, + {26, nullptr, "SetExclusiveClient"}, + {27, nullptr, "GetDefaultIpSetting"}, + {28, nullptr, "SetDefaultIpSetting"}, + {29, nullptr, "SetWirelessCommunicationEnabledForTest"}, + {30, nullptr, "SetEthernetCommunicationEnabledForTest"}, + {31, nullptr, "GetTelemetorySystemEventReadableHandle"}, + {32, nullptr, "GetTelemetryInfo"}, + {33, nullptr, "ConfirmSystemAvailability"}, + {34, nullptr, "SetBackgroundRequestEnabled"}, + {35, nullptr, "GetScanData"}, + {36, nullptr, "GetCurrentAccessPoint"}, + {37, nullptr, "Shutdown"}, + }; + RegisterHandlers(functions); +} + +void Module::Interface::CreateGeneralServiceOld(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IGeneralService>(); + LOG_DEBUG(Service_NIFM, "called"); +} + +void Module::Interface::CreateGeneralService(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IGeneralService>(); + LOG_DEBUG(Service_NIFM, "called"); +} + +Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) + : ServiceFramework(name), module(std::move(module)) {} + +void InstallInterfaces(SM::ServiceManager& service_manager) { + auto module = std::make_shared<Module>(); + std::make_shared<NIFM_A>(module)->InstallAsService(service_manager); + std::make_shared<NIFM_S>(module)->InstallAsService(service_manager); + std::make_shared<NIFM_U>(module)->InstallAsService(service_manager); +} + +} // namespace NIFM +} // namespace Service diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h new file mode 100644 index 000000000..11d263b12 --- /dev/null +++ b/src/core/hle/service/nifm/nifm.h @@ -0,0 +1,29 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace NIFM { + +class Module final { +public: + class Interface : public ServiceFramework<Interface> { + public: + Interface(std::shared_ptr<Module> module, const char* name); + + void CreateGeneralServiceOld(Kernel::HLERequestContext& ctx); + void CreateGeneralService(Kernel::HLERequestContext& ctx); + + protected: + std::shared_ptr<Module> module; + }; +}; + +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace NIFM +} // namespace Service diff --git a/src/core/hle/service/nifm/nifm_a.cpp b/src/core/hle/service/nifm/nifm_a.cpp new file mode 100644 index 000000000..f75df8c04 --- /dev/null +++ b/src/core/hle/service/nifm/nifm_a.cpp @@ -0,0 +1,19 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/nifm/nifm_a.h" + +namespace Service { +namespace NIFM { + +NIFM_A::NIFM_A(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nifm:a") { + static const FunctionInfo functions[] = { + {4, &NIFM_A::CreateGeneralServiceOld, "CreateGeneralServiceOld"}, + {5, &NIFM_A::CreateGeneralService, "CreateGeneralService"}, + }; + RegisterHandlers(functions); +} + +} // namespace NIFM +} // namespace Service diff --git a/src/core/hle/service/nifm/nifm_a.h b/src/core/hle/service/nifm/nifm_a.h new file mode 100644 index 000000000..eaea14e29 --- /dev/null +++ b/src/core/hle/service/nifm/nifm_a.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/nifm/nifm.h" + +namespace Service { +namespace NIFM { + +class NIFM_A final : public Module::Interface { +public: + explicit NIFM_A(std::shared_ptr<Module> module); +}; + +} // namespace NIFM +} // namespace Service diff --git a/src/core/hle/service/nifm/nifm_s.cpp b/src/core/hle/service/nifm/nifm_s.cpp new file mode 100644 index 000000000..9c0b300e4 --- /dev/null +++ b/src/core/hle/service/nifm/nifm_s.cpp @@ -0,0 +1,19 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/nifm/nifm_s.h" + +namespace Service { +namespace NIFM { + +NIFM_S::NIFM_S(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nifm:s") { + static const FunctionInfo functions[] = { + {4, &NIFM_S::CreateGeneralServiceOld, "CreateGeneralServiceOld"}, + {5, &NIFM_S::CreateGeneralService, "CreateGeneralService"}, + }; + RegisterHandlers(functions); +} + +} // namespace NIFM +} // namespace Service diff --git a/src/core/hle/service/nifm/nifm_s.h b/src/core/hle/service/nifm/nifm_s.h new file mode 100644 index 000000000..f9e2d8039 --- /dev/null +++ b/src/core/hle/service/nifm/nifm_s.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/nifm/nifm.h" + +namespace Service { +namespace NIFM { + +class NIFM_S final : public Module::Interface { +public: + explicit NIFM_S(std::shared_ptr<Module> module); +}; + +} // namespace NIFM +} // namespace Service diff --git a/src/core/hle/service/nifm/nifm_u.cpp b/src/core/hle/service/nifm/nifm_u.cpp new file mode 100644 index 000000000..44e6f483d --- /dev/null +++ b/src/core/hle/service/nifm/nifm_u.cpp @@ -0,0 +1,19 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/nifm/nifm_u.h" + +namespace Service { +namespace NIFM { + +NIFM_U::NIFM_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "nifm:u") { + static const FunctionInfo functions[] = { + {4, &NIFM_U::CreateGeneralServiceOld, "CreateGeneralServiceOld"}, + {5, &NIFM_U::CreateGeneralService, "CreateGeneralService"}, + }; + RegisterHandlers(functions); +} + +} // namespace NIFM +} // namespace Service diff --git a/src/core/hle/service/nifm/nifm_u.h b/src/core/hle/service/nifm/nifm_u.h new file mode 100644 index 000000000..912006775 --- /dev/null +++ b/src/core/hle/service/nifm/nifm_u.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/nifm/nifm.h" + +namespace Service { +namespace NIFM { + +class NIFM_U final : public Module::Interface { +public: + explicit NIFM_U(std::shared_ptr<Module> module); +}; + +} // namespace NIFM +} // namespace Service diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp new file mode 100644 index 000000000..45681c50f --- /dev/null +++ b/src/core/hle/service/ns/ns.cpp @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/ns/ns.h" +#include "core/hle/service/ns/pl_u.h" + +namespace Service { +namespace NS { + +void InstallInterfaces(SM::ServiceManager& service_manager) { + std::make_shared<PL_U>()->InstallAsService(service_manager); +} + +} // namespace NS +} // namespace Service diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h new file mode 100644 index 000000000..a4b7e3ded --- /dev/null +++ b/src/core/hle/service/ns/ns.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace NS { + +/// Registers all NS services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace NS +} // namespace Service diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp new file mode 100644 index 000000000..ef3c7799a --- /dev/null +++ b/src/core/hle/service/ns/pl_u.cpp @@ -0,0 +1,122 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/common_paths.h" +#include "common/file_util.h" +#include "core/core.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/ns/pl_u.h" + +namespace Service { +namespace NS { + +struct FontRegion { + u32 offset; + u32 size; +}; + +// The below data is specific to shared font data dumped from Switch on f/w 2.2 +// Virtual address and offsets/sizes likely will vary by dump +static constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL}; +static constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000}; +static constexpr std::array<FontRegion, 6> SHARED_FONT_REGIONS{ + FontRegion{0x00000008, 0x001fe764}, FontRegion{0x001fe774, 0x00773e58}, + FontRegion{0x009725d4, 0x0001aca8}, FontRegion{0x0098d284, 0x00369cec}, + FontRegion{0x00cf6f78, 0x0039b858}, FontRegion{0x010927d8, 0x00019e80}, +}; + +enum class LoadState : u32 { + Loading = 0, + Done = 1, +}; + +PL_U::PL_U() : ServiceFramework("pl:u") { + static const FunctionInfo functions[] = { + {0, &PL_U::RequestLoad, "RequestLoad"}, + {1, &PL_U::GetLoadState, "GetLoadState"}, + {2, &PL_U::GetSize, "GetSize"}, + {3, &PL_U::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"}, + {4, &PL_U::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}}; + RegisterHandlers(functions); + + // Attempt to load shared font data from disk + const std::string filepath{FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT}; + FileUtil::CreateFullPath(filepath); // Create path if not already created + FileUtil::IOFile file(filepath, "rb"); + + if (file.IsOpen()) { + // Read shared font data + ASSERT(file.GetSize() == SHARED_FONT_MEM_SIZE); + shared_font = std::make_shared<std::vector<u8>>(static_cast<size_t>(file.GetSize())); + file.ReadBytes(shared_font->data(), shared_font->size()); + } else { + LOG_WARNING(Service_NS, "Unable to load shared font: %s", filepath.c_str()); + } +} + +void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u32 shared_font_type{rp.Pop<u32>()}; + + LOG_DEBUG(Service_NS, "called, shared_font_type=%d", shared_font_type); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void PL_U::GetLoadState(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u32 font_id{rp.Pop<u32>()}; + + LOG_DEBUG(Service_NS, "called, font_id=%d", font_id); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(static_cast<u32>(LoadState::Done)); +} + +void PL_U::GetSize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u32 font_id{rp.Pop<u32>()}; + + LOG_DEBUG(Service_NS, "called, font_id=%d", font_id); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(SHARED_FONT_REGIONS[font_id].size); +} + +void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u32 font_id{rp.Pop<u32>()}; + + LOG_DEBUG(Service_NS, "called, font_id=%d", font_id); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(SHARED_FONT_REGIONS[font_id].offset); +} + +void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { + if (shared_font != nullptr) { + // TODO(bunnei): This is a less-than-ideal solution to load a RAM dump of the Switch shared + // font data. This (likely) relies on exact address, size, and offsets from the original + // dump. In the future, we need to replace this with a more robust solution. + + // Map backing memory for the font data + Core::CurrentProcess()->vm_manager.MapMemoryBlock(SHARED_FONT_MEM_VADDR, shared_font, 0, + SHARED_FONT_MEM_SIZE, + Kernel::MemoryState::Shared); + + // Create shared font memory object + shared_font_mem = Kernel::SharedMemory::Create( + Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite, + Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE, + "PL_U:shared_font_mem"); + } + + LOG_DEBUG(Service_NS, "called"); + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(shared_font_mem); +} + +} // namespace NS +} // namespace Service diff --git a/src/core/hle/service/ns/pl_u.h b/src/core/hle/service/ns/pl_u.h new file mode 100644 index 000000000..360482d13 --- /dev/null +++ b/src/core/hle/service/ns/pl_u.h @@ -0,0 +1,34 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include "core/hle/kernel/shared_memory.h" +#include "core/hle/service/service.h" + +namespace Service { +namespace NS { + +class PL_U final : public ServiceFramework<PL_U> { +public: + PL_U(); + ~PL_U() = default; + +private: + void RequestLoad(Kernel::HLERequestContext& ctx); + void GetLoadState(Kernel::HLERequestContext& ctx); + void GetSize(Kernel::HLERequestContext& ctx); + void GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx); + void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx); + + /// Handle to shared memory region designated for a shared font + Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem; + + /// Backing memory for the shared font data + std::shared_ptr<std::vector<u8>> shared_font; +}; + +} // namespace NS +} // namespace Service diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h index 5ee33b3d6..cdc25b059 100644 --- a/src/core/hle/service/nvdrv/devices/nvdevice.h +++ b/src/core/hle/service/nvdrv/devices/nvdevice.h @@ -5,7 +5,9 @@ #pragma once #include <vector> +#include "common/bit_field.h" #include "common/common_types.h" +#include "common/swap.h" namespace Service { namespace Nvidia { @@ -17,6 +19,14 @@ class nvdevice { public: nvdevice() = default; virtual ~nvdevice() = default; + union Ioctl { + u32_le raw; + BitField<0, 8, u32_le> cmd; + BitField<8, 8, u32_le> group; + BitField<16, 14, u32_le> length; + BitField<30, 1, u32_le> is_in; + BitField<31, 1, u32_le> is_out; + }; /** * Handles an ioctl request. @@ -25,7 +35,7 @@ public: * @param output A buffer where the output data will be written to. * @returns The result code of the ioctl. */ - virtual u32 ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) = 0; + virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) = 0; }; } // namespace Devices diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index b65d79f11..87b3a2d74 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -14,24 +14,25 @@ namespace Service { namespace Nvidia { namespace Devices { -u32 nvdisp_disp0::ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { UNIMPLEMENTED(); return 0; } void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, - u32 stride) { + u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform) { VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle); LOG_WARNING(Service, - "Drawing from address %llx offset %08X Width %u Height %u Stride %u Format %u", - addr, offset, width, height, stride, format); + "Drawing from address %lx offset %08X Width %u Height %u Stride %u Format %u", addr, + offset, width, height, stride, format); - using PixelFormat = RendererBase::FramebufferInfo::PixelFormat; - const RendererBase::FramebufferInfo framebuffer_info{ - addr, offset, width, height, stride, static_cast<PixelFormat>(format)}; + using PixelFormat = Tegra::FramebufferConfig::PixelFormat; + const Tegra::FramebufferConfig framebuffer{ + addr, offset, width, height, stride, static_cast<PixelFormat>(format), transform}; Core::System::GetInstance().perf_stats.EndGameFrame(); - VideoCore::g_renderer->SwapBuffers(framebuffer_info); + + VideoCore::g_renderer->SwapBuffers(framebuffer); } } // namespace Devices diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index f5f9de3f4..66f56f23d 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h @@ -8,6 +8,7 @@ #include <vector> #include "common/common_types.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" +#include "core/hle/service/nvflinger/buffer_queue.h" namespace Service { namespace Nvidia { @@ -20,10 +21,11 @@ public: nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev) : nvdevice(), nvmap_dev(std::move(nvmap_dev)) {} ~nvdisp_disp0() = default; - u32 ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; /// Performs a screen flip, drawing the buffer pointed to by the handle. - void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride); + void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, + NVFlinger::BufferQueue::BufferTransformFlags transform); private: std::shared_ptr<nvmap> nvmap_dev; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 9db08339a..9892402fa 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -2,16 +2,116 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cinttypes> #include "common/assert.h" #include "common/logging/log.h" +#include "core/core.h" #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" +#include "core/hle/service/nvdrv/devices/nvmap.h" namespace Service { namespace Nvidia { namespace Devices { -u32 nvhost_as_gpu::ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) { - UNIMPLEMENTED(); +u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { + LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%zx, output_size=0x%zx", + command.raw, input.size(), output.size()); + + switch (static_cast<IoctlCommand>(command.raw)) { + case IoctlCommand::IocInitalizeExCommand: + return InitalizeEx(input, output); + case IoctlCommand::IocAllocateSpaceCommand: + return AllocateSpace(input, output); + case IoctlCommand::IocMapBufferExCommand: + return MapBufferEx(input, output); + case IoctlCommand::IocBindChannelCommand: + return BindChannel(input, output); + case IoctlCommand::IocGetVaRegionsCommand: + return GetVARegions(input, output); + } + return 0; +} + +u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlInitalizeEx params{}; + std::memcpy(¶ms, input.data(), input.size()); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x%x", params.big_page_size); + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlAllocSpace params{}; + std::memcpy(¶ms, input.data(), input.size()); + LOG_DEBUG(Service_NVDRV, "called, pages=%x, page_size=%x, flags=%x", params.pages, + params.page_size, params.flags); + + auto& gpu = Core::System::GetInstance().GPU(); + const u64 size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)}; + if (params.flags & 1) { + params.offset = gpu.memory_manager->AllocateSpace(params.offset, size, 1); + } else { + params.offset = gpu.memory_manager->AllocateSpace(size, params.align); + } + + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlMapBufferEx params{}; + std::memcpy(¶ms, input.data(), input.size()); + + LOG_DEBUG(Service_NVDRV, + "called, flags=%x, nvmap_handle=%x, buffer_offset=%" PRIu64 ", mapping_size=%" PRIu64 + ", offset=%" PRIu64, + params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size, + params.offset); + + if (!params.nvmap_handle) { + return 0; + } + + auto object = nvmap_dev->GetObject(params.nvmap_handle); + ASSERT(object); + + auto& gpu = Core::System::GetInstance().GPU(); + + if (params.flags & 1) { + params.offset = gpu.memory_manager->MapBufferEx(object->addr, params.offset, object->size); + } else { + params.offset = gpu.memory_manager->MapBufferEx(object->addr, object->size); + } + + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlBindChannel params{}; + std::memcpy(¶ms, input.data(), input.size()); + LOG_DEBUG(Service_NVDRV, "called, fd=%x", params.fd); + channel = params.fd; + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlGetVaRegions params{}; + std::memcpy(¶ms, input.data(), input.size()); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr=%" PRIu64 ", buf_size=%x", + params.buf_addr, params.buf_size); + + params.buf_size = 0x30; + params.regions[0].offset = 0x04000000; + params.regions[0].page_size = 0x1000; + params.regions[0].pages = 0x3fbfff; + + params.regions[1].offset = 0x04000000; + params.regions[1].page_size = 0x10000; + params.regions[1].pages = 0x1bffff; + // TODO(ogniK): This probably can stay stubbed but should add support way way later + std::memcpy(output.data(), ¶ms, output.size()); return 0; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index 01f8861c8..f8a60cce7 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -4,20 +4,100 @@ #pragma once +#include <memory> +#include <utility> #include <vector> #include "common/common_types.h" +#include "common/swap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" namespace Service { namespace Nvidia { namespace Devices { +class nvmap; + class nvhost_as_gpu final : public nvdevice { public: - nvhost_as_gpu() = default; + nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} ~nvhost_as_gpu() override = default; - u32 ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + +private: + enum class IoctlCommand : u32_le { + IocInitalizeExCommand = 0x40284109, + IocAllocateSpaceCommand = 0xC0184102, + IocMapBufferExCommand = 0xC0284106, + IocBindChannelCommand = 0x40044101, + IocGetVaRegionsCommand = 0xC0404108, + }; + + struct IoctlInitalizeEx { + u32_le big_page_size; // depends on GPU's available_big_page_sizes; 0=default + s32_le as_fd; // ignored; passes 0 + u32_le flags; // passes 0 + u32_le reserved; // ignored; passes 0 + u64_le unk0; + u64_le unk1; + u64_le unk2; + }; + static_assert(sizeof(IoctlInitalizeEx) == 40, "IoctlInitalizeEx is incorrect size"); + + struct IoctlAllocSpace { + u32_le pages; + u32_le page_size; + u32_le flags; + INSERT_PADDING_WORDS(1); + union { + u64_le offset; + u64_le align; + }; + }; + static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitalizeEx is incorrect size"); + + struct IoctlMapBufferEx { + u32_le flags; // bit0: fixed_offset, bit2: cacheable + u32_le kind; // -1 is default + u32_le nvmap_handle; + u32_le page_size; // 0 means don't care + u64_le buffer_offset; + u64_le mapping_size; + u64_le offset; + }; + static_assert(sizeof(IoctlMapBufferEx) == 40, "IoctlMapBufferEx is incorrect size"); + + struct IoctlBindChannel { + u32_le fd; + }; + static_assert(sizeof(IoctlBindChannel) == 4, "IoctlBindChannel is incorrect size"); + + struct IoctlVaRegion { + u64_le offset; + u32_le page_size; + INSERT_PADDING_WORDS(1); + u64_le pages; + }; + static_assert(sizeof(IoctlVaRegion) == 24, "IoctlVaRegion is incorrect size"); + + struct IoctlGetVaRegions { + u64_le buf_addr; // (contained output user ptr on linux, ignored) + u32_le buf_size; // forced to 2*sizeof(struct va_region) + u32_le reserved; + IoctlVaRegion regions[2]; + }; + static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2, + "IoctlGetVaRegions is incorrect size"); + + u32 channel{}; + + u32 InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output); + u32 AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output); + u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output); + u32 BindChannel(const std::vector<u8>& input, std::vector<u8>& output); + u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output); + + std::shared_ptr<nvmap> nvmap_dev; }; } // namespace Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp new file mode 100644 index 000000000..45711d686 --- /dev/null +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -0,0 +1,64 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" + +namespace Service { +namespace Nvidia { +namespace Devices { + +u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { + LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%zx, output_size=0x%zx", + command.raw, input.size(), output.size()); + + switch (static_cast<IoctlCommand>(command.raw)) { + case IoctlCommand::IocGetConfigCommand: + return NvOsGetConfigU32(input, output); + case IoctlCommand::IocCtrlEventWaitCommand: + return IocCtrlEventWait(input, output); + } + UNIMPLEMENTED(); + return 0; +} + +u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) { + IocGetConfigParams params{}; + std::memcpy(¶ms, input.data(), sizeof(params)); + LOG_DEBUG(Service_NVDRV, "called, setting=%s!%s", params.domain_str.data(), + params.param_str.data()); + + if (!strcmp(params.domain_str.data(), "nv")) { + if (!strcmp(params.param_str.data(), "NV_MEMORY_PROFILER")) { + params.config_str[0] = '0'; + } else if (!strcmp(params.param_str.data(), "NVN_THROUGH_OPENGL")) { + params.config_str[0] = '0'; + } else if (!strcmp(params.param_str.data(), "NVRM_GPU_PREVENT_USE")) { + params.config_str[0] = '0'; + } else { + params.config_str[0] = '0'; + } + } else { + UNIMPLEMENTED(); // unknown domain? Only nv has been seen so far on hardware + } + std::memcpy(output.data(), ¶ms, sizeof(params)); + return 0; +} + +u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output) { + IocCtrlEventWaitParams params{}; + std::memcpy(¶ms, input.data(), sizeof(params)); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, syncpt_id=%u threshold=%u timeout=%d", + params.syncpt_id, params.threshold, params.timeout); + + // TODO(Subv): Implement actual syncpt waiting. + params.value = 0; + std::memcpy(output.data(), ¶ms, sizeof(params)); + return 0; +} + +} // namespace Devices +} // namespace Nvidia +} // namespace Service diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h new file mode 100644 index 000000000..0ca01aa6d --- /dev/null +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -0,0 +1,60 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <cstdlib> +#include <cstring> +#include <vector> +#include "common/common_types.h" +#include "core/hle/service/nvdrv/devices/nvdevice.h" + +namespace Service { +namespace Nvidia { +namespace Devices { + +class nvhost_ctrl final : public nvdevice { +public: + nvhost_ctrl() = default; + ~nvhost_ctrl() override = default; + + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + +private: + enum class IoctlCommand : u32_le { + IocSyncptReadCommand = 0xC0080014, + IocSyncptIncrCommand = 0x40040015, + IocSyncptWaitCommand = 0xC00C0016, + IocModuleMutexCommand = 0x40080017, + IocModuleRegRDWRCommand = 0xC008010E, + IocSyncptWaitexCommand = 0xC0100019, + IocSyncptReadMaxCommand = 0xC008001A, + IocCtrlEventWaitCommand = 0xC010001D, + IocGetConfigCommand = 0xC183001B, + }; + + struct IocGetConfigParams { + std::array<char, 0x41> domain_str; + std::array<char, 0x41> param_str; + std::array<char, 0x101> config_str; + }; + static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size"); + + struct IocCtrlEventWaitParams { + u32_le syncpt_id; + u32_le threshold; + s32_le timeout; + u32_le value; + }; + static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size"); + + u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); + + u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output); +}; + +} // namespace Devices +} // namespace Nvidia +} // namespace Service diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp new file mode 100644 index 000000000..3b353d742 --- /dev/null +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -0,0 +1,127 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cinttypes> +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" + +namespace Service { +namespace Nvidia { +namespace Devices { + +u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { + LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%zx, output_size=0x%zx", + command.raw, input.size(), output.size()); + + switch (static_cast<IoctlCommand>(command.raw)) { + case IoctlCommand::IocGetCharacteristicsCommand: + return GetCharacteristics(input, output); + case IoctlCommand::IocGetTPCMasksCommand: + return GetTPCMasks(input, output); + case IoctlCommand::IocGetActiveSlotMaskCommand: + return GetActiveSlotMask(input, output); + case IoctlCommand::IocZcullGetCtxSizeCommand: + return ZCullGetCtxSize(input, output); + case IoctlCommand::IocZcullGetInfo: + return ZCullGetInfo(input, output); + } + UNIMPLEMENTED(); + return 0; +} + +u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output) { + LOG_DEBUG(Service_NVDRV, "called"); + IoctlCharacteristics params{}; + std::memcpy(¶ms, input.data(), input.size()); + params.gc.arch = 0x120; + params.gc.impl = 0xb; + params.gc.rev = 0xa1; + params.gc.num_gpc = 0x1; + params.gc.l2_cache_size = 0x40000; + params.gc.on_board_video_memory_size = 0x0; + params.gc.num_tpc_per_gpc = 0x2; + params.gc.bus_type = 0x20; + params.gc.big_page_size = 0x20000; + params.gc.compression_page_size = 0x20000; + params.gc.pde_coverage_bit_count = 0x1B; + params.gc.available_big_page_sizes = 0x30000; + params.gc.gpc_mask = 0x1; + params.gc.sm_arch_sm_version = 0x503; + params.gc.sm_arch_spa_version = 0x503; + params.gc.sm_arch_warp_count = 0x80; + params.gc.gpu_va_bit_count = 0x28; + params.gc.reserved = 0x0; + params.gc.flags = 0x55; + params.gc.twod_class = 0x902D; + params.gc.threed_class = 0xB197; + params.gc.compute_class = 0xB1C0; + params.gc.gpfifo_class = 0xB06F; + params.gc.inline_to_memory_class = 0xA140; + params.gc.dma_copy_class = 0xB0B5; + params.gc.max_fbps_count = 0x1; + params.gc.fbp_en_mask = 0x0; + params.gc.max_ltc_per_fbp = 0x2; + params.gc.max_lts_per_ltc = 0x1; + params.gc.max_tex_per_tpc = 0x0; + params.gc.max_gpc_count = 0x1; + params.gc.rop_l2_en_mask_0 = 0x21D70; + params.gc.rop_l2_en_mask_1 = 0x0; + params.gc.chipname = 0x6230326D67; + params.gc.gr_compbit_store_base_hw = 0x0; + params.gpu_characteristics_buf_size = 0xA0; + params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED) + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlGpuGetTpcMasksArgs params{}; + std::memcpy(¶ms, input.data(), input.size()); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, mask=0x%x, mask_buf_addr=0x%" PRIx64, + params.mask_buf_size, params.mask_buf_addr); + std::memcpy(output.data(), ¶ms, sizeof(params)); + return 0; +} + +u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) { + LOG_DEBUG(Service_NVDRV, "called"); + IoctlActiveSlotMask params{}; + std::memcpy(¶ms, input.data(), input.size()); + params.slot = 0x07; + params.mask = 0x01; + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) { + LOG_DEBUG(Service_NVDRV, "called"); + IoctlZcullGetCtxSize params{}; + std::memcpy(¶ms, input.data(), input.size()); + params.size = 0x1; + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) { + LOG_DEBUG(Service_NVDRV, "called"); + IoctlNvgpuGpuZcullGetInfoArgs params{}; + std::memcpy(¶ms, input.data(), input.size()); + params.width_align_pixels = 0x20; + params.height_align_pixels = 0x20; + params.pixel_squares_by_aliquots = 0x400; + params.aliquot_total = 0x800; + params.region_byte_multiplier = 0x20; + params.region_header_size = 0x20; + params.subregion_header_size = 0xc0; + params.subregion_width_align_pixels = 0x20; + params.subregion_height_align_pixels = 0x40; + params.subregion_count = 0x10; + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +} // namespace Devices +} // namespace Nvidia +} // namespace Service diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h new file mode 100644 index 000000000..dc0476993 --- /dev/null +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h @@ -0,0 +1,130 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <vector> +#include "common/common_types.h" +#include "common/swap.h" +#include "core/hle/service/nvdrv/devices/nvdevice.h" + +namespace Service { +namespace Nvidia { +namespace Devices { + +class nvhost_ctrl_gpu final : public nvdevice { +public: + nvhost_ctrl_gpu() = default; + ~nvhost_ctrl_gpu() override = default; + + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + +private: + enum class IoctlCommand : u32_le { + IocGetCharacteristicsCommand = 0xC0B04705, + IocGetTPCMasksCommand = 0xC0184706, + IocGetActiveSlotMaskCommand = 0x80084714, + IocZcullGetCtxSizeCommand = 0x80044701, + IocZcullGetInfo = 0x80284702, + }; + + struct IoctlGpuCharacteristics { + u32_le arch; // 0x120 (NVGPU_GPU_ARCH_GM200) + u32_le impl; // 0xB (NVGPU_GPU_IMPL_GM20B) + u32_le rev; // 0xA1 (Revision A1) + u32_le num_gpc; // 0x1 + u64_le l2_cache_size; // 0x40000 + u64_le on_board_video_memory_size; // 0x0 (not used) + u32_le num_tpc_per_gpc; // 0x2 + u32_le bus_type; // 0x20 (NVGPU_GPU_BUS_TYPE_AXI) + u32_le big_page_size; // 0x20000 + u32_le compression_page_size; // 0x20000 + u32_le pde_coverage_bit_count; // 0x1B + u32_le available_big_page_sizes; // 0x30000 + u32_le gpc_mask; // 0x1 + u32_le sm_arch_sm_version; // 0x503 (Maxwell Generation 5.0.3?) + u32_le sm_arch_spa_version; // 0x503 (Maxwell Generation 5.0.3?) + u32_le sm_arch_warp_count; // 0x80 + u32_le gpu_va_bit_count; // 0x28 + u32_le reserved; // NULL + u64_le flags; // 0x55 + u32_le twod_class; // 0x902D (FERMI_TWOD_A) + u32_le threed_class; // 0xB197 (MAXWELL_B) + u32_le compute_class; // 0xB1C0 (MAXWELL_COMPUTE_B) + u32_le gpfifo_class; // 0xB06F (MAXWELL_CHANNEL_GPFIFO_A) + u32_le inline_to_memory_class; // 0xA140 (KEPLER_INLINE_TO_MEMORY_B) + u32_le dma_copy_class; // 0xB0B5 (MAXWELL_DMA_COPY_A) + u32_le max_fbps_count; // 0x1 + u32_le fbp_en_mask; // 0x0 (disabled) + u32_le max_ltc_per_fbp; // 0x2 + u32_le max_lts_per_ltc; // 0x1 + u32_le max_tex_per_tpc; // 0x0 (not supported) + u32_le max_gpc_count; // 0x1 + u32_le rop_l2_en_mask_0; // 0x21D70 (fuse_status_opt_rop_l2_fbp_r) + u32_le rop_l2_en_mask_1; // 0x0 + u64_le chipname; // 0x6230326D67 ("gm20b") + u64_le gr_compbit_store_base_hw; // 0x0 (not supported) + }; + static_assert(sizeof(IoctlGpuCharacteristics) == 160, + "IoctlGpuCharacteristics is incorrect size"); + + struct IoctlCharacteristics { + u64_le gpu_characteristics_buf_size; // must not be NULL, but gets overwritten with + // 0xA0=max_size + u64_le gpu_characteristics_buf_addr; // ignored, but must not be NULL + IoctlGpuCharacteristics gc; + }; + static_assert(sizeof(IoctlCharacteristics) == 16 + sizeof(IoctlGpuCharacteristics), + "IoctlCharacteristics is incorrect size"); + + struct IoctlGpuGetTpcMasksArgs { + /// [in] TPC mask buffer size reserved by userspace. Should be at least + /// sizeof(__u32) * fls(gpc_mask) to receive TPC mask for each GPC. + /// [out] full kernel buffer size + u32_le mask_buf_size; + u32_le reserved; + + /// [in] pointer to TPC mask buffer. It will receive one 32-bit TPC mask per GPC or 0 if + /// GPC is not enabled or not present. This parameter is ignored if mask_buf_size is 0. + u64_le mask_buf_addr; + u64_le unk; // Nintendo add this? + }; + static_assert(sizeof(IoctlGpuGetTpcMasksArgs) == 24, + "IoctlGpuGetTpcMasksArgs is incorrect size"); + + struct IoctlActiveSlotMask { + u32_le slot; // always 0x07 + u32_le mask; + }; + static_assert(sizeof(IoctlActiveSlotMask) == 8, "IoctlActiveSlotMask is incorrect size"); + + struct IoctlZcullGetCtxSize { + u32_le size; + }; + static_assert(sizeof(IoctlZcullGetCtxSize) == 4, "IoctlZcullGetCtxSize is incorrect size"); + + struct IoctlNvgpuGpuZcullGetInfoArgs { + u32_le width_align_pixels; + u32_le height_align_pixels; + u32_le pixel_squares_by_aliquots; + u32_le aliquot_total; + u32_le region_byte_multiplier; + u32_le region_header_size; + u32_le subregion_header_size; + u32_le subregion_width_align_pixels; + u32_le subregion_height_align_pixels; + u32_le subregion_count; + }; + static_assert(sizeof(IoctlNvgpuGpuZcullGetInfoArgs) == 40, + "IoctlNvgpuGpuZcullGetInfoArgs is incorrect size"); + + u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output); + u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output); + u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output); + u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output); + u32 ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output); +}; +} // namespace Devices +} // namespace Nvidia +} // namespace Service diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp new file mode 100644 index 000000000..da44c65f3 --- /dev/null +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -0,0 +1,147 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cinttypes> +#include <map> +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/core.h" +#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" + +namespace Service { +namespace Nvidia { +namespace Devices { + +u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { + LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%zx, output_size=0x%zx", + command.raw, input.size(), output.size()); + + switch (static_cast<IoctlCommand>(command.raw)) { + case IoctlCommand::IocSetNVMAPfdCommand: + return SetNVMAPfd(input, output); + case IoctlCommand::IocSetClientDataCommand: + return SetClientData(input, output); + case IoctlCommand::IocGetClientDataCommand: + return GetClientData(input, output); + case IoctlCommand::IocZCullBind: + return ZCullBind(input, output); + case IoctlCommand::IocSetErrorNotifierCommand: + return SetErrorNotifier(input, output); + case IoctlCommand::IocChannelSetPriorityCommand: + return SetChannelPriority(input, output); + case IoctlCommand::IocAllocGPFIFOEx2Command: + return AllocGPFIFOEx2(input, output); + case IoctlCommand::IocAllocObjCtxCommand: + return AllocateObjectContext(input, output); + } + + if (command.group == NVGPU_IOCTL_MAGIC) { + if (command.cmd == NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO) { + return SubmitGPFIFO(input, output); + } + } + + UNIMPLEMENTED(); + return 0; +}; + +u32 nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlSetNvmapFD params{}; + std::memcpy(¶ms, input.data(), input.size()); + LOG_DEBUG(Service_NVDRV, "called, fd=%x", params.nvmap_fd); + nvmap_fd = params.nvmap_fd; + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& output) { + LOG_DEBUG(Service_NVDRV, "called"); + IoctlClientData params{}; + std::memcpy(¶ms, input.data(), input.size()); + user_data = params.data; + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& output) { + LOG_DEBUG(Service_NVDRV, "called"); + IoctlClientData params{}; + std::memcpy(¶ms, input.data(), input.size()); + params.data = user_data; + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) { + std::memcpy(&zcull_params, input.data(), input.size()); + LOG_DEBUG(Service_NVDRV, "called, gpu_va=%" PRIx64 ", mode=%x", zcull_params.gpu_va, + zcull_params.mode); + std::memcpy(output.data(), &zcull_params, output.size()); + return 0; +} + +u32 nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlSetErrorNotifier params{}; + std::memcpy(¶ms, input.data(), input.size()); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset=%" PRIx64 ", size=%" PRIx64 ", mem=%x", + params.offset, params.size, params.mem); + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) { + std::memcpy(&channel_priority, input.data(), input.size()); + LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority=%x", channel_priority); + std::memcpy(output.data(), &channel_priority, output.size()); + return 0; +} + +u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlAllocGpfifoEx2 params{}; + std::memcpy(¶ms, input.data(), input.size()); + LOG_WARNING(Service_NVDRV, + "(STUBBED) called, num_entries=%x, flags=%x, unk0=%x, unk1=%x, unk2=%x, unk3=%x", + params.num_entries, params.flags, params.unk0, params.unk1, params.unk2, + params.unk3); + params.fence_out.id = 0; + params.fence_out.value = 0; + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlAllocObjCtx params{}; + std::memcpy(¶ms, input.data(), input.size()); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num=%x, flags=%x", params.class_num, + params.flags); + params.obj_id = 0x0; + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) { + if (input.size() < sizeof(IoctlSubmitGpfifo)) + UNIMPLEMENTED(); + IoctlSubmitGpfifo params{}; + std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo)); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo=%" PRIx64 ", num_entries=%x, flags=%x", + params.gpfifo, params.num_entries, params.flags); + + auto entries = std::vector<IoctlGpfifoEntry>(); + entries.resize(params.num_entries); + std::memcpy(&entries[0], &input.data()[sizeof(IoctlSubmitGpfifo)], + params.num_entries * sizeof(IoctlGpfifoEntry)); + for (auto entry : entries) { + VAddr va_addr = entry.Address(); + Core::System::GetInstance().GPU().ProcessCommandList(va_addr, entry.sz); + } + params.fence_out.id = 0; + params.fence_out.value = 0; + std::memcpy(output.data(), ¶ms, output.size()); + return 0; +} + +} // namespace Devices +} // namespace Nvidia +} // namespace Service diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h new file mode 100644 index 000000000..e7e9a0088 --- /dev/null +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -0,0 +1,144 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <vector> +#include "common/common_types.h" +#include "common/swap.h" +#include "core/hle/service/nvdrv/devices/nvdevice.h" + +namespace Service { +namespace Nvidia { +namespace Devices { + +class nvmap; +constexpr u32 NVGPU_IOCTL_MAGIC('H'); +constexpr u32 NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO(0x8); + +class nvhost_gpu final : public nvdevice { +public: + nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} + ~nvhost_gpu() override = default; + + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + +private: + enum class IoctlCommand : u32_le { + IocSetNVMAPfdCommand = 0x40044801, + IocSetClientDataCommand = 0x40084714, + IocGetClientDataCommand = 0x80084715, + IocZCullBind = 0xc010480b, + IocSetErrorNotifierCommand = 0xC018480C, + IocChannelSetPriorityCommand = 0x4004480D, + IocAllocGPFIFOEx2Command = 0xC020481A, + IocAllocObjCtxCommand = 0xC0104809, + }; + + enum class CtxObjects : u32_le { + Ctx2D = 0x902D, + Ctx3D = 0xB197, + CtxCompute = 0xB1C0, + CtxKepler = 0xA140, + CtxDMA = 0xB0B5, + CtxChannelGPFIFO = 0xB06F, + }; + + struct IoctlSetNvmapFD { + u32_le nvmap_fd; + }; + static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size"); + + struct IoctlClientData { + u64_le data; + }; + static_assert(sizeof(IoctlClientData) == 8, "IoctlClientData is incorrect size"); + + struct IoctlZCullBind { + u64_le gpu_va; + u32_le mode; // 0=global, 1=no_ctxsw, 2=separate_buffer, 3=part_of_regular_buf + INSERT_PADDING_WORDS(1); + }; + static_assert(sizeof(IoctlZCullBind) == 16, "IoctlZCullBind is incorrect size"); + + struct IoctlSetErrorNotifier { + u64_le offset; + u64_le size; + u32_le mem; // nvmap object handle + INSERT_PADDING_WORDS(1); + }; + static_assert(sizeof(IoctlSetErrorNotifier) == 24, "IoctlSetErrorNotifier is incorrect size"); + + struct IoctlFence { + u32_le id; + u32_le value; + }; + static_assert(sizeof(IoctlFence) == 8, "IoctlFence is incorrect size"); + + struct IoctlAllocGpfifoEx2 { + u32_le num_entries; // in + u32_le flags; // in + u32_le unk0; // in (1 works) + IoctlFence fence_out; // out + u32_le unk1; // in + u32_le unk2; // in + u32_le unk3; // in + }; + static_assert(sizeof(IoctlAllocGpfifoEx2) == 32, "IoctlAllocGpfifoEx2 is incorrect size"); + + struct IoctlAllocObjCtx { + u32_le class_num; // 0x902D=2d, 0xB197=3d, 0xB1C0=compute, 0xA140=kepler, 0xB0B5=DMA, + // 0xB06F=channel_gpfifo + u32_le flags; + u64_le obj_id; // (ignored) used for FREE_OBJ_CTX ioctl, which is not supported + }; + static_assert(sizeof(IoctlAllocObjCtx) == 16, "IoctlAllocObjCtx is incorrect size"); + + struct IoctlGpfifoEntry { + u32_le entry0; // gpu_va_lo + union { + u32_le entry1; // gpu_va_hi | (unk_0x02 << 0x08) | (size << 0x0A) | (unk_0x01 << 0x1F) + BitField<0, 8, u32_le> gpu_va_hi; + BitField<8, 2, u32_le> unk1; + BitField<10, 21, u32_le> sz; + BitField<31, 1, u32_le> unk2; + }; + + VAddr Address() const { + return (static_cast<VAddr>(gpu_va_hi) << 32) | entry0; + } + }; + static_assert(sizeof(IoctlGpfifoEntry) == 8, "IoctlGpfifoEntry is incorrect size"); + + struct IoctlSubmitGpfifo { + u64_le gpfifo; // (ignored) pointer to gpfifo fence structs + u32_le num_entries; // number of fence objects being submitted + u32_le flags; + IoctlFence fence_out; // returned new fence object for others to wait on + }; + static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(IoctlFence), + "submit_gpfifo is incorrect size"); + + u32_le nvmap_fd{}; + u64_le user_data{}; + IoctlZCullBind zcull_params{}; + u32_le channel_priority{}; + + u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); + u32 SetClientData(const std::vector<u8>& input, std::vector<u8>& output); + u32 GetClientData(const std::vector<u8>& input, std::vector<u8>& output); + u32 ZCullBind(const std::vector<u8>& input, std::vector<u8>& output); + u32 SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output); + u32 SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output); + u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output); + u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output); + u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output); + + std::shared_ptr<nvmap> nvmap_dev; +}; + +} // namespace Devices +} // namespace Nvidia +} // namespace Service diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index d37b5b159..b3842eb4c 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include <algorithm> +#include <cinttypes> #include "common/assert.h" #include "common/logging/log.h" @@ -13,29 +14,28 @@ namespace Nvidia { namespace Devices { VAddr nvmap::GetObjectAddress(u32 handle) const { - auto itr = handles.find(handle); - ASSERT(itr != handles.end()); - - auto object = itr->second; + auto object = GetObject(handle); + ASSERT(object); ASSERT(object->status == Object::Status::Allocated); return object->addr; } -u32 nvmap::ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) { - switch (command) { - case IocCreateCommand: +u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { + switch (static_cast<IoctlCommand>(command.raw)) { + case IoctlCommand::Create: return IocCreate(input, output); - case IocAllocCommand: + case IoctlCommand::Alloc: return IocAlloc(input, output); - case IocGetIdCommand: + case IoctlCommand::GetId: return IocGetId(input, output); - case IocFromIdCommand: + case IoctlCommand::FromId: return IocFromId(input, output); - case IocParamCommand: + case IoctlCommand::Param: return IocParam(input, output); } UNIMPLEMENTED(); + return 0; } u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { @@ -51,7 +51,7 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { u32 handle = next_handle++; handles[handle] = std::move(object); - LOG_WARNING(Service, "(STUBBED) size 0x%08X", params.size); + LOG_DEBUG(Service_NVDRV, "size=0x%08X", params.size); params.handle = handle; @@ -63,17 +63,16 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) { IocAllocParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - auto itr = handles.find(params.handle); - ASSERT(itr != handles.end()); + auto object = GetObject(params.handle); + ASSERT(object); - auto object = itr->second; object->flags = params.flags; object->align = params.align; object->kind = params.kind; object->addr = params.addr; object->status = Object::Status::Allocated; - LOG_WARNING(Service, "(STUBBED) Allocated address 0x%llx", params.addr); + LOG_DEBUG(Service_NVDRV, "called, addr=0x%" PRIx64, params.addr); std::memcpy(output.data(), ¶ms, sizeof(params)); return 0; @@ -83,12 +82,12 @@ u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) { IocGetIdParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service, "called"); + LOG_WARNING(Service_NVDRV, "called"); - auto itr = handles.find(params.handle); - ASSERT(itr != handles.end()); + auto object = GetObject(params.handle); + ASSERT(object); - params.id = itr->second->id; + params.id = object->id; std::memcpy(output.data(), ¶ms, sizeof(params)); return 0; @@ -98,17 +97,14 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) { IocFromIdParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service, "(STUBBED) called"); + LOG_WARNING(Service_NVDRV, "(STUBBED) called"); auto itr = std::find_if(handles.begin(), handles.end(), [&](const auto& entry) { return entry.second->id == params.id; }); ASSERT(itr != handles.end()); - // Make a new handle for the object - u32 handle = next_handle++; - handles[handle] = itr->second; - - params.handle = handle; + // Return the existing handle instead of creating a new one. + params.handle = itr->first; std::memcpy(output.data(), ¶ms, sizeof(params)); return 0; @@ -120,12 +116,10 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) { IocParamParams params; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service, "(STUBBED) called type=%u", params.type); - - auto itr = handles.find(params.handle); - ASSERT(itr != handles.end()); + LOG_WARNING(Service_NVDRV, "(STUBBED) called type=%u", params.type); - auto object = itr->second; + auto object = GetObject(params.handle); + ASSERT(object); ASSERT(object->status == Object::Status::Allocated); switch (static_cast<ParamTypes>(params.type)) { diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index 6954c0324..4681e438b 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h @@ -24,10 +24,9 @@ public: /// Returns the allocated address of an nvmap object given its handle. VAddr GetObjectAddress(u32 handle) const; - u32 ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; -private: - // Represents an nvmap object. + /// Represents an nvmap object. struct Object { enum class Status { Created, Allocated }; u32 id; @@ -39,21 +38,30 @@ private: Status status; }; + std::shared_ptr<Object> GetObject(u32 handle) const { + auto itr = handles.find(handle); + if (itr != handles.end()) { + return itr->second; + } + return {}; + } + +private: /// Id to use for the next handle that is created. u32 next_handle = 1; - // Id to use for the next object that is created. + /// Id to use for the next object that is created. u32 next_id = 1; /// Mapping of currently allocated handles to the objects they represent. std::unordered_map<u32, std::shared_ptr<Object>> handles; - enum IoctlCommands { - IocCreateCommand = 0xC0080101, - IocFromIdCommand = 0xC0080103, - IocAllocCommand = 0xC0200104, - IocParamCommand = 0xC00C0109, - IocGetIdCommand = 0xC008010E + enum class IoctlCommand : u32 { + Create = 0xC0080101, + FromId = 0xC0080103, + Alloc = 0xC0200104, + Param = 0xC00C0109, + GetId = 0xC008010E }; struct IocCreateParams { diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp index 848615fa7..c70370f1f 100644 --- a/src/core/hle/service/nvdrv/interface.cpp +++ b/src/core/hle/service/nvdrv/interface.cpp @@ -2,8 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cinttypes> #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/event.h" #include "core/hle/service/nvdrv/interface.h" #include "core/hle/service/nvdrv/nvdrv.h" @@ -11,62 +13,81 @@ namespace Service { namespace Nvidia { void NVDRV::Open(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + LOG_DEBUG(Service_NVDRV, "called"); - auto buffer = ctx.BufferDescriptorA()[0]; - - std::string device_name = Memory::ReadCString(buffer.Address(), buffer.Size()); + const auto& buffer = ctx.ReadBuffer(); + std::string device_name(buffer.begin(), buffer.end()); u32 fd = nvdrv->Open(device_name); - IPC::RequestBuilder rb{ctx, 4}; + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(fd); rb.Push<u32>(0); } void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + LOG_DEBUG(Service_NVDRV, "called"); IPC::RequestParser rp{ctx}; u32 fd = rp.Pop<u32>(); u32 command = rp.Pop<u32>(); - auto input_buffer = ctx.BufferDescriptorA()[0]; - auto output_buffer = ctx.BufferDescriptorB()[0]; - - std::vector<u8> input(input_buffer.Size()); - std::vector<u8> output(output_buffer.Size()); - - Memory::ReadBlock(input_buffer.Address(), input.data(), input_buffer.Size()); + std::vector<u8> output(ctx.GetWriteBufferSize()); - u32 nv_result = nvdrv->Ioctl(fd, command, input, output); - - Memory::WriteBlock(output_buffer.Address(), output.data(), output_buffer.Size()); - - IPC::RequestBuilder rb{ctx, 3}; + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(nv_result); + rb.Push(nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output)); + + ctx.WriteBuffer(output); } void NVDRV::Close(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + LOG_DEBUG(Service_NVDRV, "called"); IPC::RequestParser rp{ctx}; u32 fd = rp.Pop<u32>(); auto result = nvdrv->Close(fd); - IPC::RequestBuilder rb{ctx, 2}; + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(result); } void NVDRV::Initialize(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - IPC::RequestBuilder rb{ctx, 3}; + LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(0); } +void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + u32 fd = rp.Pop<u32>(); + u32 event_id = rp.Pop<u32>(); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd=%x, event_id=%x", fd, event_id); + + IPC::ResponseBuilder rb{ctx, 3, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(query_event); + rb.Push<u32>(0); +} + +void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + pid = rp.Pop<u64>(); + + LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid=0x%" PRIx64, pid); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); +} + +void NVDRV::FinishInitialize(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name) : ServiceFramework(name), nvdrv(std::move(nvdrv)) { static const FunctionInfo functions[] = { @@ -74,8 +95,13 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name) {1, &NVDRV::Ioctl, "Ioctl"}, {2, &NVDRV::Close, "Close"}, {3, &NVDRV::Initialize, "Initialize"}, + {4, &NVDRV::QueryEvent, "QueryEvent"}, + {8, &NVDRV::SetClientPID, "SetClientPID"}, + {13, &NVDRV::FinishInitialize, "FinishInitialize"}, }; RegisterHandlers(functions); + + query_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "NVDRV::query_event"); } } // namespace Nvidia diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h index 1b9aa9938..daf2302af 100644 --- a/src/core/hle/service/nvdrv/interface.h +++ b/src/core/hle/service/nvdrv/interface.h @@ -6,6 +6,7 @@ #include <memory> #include <string> +#include "core/hle/kernel/event.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/service.h" @@ -22,8 +23,15 @@ private: void Ioctl(Kernel::HLERequestContext& ctx); void Close(Kernel::HLERequestContext& ctx); void Initialize(Kernel::HLERequestContext& ctx); + void QueryEvent(Kernel::HLERequestContext& ctx); + void SetClientPID(Kernel::HLERequestContext& ctx); + void FinishInitialize(Kernel::HLERequestContext& ctx); std::shared_ptr<Module> nvdrv; + + u64 pid{}; + + Kernel::SharedPtr<Kernel::Event> query_event; }; } // namespace Nvidia diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 9b73886bb..ea00240e6 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -6,9 +6,13 @@ #include "core/hle/service/nvdrv/devices/nvdevice.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" +#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" +#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" +#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" #include "core/hle/service/nvdrv/devices/nvmap.h" -#include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvdrv/interface.h" +#include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/nvdrv/nvmemp.h" namespace Service { namespace Nvidia { @@ -19,14 +23,20 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { auto module_ = std::make_shared<Module>(); std::make_shared<NVDRV>(module_, "nvdrv")->InstallAsService(service_manager); std::make_shared<NVDRV>(module_, "nvdrv:a")->InstallAsService(service_manager); + std::make_shared<NVDRV>(module_, "nvdrv:s")->InstallAsService(service_manager); + std::make_shared<NVDRV>(module_, "nvdrv:t")->InstallAsService(service_manager); + std::make_shared<NVMEMP>()->InstallAsService(service_manager); nvdrv = module_; } Module::Module() { auto nvmap_dev = std::make_shared<Devices::nvmap>(); - devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(); + devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(nvmap_dev); + devices["/dev/nvhost-gpu"] = std::make_shared<Devices::nvhost_gpu>(nvmap_dev); + devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(); devices["/dev/nvmap"] = nvmap_dev; devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(nvmap_dev); + devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>(); } u32 Module::Open(std::string device_name) { @@ -41,12 +51,12 @@ u32 Module::Open(std::string device_name) { return fd; } -u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 Module::Ioctl(u32 fd, u32_le command, const std::vector<u8>& input, std::vector<u8>& output) { auto itr = open_files.find(fd); ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device"); auto device = itr->second; - return device->ioctl(command, input, output); + return device->ioctl({command}, input, output); } ResultCode Module::Close(u32 fd) { diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index e44644624..6a55ff96d 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -17,6 +17,13 @@ namespace Devices { class nvdevice; } +struct IoctlFence { + u32 id; + u32 value; +}; + +static_assert(sizeof(IoctlFence) == 8, "IoctlFence has wrong size"); + class Module final { public: Module(); diff --git a/src/core/hle/service/nvdrv/nvmemp.cpp b/src/core/hle/service/nvdrv/nvmemp.cpp new file mode 100644 index 000000000..5a13732c7 --- /dev/null +++ b/src/core/hle/service/nvdrv/nvmemp.cpp @@ -0,0 +1,31 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/nvdrv/nvmemp.h" + +namespace Service { +namespace Nvidia { + +NVMEMP::NVMEMP() : ServiceFramework("nvmemp") { + static const FunctionInfo functions[] = { + {0, &NVMEMP::Unknown0, "Unknown0"}, + {1, &NVMEMP::Unknown1, "Unknown1"}, + }; + RegisterHandlers(functions); +} + +void NVMEMP::Unknown0(Kernel::HLERequestContext& ctx) { + UNIMPLEMENTED(); +} + +void NVMEMP::Unknown1(Kernel::HLERequestContext& ctx) { + UNIMPLEMENTED(); +} + +} // namespace Nvidia +} // namespace Service diff --git a/src/core/hle/service/nvdrv/nvmemp.h b/src/core/hle/service/nvdrv/nvmemp.h new file mode 100644 index 000000000..a6b5fbb82 --- /dev/null +++ b/src/core/hle/service/nvdrv/nvmemp.h @@ -0,0 +1,23 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace Nvidia { + +class NVMEMP final : public ServiceFramework<NVMEMP> { +public: + NVMEMP(); + ~NVMEMP() = default; + +private: + void Unknown0(Kernel::HLERequestContext& ctx); + void Unknown1(Kernel::HLERequestContext& ctx); +}; + +} // namespace Nvidia +} // namespace Service diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp new file mode 100644 index 000000000..e4ff2e267 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp @@ -0,0 +1,115 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> + +#include "common/alignment.h" +#include "common/scope_exit.h" +#include "core/core_timing.h" +#include "core/hle/service/nvflinger/buffer_queue.h" + +namespace Service { +namespace NVFlinger { + +BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) { + native_handle = Kernel::Event::Create(Kernel::ResetType::OneShot, "BufferQueue NativeHandle"); + native_handle->Signal(); +} + +void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) { + Buffer buffer{}; + buffer.slot = slot; + buffer.igbp_buffer = igbp_buffer; + buffer.status = Buffer::Status::Free; + + LOG_WARNING(Service, "Adding graphics buffer %u", slot); + + queue.emplace_back(buffer); + + if (buffer_wait_event) { + buffer_wait_event->Signal(); + } +} + +boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) { + auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { + // Only consider free buffers. Buffers become free once again after they've been Acquired + // and Released by the compositor, see the NVFlinger::Compose method. + if (buffer.status != Buffer::Status::Free) { + return false; + } + + // Make sure that the parameters match. + return buffer.igbp_buffer.width == width && buffer.igbp_buffer.height == height; + }); + + if (itr == queue.end()) { + return boost::none; + } + + buffer_wait_event = nullptr; + + itr->status = Buffer::Status::Dequeued; + return itr->slot; +} + +const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const { + auto itr = std::find_if(queue.begin(), queue.end(), + [&](const Buffer& buffer) { return buffer.slot == slot; }); + ASSERT(itr != queue.end()); + ASSERT(itr->status == Buffer::Status::Dequeued); + return itr->igbp_buffer; +} + +void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform) { + auto itr = std::find_if(queue.begin(), queue.end(), + [&](const Buffer& buffer) { return buffer.slot == slot; }); + ASSERT(itr != queue.end()); + ASSERT(itr->status == Buffer::Status::Dequeued); + itr->status = Buffer::Status::Queued; + itr->transform = transform; +} + +boost::optional<const BufferQueue::Buffer&> BufferQueue::AcquireBuffer() { + auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) { + return buffer.status == Buffer::Status::Queued; + }); + if (itr == queue.end()) + return boost::none; + itr->status = Buffer::Status::Acquired; + return *itr; +} + +void BufferQueue::ReleaseBuffer(u32 slot) { + auto itr = std::find_if(queue.begin(), queue.end(), + [&](const Buffer& buffer) { return buffer.slot == slot; }); + ASSERT(itr != queue.end()); + ASSERT(itr->status == Buffer::Status::Acquired); + itr->status = Buffer::Status::Free; + + if (buffer_wait_event) { + buffer_wait_event->Signal(); + } +} + +u32 BufferQueue::Query(QueryType type) { + LOG_WARNING(Service, "(STUBBED) called type=%u", static_cast<u32>(type)); + switch (type) { + case QueryType::NativeWindowFormat: + // TODO(Subv): Use an enum for this + static constexpr u32 FormatABGR8 = 1; + return FormatABGR8; + } + + UNIMPLEMENTED(); + return 0; +} + +void BufferQueue::SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event) { + ASSERT_MSG(!buffer_wait_event, "buffer_wait_event only supports a single waiting thread!"); + buffer_wait_event = std::move(wait_event); +} + +} // namespace NVFlinger +} // namespace Service diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h new file mode 100644 index 000000000..1de5767cb --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue.h @@ -0,0 +1,102 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <vector> +#include <boost/optional.hpp> +#include "common/swap.h" +#include "core/hle/kernel/event.h" + +namespace CoreTiming { +struct EventType; +} + +namespace Service { +namespace NVFlinger { + +struct IGBPBuffer { + u32_le magic; + u32_le width; + u32_le height; + u32_le stride; + u32_le format; + u32_le usage; + INSERT_PADDING_WORDS(1); + u32_le index; + INSERT_PADDING_WORDS(3); + u32_le gpu_buffer_id; + INSERT_PADDING_WORDS(17); + u32_le nvmap_handle; + u32_le offset; + INSERT_PADDING_WORDS(60); +}; + +static_assert(sizeof(IGBPBuffer) == 0x16C, "IGBPBuffer has wrong size"); + +class BufferQueue final { +public: + enum class QueryType { + NativeWindowWidth = 0, + NativeWindowHeight = 1, + NativeWindowFormat = 2, + }; + + BufferQueue(u32 id, u64 layer_id); + ~BufferQueue() = default; + + enum class BufferTransformFlags : u32 { + /// No transform flags are set + Unset = 0x00, + /// Flip source image horizontally (around the vertical axis) + FlipH = 0x01, + /// Flip source image vertically (around the horizontal axis) + FlipV = 0x02, + /// Rotate source image 90 degrees clockwise + Rotate90 = 0x04, + /// Rotate source image 180 degrees + Roate180 = 0x03, + /// Rotate source image 270 degrees clockwise + Roate270 = 0x07, + }; + + struct Buffer { + enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 }; + + u32 slot; + Status status = Status::Free; + IGBPBuffer igbp_buffer; + BufferTransformFlags transform; + }; + + void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer); + boost::optional<u32> DequeueBuffer(u32 width, u32 height); + const IGBPBuffer& RequestBuffer(u32 slot) const; + void QueueBuffer(u32 slot, BufferTransformFlags transform); + boost::optional<const Buffer&> AcquireBuffer(); + void ReleaseBuffer(u32 slot); + u32 Query(QueryType type); + void SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event); + + u32 GetId() const { + return id; + } + + Kernel::SharedPtr<Kernel::Event> GetNativeHandle() const { + return native_handle; + } + +private: + u32 id; + u64 layer_id; + + std::vector<Buffer> queue; + Kernel::SharedPtr<Kernel::Event> native_handle; + + /// Used to signal waiting thread when no buffers are available + Kernel::SharedPtr<Kernel::Event> buffer_wait_event; +}; + +} // namespace NVFlinger +} // namespace Service diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp new file mode 100644 index 000000000..0d30f54dc --- /dev/null +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -0,0 +1,166 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> + +#include "common/alignment.h" +#include "common/scope_exit.h" +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" +#include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/nvflinger/buffer_queue.h" +#include "core/hle/service/nvflinger/nvflinger.h" +#include "video_core/renderer_base.h" +#include "video_core/video_core.h" + +namespace Service { +namespace NVFlinger { + +constexpr size_t SCREEN_REFRESH_RATE = 60; +constexpr u64 frame_ticks = static_cast<u64>(BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); + +NVFlinger::NVFlinger() { + // Add the different displays to the list of displays. + Display default_{0, "Default"}; + Display external{1, "External"}; + Display edid{2, "Edid"}; + Display internal{3, "Internal"}; + + displays.emplace_back(default_); + displays.emplace_back(external); + displays.emplace_back(edid); + displays.emplace_back(internal); + + // Schedule the screen composition events + composition_event = + CoreTiming::RegisterEvent("ScreenCompositioin", [this](u64 userdata, int cycles_late) { + Compose(); + CoreTiming::ScheduleEvent(frame_ticks - cycles_late, composition_event); + }); + + CoreTiming::ScheduleEvent(frame_ticks, composition_event); +} + +NVFlinger::~NVFlinger() { + CoreTiming::UnscheduleEvent(composition_event, 0); +} + +u64 NVFlinger::OpenDisplay(const std::string& name) { + LOG_WARNING(Service, "Opening display %s", name.c_str()); + + // TODO(Subv): Currently we only support the Default display. + ASSERT(name == "Default"); + + auto itr = std::find_if(displays.begin(), displays.end(), + [&](const Display& display) { return display.name == name; }); + + ASSERT(itr != displays.end()); + + return itr->id; +} + +u64 NVFlinger::CreateLayer(u64 display_id) { + auto& display = GetDisplay(display_id); + + ASSERT_MSG(display.layers.empty(), "Only one layer is supported per display at the moment"); + + u64 layer_id = next_layer_id++; + u32 buffer_queue_id = next_buffer_queue_id++; + auto buffer_queue = std::make_shared<BufferQueue>(buffer_queue_id, layer_id); + display.layers.emplace_back(layer_id, buffer_queue); + buffer_queues.emplace_back(std::move(buffer_queue)); + return layer_id; +} + +u32 NVFlinger::GetBufferQueueId(u64 display_id, u64 layer_id) { + const auto& layer = GetLayer(display_id, layer_id); + return layer.buffer_queue->GetId(); +} + +Kernel::SharedPtr<Kernel::Event> NVFlinger::GetVsyncEvent(u64 display_id) { + const auto& display = GetDisplay(display_id); + return display.vsync_event; +} + +std::shared_ptr<BufferQueue> NVFlinger::GetBufferQueue(u32 id) const { + auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(), + [&](const auto& queue) { return queue->GetId() == id; }); + + ASSERT(itr != buffer_queues.end()); + return *itr; +} + +Display& NVFlinger::GetDisplay(u64 display_id) { + auto itr = std::find_if(displays.begin(), displays.end(), + [&](const Display& display) { return display.id == display_id; }); + + ASSERT(itr != displays.end()); + return *itr; +} + +Layer& NVFlinger::GetLayer(u64 display_id, u64 layer_id) { + auto& display = GetDisplay(display_id); + + auto itr = std::find_if(display.layers.begin(), display.layers.end(), + [&](const Layer& layer) { return layer.id == layer_id; }); + + ASSERT(itr != display.layers.end()); + return *itr; +} + +void NVFlinger::Compose() { + for (auto& display : displays) { + // Trigger vsync for this display at the end of drawing + SCOPE_EXIT({ display.vsync_event->Signal(); }); + + // Don't do anything for displays without layers. + if (display.layers.empty()) + continue; + + // TODO(Subv): Support more than 1 layer. + ASSERT_MSG(display.layers.size() == 1, "Max 1 layer per display is supported"); + + Layer& layer = display.layers[0]; + auto& buffer_queue = layer.buffer_queue; + + // Search for a queued buffer and acquire it + auto buffer = buffer_queue->AcquireBuffer(); + + if (buffer == boost::none) { + // There was no queued buffer to draw, render previous frame + Core::System::GetInstance().perf_stats.EndGameFrame(); + VideoCore::g_renderer->SwapBuffers({}); + continue; + } + + auto& igbp_buffer = buffer->igbp_buffer; + + // Now send the buffer to the GPU for drawing. + auto nvdrv = Nvidia::nvdrv.lock(); + ASSERT(nvdrv); + + // TODO(Subv): Support more than just disp0. The display device selection is probably based + // on which display we're drawing (Default, Internal, External, etc) + auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0"); + ASSERT(nvdisp); + + nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format, + igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, buffer->transform); + + buffer_queue->ReleaseBuffer(buffer->slot); + + // TODO(Subv): Figure out when we should actually signal this event. + buffer_queue->GetNativeHandle()->Signal(); + } +} + +Layer::Layer(u64 id, std::shared_ptr<BufferQueue> queue) : id(id), buffer_queue(std::move(queue)) {} + +Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) { + vsync_event = Kernel::Event::Create(Kernel::ResetType::Pulse, "Display VSync Event"); +} + +} // namespace NVFlinger +} // namespace Service diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h new file mode 100644 index 000000000..3126018ad --- /dev/null +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -0,0 +1,84 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <boost/optional.hpp> +#include "core/hle/kernel/event.h" + +namespace CoreTiming { +struct EventType; +} + +namespace Service { +namespace NVFlinger { + +class BufferQueue; + +struct Layer { + Layer(u64 id, std::shared_ptr<BufferQueue> queue); + ~Layer() = default; + + u64 id; + std::shared_ptr<BufferQueue> buffer_queue; +}; + +struct Display { + Display(u64 id, std::string name); + ~Display() = default; + + u64 id; + std::string name; + + std::vector<Layer> layers; + Kernel::SharedPtr<Kernel::Event> vsync_event; +}; + +class NVFlinger final { +public: + NVFlinger(); + ~NVFlinger(); + + /// Opens the specified display and returns the id. + u64 OpenDisplay(const std::string& name); + + /// Creates a layer on the specified display and returns the layer id. + u64 CreateLayer(u64 display_id); + + /// Gets the buffer queue id of the specified layer in the specified display. + u32 GetBufferQueueId(u64 display_id, u64 layer_id); + + /// Gets the vsync event for the specified display. + Kernel::SharedPtr<Kernel::Event> GetVsyncEvent(u64 display_id); + + /// Obtains a buffer queue identified by the id. + std::shared_ptr<BufferQueue> GetBufferQueue(u32 id) const; + + /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when + /// finished. + void Compose(); + +private: + /// Returns the display identified by the specified id. + Display& GetDisplay(u64 display_id); + + /// Returns the layer identified by the specified id in the desired display. + Layer& GetLayer(u64 display_id, u64 layer_id); + + std::vector<Display> displays; + std::vector<std::shared_ptr<BufferQueue>> buffer_queues; + + /// Id to use for the next layer that is created, this counter is shared among all displays. + u64 next_layer_id = 1; + /// Id to use for the next buffer queue that is created, this counter is shared among all + /// layers. + u32 next_buffer_queue_id = 1; + + /// CoreTiming event that handles screen composition. + CoreTiming::EventType* composition_event; +}; + +} // namespace NVFlinger +} // namespace Service diff --git a/src/core/hle/service/pctl/pctl_a.cpp b/src/core/hle/service/pctl/pctl_a.cpp index 7978aecb8..c65fffa07 100644 --- a/src/core/hle/service/pctl/pctl_a.cpp +++ b/src/core/hle/service/pctl/pctl_a.cpp @@ -15,10 +15,10 @@ public: }; void PCTL_A::GetService(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IParentalControlService>(); - LOG_DEBUG(Service, "called"); + LOG_DEBUG(Service_PCTL, "called"); } PCTL_A::PCTL_A() : ServiceFramework("pctl:a") { diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index fe76b381c..b224b89da 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -7,6 +7,7 @@ #include "common/assert.h" #include "common/logging/log.h" #include "common/string_util.h" +#include "core/core.h" #include "core/hle/ipc.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/client_port.h" @@ -19,13 +20,22 @@ #include "core/hle/service/aoc/aoc_u.h" #include "core/hle/service/apm/apm.h" #include "core/hle/service/audio/audio.h" +#include "core/hle/service/fatal/fatal.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/friend/friend.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/lm/lm.h" +#include "core/hle/service/nifm/nifm.h" +#include "core/hle/service/ns/ns.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/pctl/pctl.h" #include "core/hle/service/service.h" +#include "core/hle/service/set/settings.h" #include "core/hle/service/sm/controller.h" #include "core/hle/service/sm/sm.h" +#include "core/hle/service/sockets/sockets.h" +#include "core/hle/service/spl/module.h" +#include "core/hle/service/ssl/ssl.h" #include "core/hle/service/time/time.h" #include "core/hle/service/vi/vi.h" @@ -129,7 +139,7 @@ void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) { ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& context) { switch (context.GetCommandType()) { case IPC::CommandType::Close: { - IPC::RequestBuilder rb{context, 1}; + IPC::ResponseBuilder rb{context, 2}; rb.Push(RESULT_SUCCESS); return ResultCode(ErrorModule::HIPC, ErrorDescription::RemoteProcessDead); } @@ -142,12 +152,10 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co break; } default: - UNIMPLEMENTED_MSG("command_type=%d", context.GetCommandType()); + UNIMPLEMENTED_MSG("command_type=%d", static_cast<int>(context.GetCommandType())); } - u32* cmd_buf = (u32*)Memory::GetPointer(Kernel::GetCurrentThread()->GetTLSAddress()); - context.WriteToOutgoingCommandBuffer(cmd_buf, *Kernel::g_current_process, - Kernel::g_handle_table); + context.WriteToOutgoingCommandBuffer(*Kernel::GetCurrentThread()); return RESULT_SUCCESS; } @@ -162,20 +170,33 @@ void AddNamedPort(std::string name, SharedPtr<ClientPort> port) { /// Initialize ServiceManager void Init() { + // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it + // here and pass it into the respective InstallInterfaces functions. + auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(); + SM::g_service_manager = std::make_shared<SM::ServiceManager>(); SM::ServiceManager::InstallInterfaces(SM::g_service_manager); Account::InstallInterfaces(*SM::g_service_manager); - AM::InstallInterfaces(*SM::g_service_manager); + AM::InstallInterfaces(*SM::g_service_manager, nv_flinger); AOC::InstallInterfaces(*SM::g_service_manager); APM::InstallInterfaces(*SM::g_service_manager); Audio::InstallInterfaces(*SM::g_service_manager); + Fatal::InstallInterfaces(*SM::g_service_manager); + FileSystem::InstallInterfaces(*SM::g_service_manager); + Friend::InstallInterfaces(*SM::g_service_manager); HID::InstallInterfaces(*SM::g_service_manager); LM::InstallInterfaces(*SM::g_service_manager); + NIFM::InstallInterfaces(*SM::g_service_manager); + NS::InstallInterfaces(*SM::g_service_manager); Nvidia::InstallInterfaces(*SM::g_service_manager); PCTL::InstallInterfaces(*SM::g_service_manager); + Sockets::InstallInterfaces(*SM::g_service_manager); + SPL::InstallInterfaces(*SM::g_service_manager); + SSL::InstallInterfaces(*SM::g_service_manager); Time::InstallInterfaces(*SM::g_service_manager); - VI::InstallInterfaces(*SM::g_service_manager); + VI::InstallInterfaces(*SM::g_service_manager, nv_flinger); + Set::InstallInterfaces(*SM::g_service_manager); LOG_DEBUG(Service, "initialized OK"); } diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 8e1c5b399..9c2e826da 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -21,7 +21,7 @@ class ClientPort; class ServerPort; class ServerSession; class HLERequestContext; -} +} // namespace Kernel namespace Service { @@ -189,4 +189,4 @@ extern std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_ /// Adds a port to the named port table void AddNamedPort(std::string name, Kernel::SharedPtr<Kernel::ClientPort> port); -} // namespace +} // namespace Service diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp new file mode 100644 index 000000000..aa7c924e7 --- /dev/null +++ b/src/core/hle/service/set/set.cpp @@ -0,0 +1,44 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <chrono> +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/client_port.h" +#include "core/hle/kernel/client_session.h" +#include "core/hle/service/set/set.h" + +namespace Service { +namespace Set { + +void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + u32 id = rp.Pop<u32>(); + constexpr std::array<u8, 13> lang_codes{}; + + ctx.WriteBuffer(lang_codes.data(), lang_codes.size()); + + IPC::ResponseBuilder rb{ctx, 2}; + + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_SET, "(STUBBED) called"); +} + +SET::SET() : ServiceFramework("set") { + static const FunctionInfo functions[] = { + {0, nullptr, "GetLanguageCode"}, + {1, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"}, + {2, nullptr, "MakeLanguageCode"}, + {3, nullptr, "GetAvailableLanguageCodeCount"}, + {4, nullptr, "GetRegionCode"}, + {5, nullptr, "GetAvailableLanguageCodes2"}, + {6, nullptr, "GetAvailableLanguageCodeCount2"}, + {7, nullptr, "GetKeyCodeMap"}, + }; + RegisterHandlers(functions); +} + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h new file mode 100644 index 000000000..7b7814ed1 --- /dev/null +++ b/src/core/hle/service/set/set.h @@ -0,0 +1,22 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace Set { + +class SET final : public ServiceFramework<SET> { +public: + explicit SET(); + ~SET() = default; + +private: + void GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx); +}; + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp new file mode 100644 index 000000000..6231acd96 --- /dev/null +++ b/src/core/hle/service/set/set_cal.cpp @@ -0,0 +1,40 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/set/set_cal.h" + +namespace Service { +namespace Set { + +SET_CAL::SET_CAL() : ServiceFramework("set:cal") { + static const FunctionInfo functions[] = { + {0, nullptr, "GetBluetoothBdAddress"}, + {1, nullptr, "GetConfigurationId1"}, + {2, nullptr, "GetAccelerometerOffset"}, + {3, nullptr, "GetAccelerometerScale"}, + {4, nullptr, "GetGyroscopeOffset"}, + {5, nullptr, "GetGyroscopeScale"}, + {6, nullptr, "GetWirelessLanMacAddress"}, + {7, nullptr, "GetWirelessLanCountryCodeCount"}, + {8, nullptr, "GetWirelessLanCountryCodes"}, + {9, nullptr, "GetSerialNumber"}, + {10, nullptr, "SetInitialSystemAppletProgramId"}, + {11, nullptr, "SetOverlayDispProgramId"}, + {12, nullptr, "GetBatteryLot"}, + {14, nullptr, "GetEciDeviceCertificate"}, + {15, nullptr, "GetEticketDeviceCertificate"}, + {16, nullptr, "GetSslKey"}, + {17, nullptr, "GetSslCertificate"}, + {18, nullptr, "GetGameCardKey"}, + {19, nullptr, "GetGameCardCertificate"}, + {20, nullptr, "GetEciDeviceKey"}, + {21, nullptr, "GetEticketDeviceKey"}, + {22, nullptr, "GetSpeakerParameter"}, + {23, nullptr, "GetLcdVendorId"}, + }; + RegisterHandlers(functions); +} + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/set_cal.h b/src/core/hle/service/set/set_cal.h new file mode 100644 index 000000000..9c0b851d0 --- /dev/null +++ b/src/core/hle/service/set/set_cal.h @@ -0,0 +1,19 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace Set { + +class SET_CAL final : public ServiceFramework<SET_CAL> { +public: + explicit SET_CAL(); + ~SET_CAL() = default; +}; + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/set_fd.cpp b/src/core/hle/service/set/set_fd.cpp new file mode 100644 index 000000000..8320d4250 --- /dev/null +++ b/src/core/hle/service/set/set_fd.cpp @@ -0,0 +1,25 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/set/set_fd.h" + +namespace Service { +namespace Set { + +SET_FD::SET_FD() : ServiceFramework("set:fd") { + static const FunctionInfo functions[] = { + {2, nullptr, "SetSettingsItemValue"}, + {3, nullptr, "ResetSettingsItemValue"}, + {4, nullptr, "CreateSettingsItemKeyIterator"}, + {10, nullptr, "ReadSettings"}, + {11, nullptr, "ResetSettings"}, + {20, nullptr, "SetWebInspectorFlag"}, + {21, nullptr, "SetAllowedSslHosts"}, + {22, nullptr, "SetHostFsMountPoint"}, + }; + RegisterHandlers(functions); +} + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/set_fd.h b/src/core/hle/service/set/set_fd.h new file mode 100644 index 000000000..65b36bcb3 --- /dev/null +++ b/src/core/hle/service/set/set_fd.h @@ -0,0 +1,19 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace Set { + +class SET_FD final : public ServiceFramework<SET_FD> { +public: + explicit SET_FD(); + ~SET_FD() = default; +}; + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp new file mode 100644 index 000000000..363abd10a --- /dev/null +++ b/src/core/hle/service/set/set_sys.cpp @@ -0,0 +1,167 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/client_port.h" +#include "core/hle/service/set/set_sys.h" + +namespace Service { +namespace Set { + +void SET_SYS::GetColorSetId(Kernel::HLERequestContext& ctx) { + + IPC::ResponseBuilder rb{ctx, 3}; + + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); + + LOG_WARNING(Service_SET, "(STUBBED) called"); +} + +SET_SYS::SET_SYS() : ServiceFramework("set:sys") { + static const FunctionInfo functions[] = { + {0, nullptr, "SetLanguageCode"}, + {1, nullptr, "SetNetworkSettings"}, + {2, nullptr, "GetNetworkSettings"}, + {3, nullptr, "GetFirmwareVersion"}, + {4, nullptr, "GetFirmwareVersion2"}, + {7, nullptr, "GetLockScreenFlag"}, + {8, nullptr, "SetLockScreenFlag"}, + {9, nullptr, "GetBacklightSettings"}, + {10, nullptr, "SetBacklightSettings"}, + {11, nullptr, "SetBluetoothDevicesSettings"}, + {12, nullptr, "GetBluetoothDevicesSettings"}, + {13, nullptr, "GetExternalSteadyClockSourceId"}, + {14, nullptr, "SetExternalSteadyClockSourceId"}, + {15, nullptr, "GetUserSystemClockContext"}, + {16, nullptr, "SetUserSystemClockContext"}, + {17, nullptr, "GetAccountSettings"}, + {18, nullptr, "SetAccountSettings"}, + {19, nullptr, "GetAudioVolume"}, + {20, nullptr, "SetAudioVolume"}, + {21, nullptr, "GetEulaVersions"}, + {22, nullptr, "SetEulaVersions"}, + {23, &SET_SYS::GetColorSetId, "GetColorSetId"}, + {24, nullptr, "SetColorSetId"}, + {25, nullptr, "GetConsoleInformationUploadFlag"}, + {26, nullptr, "SetConsoleInformationUploadFlag"}, + {27, nullptr, "GetAutomaticApplicationDownloadFlag"}, + {28, nullptr, "SetAutomaticApplicationDownloadFlag"}, + {29, nullptr, "GetNotificationSettings"}, + {30, nullptr, "SetNotificationSettings"}, + {31, nullptr, "GetAccountNotificationSettings"}, + {32, nullptr, "SetAccountNotificationSettings"}, + {35, nullptr, "GetVibrationMasterVolume"}, + {36, nullptr, "SetVibrationMasterVolume"}, + {37, nullptr, "GetSettingsItemValueSize"}, + {38, nullptr, "GetSettingsItemValue"}, + {39, nullptr, "GetTvSettings"}, + {40, nullptr, "SetTvSettings"}, + {41, nullptr, "GetEdid"}, + {42, nullptr, "SetEdid"}, + {43, nullptr, "GetAudioOutputMode"}, + {44, nullptr, "SetAudioOutputMode"}, + {45, nullptr, "IsForceMuteOnHeadphoneRemoved"}, + {46, nullptr, "SetForceMuteOnHeadphoneRemoved"}, + {47, nullptr, "GetQuestFlag"}, + {48, nullptr, "SetQuestFlag"}, + {49, nullptr, "GetDataDeletionSettings"}, + {50, nullptr, "SetDataDeletionSettings"}, + {51, nullptr, "GetInitialSystemAppletProgramId"}, + {52, nullptr, "GetOverlayDispProgramId"}, + {53, nullptr, "GetDeviceTimeZoneLocationName"}, + {54, nullptr, "SetDeviceTimeZoneLocationName"}, + {55, nullptr, "GetWirelessCertificationFileSize"}, + {56, nullptr, "GetWirelessCertificationFile"}, + {57, nullptr, "SetRegionCode"}, + {58, nullptr, "GetNetworkSystemClockContext"}, + {59, nullptr, "SetNetworkSystemClockContext"}, + {60, nullptr, "IsUserSystemClockAutomaticCorrectionEnabled"}, + {61, nullptr, "SetUserSystemClockAutomaticCorrectionEnabled"}, + {62, nullptr, "GetDebugModeFlag"}, + {63, nullptr, "GetPrimaryAlbumStorage"}, + {64, nullptr, "SetPrimaryAlbumStorage"}, + {65, nullptr, "GetUsb30EnableFlag"}, + {66, nullptr, "SetUsb30EnableFlag"}, + {67, nullptr, "GetBatteryLot"}, + {68, nullptr, "GetSerialNumber"}, + {69, nullptr, "GetNfcEnableFlag"}, + {70, nullptr, "SetNfcEnableFlag"}, + {71, nullptr, "GetSleepSettings"}, + {72, nullptr, "SetSleepSettings"}, + {73, nullptr, "GetWirelessLanEnableFlag"}, + {74, nullptr, "SetWirelessLanEnableFlag"}, + {75, nullptr, "GetInitialLaunchSettings"}, + {76, nullptr, "SetInitialLaunchSettings"}, + {77, nullptr, "GetDeviceNickName"}, + {78, nullptr, "SetDeviceNickName"}, + {79, nullptr, "GetProductModel"}, + {80, nullptr, "GetLdnChannel"}, + {81, nullptr, "SetLdnChannel"}, + {82, nullptr, "AcquireTelemetryDirtyFlagEventHandle"}, + {83, nullptr, "GetTelemetryDirtyFlags"}, + {84, nullptr, "GetPtmBatteryLot"}, + {85, nullptr, "SetPtmBatteryLot"}, + {86, nullptr, "GetPtmFuelGaugeParameter"}, + {87, nullptr, "SetPtmFuelGaugeParameter"}, + {88, nullptr, "GetBluetoothEnableFlag"}, + {89, nullptr, "SetBluetoothEnableFlag"}, + {90, nullptr, "GetMiiAuthorId"}, + {91, nullptr, "SetShutdownRtcValue"}, + {92, nullptr, "GetShutdownRtcValue"}, + {93, nullptr, "AcquireFatalDirtyFlagEventHandle"}, + {94, nullptr, "GetFatalDirtyFlags"}, + {95, nullptr, "GetAutoUpdateEnableFlag"}, + {96, nullptr, "SetAutoUpdateEnableFlag"}, + {97, nullptr, "GetNxControllerSettings"}, + {98, nullptr, "SetNxControllerSettings"}, + {99, nullptr, "GetBatteryPercentageFlag"}, + {100, nullptr, "SetBatteryPercentageFlag"}, + {101, nullptr, "GetExternalRtcResetFlag"}, + {102, nullptr, "SetExternalRtcResetFlag"}, + {103, nullptr, "GetUsbFullKeyEnableFlag"}, + {104, nullptr, "SetUsbFullKeyEnableFlag"}, + {105, nullptr, "SetExternalSteadyClockInternalOffset"}, + {106, nullptr, "GetExternalSteadyClockInternalOffset"}, + {107, nullptr, "GetBacklightSettingsEx"}, + {108, nullptr, "SetBacklightSettingsEx"}, + {109, nullptr, "GetHeadphoneVolumeWarningCount"}, + {110, nullptr, "SetHeadphoneVolumeWarningCount"}, + {111, nullptr, "GetBluetoothAfhEnableFlag"}, + {112, nullptr, "SetBluetoothAfhEnableFlag"}, + {113, nullptr, "GetBluetoothBoostEnableFlag"}, + {114, nullptr, "SetBluetoothBoostEnableFlag"}, + {115, nullptr, "GetInRepairProcessEnableFlag"}, + {116, nullptr, "SetInRepairProcessEnableFlag"}, + {117, nullptr, "GetHeadphoneVolumeUpdateFlag"}, + {118, nullptr, "SetHeadphoneVolumeUpdateFlag"}, + {119, nullptr, "NeedsToUpdateHeadphoneVolume"}, + {120, nullptr, "GetPushNotificationActivityModeOnSleep"}, + {121, nullptr, "SetPushNotificationActivityModeOnSleep"}, + {122, nullptr, "GetServiceDiscoveryControlSettings"}, + {123, nullptr, "SetServiceDiscoveryControlSettings"}, + {124, nullptr, "GetErrorReportSharePermission"}, + {125, nullptr, "SetErrorReportSharePermission"}, + {126, nullptr, "GetAppletLaunchFlags"}, + {127, nullptr, "SetAppletLaunchFlags"}, + {128, nullptr, "GetConsoleSixAxisSensorAccelerationBias"}, + {129, nullptr, "SetConsoleSixAxisSensorAccelerationBias"}, + {130, nullptr, "GetConsoleSixAxisSensorAngularVelocityBias"}, + {131, nullptr, "SetConsoleSixAxisSensorAngularVelocityBias"}, + {132, nullptr, "GetConsoleSixAxisSensorAccelerationGain"}, + {133, nullptr, "SetConsoleSixAxisSensorAccelerationGain"}, + {134, nullptr, "GetConsoleSixAxisSensorAngularVelocityGain"}, + {135, nullptr, "SetConsoleSixAxisSensorAngularVelocityGain"}, + {136, nullptr, "GetKeyboardLayout"}, + {137, nullptr, "SetKeyboardLayout"}, + {138, nullptr, "GetWebInspectorFlag"}, + {139, nullptr, "GetAllowedSslHosts"}, + {140, nullptr, "GetHostFsMountPoint"}, + }; + RegisterHandlers(functions); +} + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h new file mode 100644 index 000000000..105f1a3c7 --- /dev/null +++ b/src/core/hle/service/set/set_sys.h @@ -0,0 +1,22 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace Set { + +class SET_SYS final : public ServiceFramework<SET_SYS> { +public: + explicit SET_SYS(); + ~SET_SYS() = default; + +private: + void GetColorSetId(Kernel::HLERequestContext& ctx); +}; + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/settings.cpp b/src/core/hle/service/set/settings.cpp new file mode 100644 index 000000000..c6bc9e240 --- /dev/null +++ b/src/core/hle/service/set/settings.cpp @@ -0,0 +1,22 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/set/set.h" +#include "core/hle/service/set/set_cal.h" +#include "core/hle/service/set/set_fd.h" +#include "core/hle/service/set/set_sys.h" +#include "core/hle/service/set/settings.h" + +namespace Service { +namespace Set { + +void InstallInterfaces(SM::ServiceManager& service_manager) { + std::make_shared<SET>()->InstallAsService(service_manager); + std::make_shared<SET_CAL>()->InstallAsService(service_manager); + std::make_shared<SET_FD>()->InstallAsService(service_manager); + std::make_shared<SET_SYS>()->InstallAsService(service_manager); +} + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/set/settings.h b/src/core/hle/service/set/settings.h new file mode 100644 index 000000000..6c8d5a58c --- /dev/null +++ b/src/core/hle/service/set/settings.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace Set { + +/// Registers all Settings services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace Set +} // namespace Service diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp index 7b1c8ee37..e12c53442 100644 --- a/src/core/hle/service/sm/controller.cpp +++ b/src/core/hle/service/sm/controller.cpp @@ -4,42 +4,43 @@ #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/domain.h" +#include "core/hle/kernel/session.h" #include "core/hle/service/sm/controller.h" namespace Service { namespace SM { void Controller::ConvertSessionToDomain(Kernel::HLERequestContext& ctx) { - auto domain = Kernel::Domain::CreateFromSession(*ctx.ServerSession()->parent).Unwrap(); + ASSERT_MSG(!ctx.Session()->IsDomain(), "session is alread a domain"); + ctx.Session()->ConvertToDomain(); - IPC::RequestBuilder rb{ctx, 3}; + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(static_cast<u32>(domain->request_handlers.size())); + rb.Push<u32>(1); // Converted sessions start with 1 request handler - LOG_DEBUG(Service, "called, domain=%d", domain->GetObjectId()); + LOG_DEBUG(Service, "called, server_session=%d", ctx.Session()->GetObjectId()); } void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 1}; + // TODO(bunnei): This is just creating a new handle to the same Session. I assume this is wrong + // and that we probably want to actually make an entirely new Session, but we still need to + // verify this on hardware. + IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; rb.Push(RESULT_SUCCESS); - // TODO(Subv): Check if this is correct - if (ctx.IsDomain()) - rb.PushMoveObjects(ctx.Domain()); - else - rb.PushMoveObjects(ctx.ServerSession()); + Kernel::SharedPtr<Kernel::ClientSession> session{ctx.Session()->parent->client}; + rb.PushMoveObjects(session); - LOG_DEBUG(Service, "called"); + LOG_DEBUG(Service, "called, session=%u", session->GetObjectId()); } void Controller::DuplicateSessionEx(Kernel::HLERequestContext& ctx) { - DuplicateSession(ctx); - LOG_WARNING(Service, "(STUBBED) called, using DuplicateSession"); + + DuplicateSession(ctx); } void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 3}; + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(0x500); diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index f3bffac54..bc72512a0 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -83,7 +83,7 @@ std::shared_ptr<ServiceManager> g_service_manager; * 0: ResultCode */ void SM::Initialize(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 1}; + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); LOG_DEBUG(Service_SM, "called"); } @@ -99,10 +99,13 @@ void SM::GetService(Kernel::HLERequestContext& ctx) { auto client_port = service_manager->GetServicePort(name); if (client_port.Failed()) { - IPC::RequestBuilder rb = rp.MakeBuilder(2, 0, 0, 0); + IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); rb.Push(client_port.Code()); LOG_ERROR(Service_SM, "called service=%s -> error 0x%08X", name.c_str(), client_port.Code().raw); + if (name.length() == 0) + return; // LibNX Fix + UNIMPLEMENTED(); return; } @@ -111,7 +114,8 @@ void SM::GetService(Kernel::HLERequestContext& ctx) { if (session.Succeeded()) { LOG_DEBUG(Service_SM, "called service=%s -> session=%u", name.c_str(), (*session)->GetObjectId()); - IPC::RequestBuilder rb = rp.MakeBuilder(2, 0, 1, 0); + IPC::ResponseBuilder rb = + rp.MakeBuilder(2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles); rb.Push(session.Code()); rb.PushMoveObjects(std::move(session).Unwrap()); } diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp new file mode 100644 index 000000000..790ff82b3 --- /dev/null +++ b/src/core/hle/service/sockets/bsd.cpp @@ -0,0 +1,90 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/sockets/bsd.h" + +namespace Service { +namespace Sockets { + +void BSD::RegisterClient(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); // bsd errno +} + +void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); // bsd errno +} + +void BSD::Socket(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + u32 domain = rp.Pop<u32>(); + u32 type = rp.Pop<u32>(); + u32 protocol = rp.Pop<u32>(); + + LOG_WARNING(Service, "(STUBBED) called domain=%u type=%u protocol=%u", domain, type, protocol); + + u32 fd = next_fd++; + + IPC::ResponseBuilder rb{ctx, 4}; + + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(fd); + rb.Push<u32>(0); // bsd errno +} + +void BSD::Connect(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 4}; + + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); // ret + rb.Push<u32>(0); // bsd errno +} + +void BSD::SendTo(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 4}; + + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); // ret + rb.Push<u32>(0); // bsd errno +} + +void BSD::Close(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 4}; + + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); // ret + rb.Push<u32>(0); // bsd errno +} + +BSD::BSD(const char* name) : ServiceFramework(name) { + static const FunctionInfo functions[] = { + {0, &BSD::RegisterClient, "RegisterClient"}, + {1, &BSD::StartMonitoring, "StartMonitoring"}, + {2, &BSD::Socket, "Socket"}, + {11, &BSD::SendTo, "SendTo"}, + {14, &BSD::Connect, "Connect"}, + {26, &BSD::Close, "Close"}, + }; + RegisterHandlers(functions); +} + +} // namespace Sockets +} // namespace Service diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h new file mode 100644 index 000000000..32d949e95 --- /dev/null +++ b/src/core/hle/service/sockets/bsd.h @@ -0,0 +1,31 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/service.h" + +namespace Service { +namespace Sockets { + +class BSD final : public ServiceFramework<BSD> { +public: + explicit BSD(const char* name); + ~BSD() = default; + +private: + void RegisterClient(Kernel::HLERequestContext& ctx); + void StartMonitoring(Kernel::HLERequestContext& ctx); + void Socket(Kernel::HLERequestContext& ctx); + void Connect(Kernel::HLERequestContext& ctx); + void SendTo(Kernel::HLERequestContext& ctx); + void Close(Kernel::HLERequestContext& ctx); + + /// Id to use for the next open file descriptor. + u32 next_fd = 1; +}; + +} // namespace Sockets +} // namespace Service diff --git a/src/core/hle/service/sockets/nsd.cpp b/src/core/hle/service/sockets/nsd.cpp new file mode 100644 index 000000000..e3542d325 --- /dev/null +++ b/src/core/hle/service/sockets/nsd.cpp @@ -0,0 +1,34 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/sockets/nsd.h" + +namespace Service { +namespace Sockets { + +NSD::NSD(const char* name) : ServiceFramework(name) { + static const FunctionInfo functions[] = { + {10, nullptr, "GetSettingName"}, + {11, nullptr, "GetEnvironmentIdentifier"}, + {12, nullptr, "GetDeviceId"}, + {13, nullptr, "DeleteSettings"}, + {14, nullptr, "ImportSettings"}, + {20, nullptr, "Resolve"}, + {21, nullptr, "ResolveEx"}, + {30, nullptr, "GetNasServiceSetting"}, + {31, nullptr, "GetNasServiceSettingEx"}, + {40, nullptr, "GetNasRequestFqdn"}, + {41, nullptr, "GetNasRequestFqdnEx"}, + {42, nullptr, "GetNasApiFqdn"}, + {43, nullptr, "GetNasApiFqdnEx"}, + {50, nullptr, "GetCurrentSetting"}, + {60, nullptr, "ReadSaveDataFromFsForTest"}, + {61, nullptr, "WriteSaveDataToFsForTest"}, + {62, nullptr, "DeleteSaveDataOfFsForTest"}, + }; + RegisterHandlers(functions); +} + +} // namespace Sockets +} // namespace Service diff --git a/src/core/hle/service/sockets/nsd.h b/src/core/hle/service/sockets/nsd.h new file mode 100644 index 000000000..a7c15a860 --- /dev/null +++ b/src/core/hle/service/sockets/nsd.h @@ -0,0 +1,20 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/service.h" + +namespace Service { +namespace Sockets { + +class NSD final : public ServiceFramework<NSD> { +public: + explicit NSD(const char* name); + ~NSD() = default; +}; + +} // namespace Sockets +} // namespace Service diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp new file mode 100644 index 000000000..eb4b5fa57 --- /dev/null +++ b/src/core/hle/service/sockets/sfdnsres.cpp @@ -0,0 +1,38 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/sockets/sfdnsres.h" + +namespace Service { +namespace Sockets { + +void SFDNSRES::GetAddrInfo(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + LOG_WARNING(Service, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + + rb.Push(RESULT_SUCCESS); +} + +SFDNSRES::SFDNSRES() : ServiceFramework("sfdnsres") { + static const FunctionInfo functions[] = { + {0, nullptr, "SetDnsAddressesPrivate"}, + {1, nullptr, "GetDnsAddressPrivate"}, + {2, nullptr, "GetHostByName"}, + {3, nullptr, "GetHostByAddr"}, + {4, nullptr, "GetHostStringError"}, + {5, nullptr, "GetGaiStringError"}, + {6, &SFDNSRES::GetAddrInfo, "GetAddrInfo"}, + {7, nullptr, "GetNameInfo"}, + {8, nullptr, "RequestCancelHandle"}, + {9, nullptr, "CancelSocketCall"}, + }; + RegisterHandlers(functions); +} + +} // namespace Sockets +} // namespace Service diff --git a/src/core/hle/service/sockets/sfdnsres.h b/src/core/hle/service/sockets/sfdnsres.h new file mode 100644 index 000000000..c07cc1594 --- /dev/null +++ b/src/core/hle/service/sockets/sfdnsres.h @@ -0,0 +1,23 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/service.h" + +namespace Service { +namespace Sockets { + +class SFDNSRES final : public ServiceFramework<SFDNSRES> { +public: + explicit SFDNSRES(); + ~SFDNSRES() = default; + +private: + void GetAddrInfo(Kernel::HLERequestContext& ctx); +}; + +} // namespace Sockets +} // namespace Service diff --git a/src/core/hle/service/sockets/sockets.cpp b/src/core/hle/service/sockets/sockets.cpp new file mode 100644 index 000000000..cedc276d9 --- /dev/null +++ b/src/core/hle/service/sockets/sockets.cpp @@ -0,0 +1,22 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/sockets/bsd.h" +#include "core/hle/service/sockets/nsd.h" +#include "core/hle/service/sockets/sfdnsres.h" +#include "core/hle/service/sockets/sockets.h" + +namespace Service { +namespace Sockets { + +void InstallInterfaces(SM::ServiceManager& service_manager) { + std::make_shared<BSD>("bsd:s")->InstallAsService(service_manager); + std::make_shared<BSD>("bsd:u")->InstallAsService(service_manager); + std::make_shared<NSD>("nsd:a")->InstallAsService(service_manager); + std::make_shared<NSD>("nsd:u")->InstallAsService(service_manager); + std::make_shared<SFDNSRES>()->InstallAsService(service_manager); +} + +} // namespace Sockets +} // namespace Service diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h new file mode 100644 index 000000000..7e89c8d2c --- /dev/null +++ b/src/core/hle/service/sockets/sockets.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace Sockets { + +/// Registers all Sockets services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace Sockets +} // namespace Service diff --git a/src/core/hle/service/spl/csrng.cpp b/src/core/hle/service/spl/csrng.cpp new file mode 100644 index 000000000..cde05717a --- /dev/null +++ b/src/core/hle/service/spl/csrng.cpp @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/spl/csrng.h" + +namespace Service { +namespace SPL { + +CSRNG::CSRNG(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "csrng") { + static const FunctionInfo functions[] = { + {0, &CSRNG::GetRandomBytes, "GetRandomBytes"}, + }; + RegisterHandlers(functions); +} + +} // namespace SPL +} // namespace Service diff --git a/src/core/hle/service/spl/csrng.h b/src/core/hle/service/spl/csrng.h new file mode 100644 index 000000000..59ca794dd --- /dev/null +++ b/src/core/hle/service/spl/csrng.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/spl/module.h" + +namespace Service { +namespace SPL { + +class CSRNG final : public Module::Interface { +public: + explicit CSRNG(std::shared_ptr<Module> module); +}; + +} // namespace SPL +} // namespace Service diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp new file mode 100644 index 000000000..fc1bcd94c --- /dev/null +++ b/src/core/hle/service/spl/module.cpp @@ -0,0 +1,42 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <cstdlib> +#include <vector> +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/spl/csrng.h" +#include "core/hle/service/spl/module.h" +#include "core/hle/service/spl/spl.h" + +namespace Service { +namespace SPL { + +Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) + : ServiceFramework(name), module(std::move(module)) {} + +void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + size_t size = ctx.GetWriteBufferSize(); + + std::vector<u8> data(size); + std::generate(data.begin(), data.end(), std::rand); + + ctx.WriteBuffer(data); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_DEBUG(Service_SPL, "called"); +} + +void InstallInterfaces(SM::ServiceManager& service_manager) { + auto module = std::make_shared<Module>(); + std::make_shared<CSRNG>(module)->InstallAsService(service_manager); + std::make_shared<SPL>(module)->InstallAsService(service_manager); +} + +} // namespace SPL +} // namespace Service diff --git a/src/core/hle/service/spl/module.h b/src/core/hle/service/spl/module.h new file mode 100644 index 000000000..12cdb2980 --- /dev/null +++ b/src/core/hle/service/spl/module.h @@ -0,0 +1,29 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace SPL { + +class Module final { +public: + class Interface : public ServiceFramework<Interface> { + public: + Interface(std::shared_ptr<Module> module, const char* name); + + void GetRandomBytes(Kernel::HLERequestContext& ctx); + + protected: + std::shared_ptr<Module> module; + }; +}; + +/// Registers all SPL services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace SPL +} // namespace Service diff --git a/src/core/hle/service/spl/spl.cpp b/src/core/hle/service/spl/spl.cpp new file mode 100644 index 000000000..deab29b91 --- /dev/null +++ b/src/core/hle/service/spl/spl.cpp @@ -0,0 +1,41 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/spl/spl.h" + +namespace Service { +namespace SPL { + +SPL::SPL(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "spl:") { + static const FunctionInfo functions[] = { + {0, nullptr, "GetConfig"}, + {1, nullptr, "UserExpMod"}, + {2, nullptr, "GenerateAesKek"}, + {3, nullptr, "LoadAesKey"}, + {4, nullptr, "GenerateAesKey"}, + {5, nullptr, "SetConfig"}, + {7, &SPL::GetRandomBytes, "GetRandomBytes"}, + {9, nullptr, "LoadSecureExpModKey"}, + {10, nullptr, "SecureExpMod"}, + {11, nullptr, "IsDevelopment"}, + {12, nullptr, "GenerateSpecificAesKey"}, + {13, nullptr, "DecryptPrivk"}, + {14, nullptr, "DecryptAesKey"}, + {15, nullptr, "DecryptAesCtr"}, + {16, nullptr, "ComputeCmac"}, + {17, nullptr, "LoadRsaOaepKey"}, + {18, nullptr, "UnwrapRsaOaepWrappedTitleKey"}, + {19, nullptr, "LoadTitleKey"}, + {20, nullptr, "UnwrapAesWrappedTitleKey"}, + {21, nullptr, "LockAesEngine"}, + {22, nullptr, "UnlockAesEngine"}, + {23, nullptr, "GetSplWaitEvent"}, + {24, nullptr, "SetSharedData"}, + {25, nullptr, "GetSharedData"}, + }; + RegisterHandlers(functions); +} + +} // namespace SPL +} // namespace Service diff --git a/src/core/hle/service/spl/spl.h b/src/core/hle/service/spl/spl.h new file mode 100644 index 000000000..9fd6059af --- /dev/null +++ b/src/core/hle/service/spl/spl.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/spl/module.h" + +namespace Service { +namespace SPL { + +class SPL final : public Module::Interface { +public: + explicit SPL(std::shared_ptr<Module> module); +}; + +} // namespace SPL +} // namespace Service diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp new file mode 100644 index 000000000..afa8d5d79 --- /dev/null +++ b/src/core/hle/service/ssl/ssl.cpp @@ -0,0 +1,17 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/ssl/ssl.h" + +namespace Service { +namespace SSL { + +SSL::SSL() : ServiceFramework("ssl") {} + +void InstallInterfaces(SM::ServiceManager& service_manager) { + std::make_shared<SSL>()->InstallAsService(service_manager); +} + +} // namespace SSL +} // namespace Service diff --git a/src/core/hle/service/ssl/ssl.h b/src/core/hle/service/ssl/ssl.h new file mode 100644 index 000000000..645dad003 --- /dev/null +++ b/src/core/hle/service/ssl/ssl.h @@ -0,0 +1,22 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace SSL { + +class SSL final : public ServiceFramework<SSL> { +public: + explicit SSL(); + ~SSL() = default; +}; + +/// Registers all SSL services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace SSL +} // namespace Service diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 674b59509..c3e46f866 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -4,10 +4,13 @@ #include <chrono> #include "common/logging/log.h" +#include "core/core_timing.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" #include "core/hle/service/time/time.h" +#include "core/hle/service/time/time_s.h" +#include "core/hle/service/time/time_u.h" namespace Service { namespace Time { @@ -17,25 +20,47 @@ public: ISystemClock() : ServiceFramework("ISystemClock") { static const FunctionInfo functions[] = { {0, &ISystemClock::GetCurrentTime, "GetCurrentTime"}, - }; + {2, &ISystemClock::GetSystemClockContext, "GetSystemClockContext"}}; RegisterHandlers(functions); } private: void GetCurrentTime(Kernel::HLERequestContext& ctx) { - const s64 time_since_epoch{std::chrono::duration_cast<std::chrono::milliseconds>( + const s64 time_since_epoch{std::chrono::duration_cast<std::chrono::seconds>( std::chrono::system_clock::now().time_since_epoch()) .count()}; - IPC::RequestBuilder rb{ctx, 4}; + LOG_DEBUG(Service_Time, "called"); + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); rb.Push<u64>(time_since_epoch); - LOG_DEBUG(Service, "called"); + } + + void GetSystemClockContext(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Time, "(STUBBED) called"); + SystemClockContext system_clock_ontext{}; + IPC::ResponseBuilder rb{ctx, (sizeof(SystemClockContext) / 4) + 2}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw(system_clock_ontext); } }; class ISteadyClock final : public ServiceFramework<ISteadyClock> { public: - ISteadyClock() : ServiceFramework("ISteadyClock") {} + ISteadyClock() : ServiceFramework("ISteadyClock") { + static const FunctionInfo functions[] = { + {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"}, + }; + RegisterHandlers(functions); + } + +private: + void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called"); + SteadyClockTimePoint steady_clock_time_point{cyclesToMs(CoreTiming::GetTicks()) / 1000}; + IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw(steady_clock_time_point); + } }; class ITimeZoneService final : public ServiceFramework<ITimeZoneService> { @@ -43,99 +68,98 @@ public: ITimeZoneService() : ServiceFramework("ITimeZoneService") { static const FunctionInfo functions[] = { {0, &ITimeZoneService::GetDeviceLocationName, "GetDeviceLocationName"}, + {1, nullptr, "SetDeviceLocationName"}, + {2, &ITimeZoneService::GetTotalLocationNameCount, "GetTotalLocationNameCount"}, + {3, nullptr, "LoadLocationNameList"}, + {4, &ITimeZoneService::LoadTimeZoneRule, "LoadTimeZoneRule"}, + {5, nullptr, "GetTimeZoneRuleVersion"}, + {100, nullptr, "ToCalendarTime"}, {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"}, + {200, nullptr, "ToPosixTime"}, + {201, nullptr, "ToPosixTimeWithMyRule"}, }; RegisterHandlers(functions); } private: void GetDeviceLocationName(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - LocationName name{}; - IPC::RequestBuilder rb{ctx, 11}; + LOG_WARNING(Service_Time, "(STUBBED) called"); + LocationName location_name{}; + IPC::ResponseBuilder rb{ctx, (sizeof(LocationName) / 4) + 2}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw(location_name); + } + + void GetTotalLocationNameCount(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Time, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); + } + + void LoadTimeZoneRule(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Time, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - rb.PushRaw(name); } void ToCalendarTimeWithMyRule(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - u64 posixTime = rp.Pop<u64>(); + u64 posix_time = rp.Pop<u64>(); - LOG_WARNING(Service, "(STUBBED) called, posixTime=0x%016llX", posixTime); + LOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x%016lX", posix_time); - CalendarTime calendarTime{2018, 1, 1, 0, 0, 0}; - CalendarAdditionalInfo additionalInfo{}; - IPC::RequestBuilder rb{ctx, 10}; + CalendarTime calendar_time{2018, 1, 1, 0, 0, 0}; + CalendarAdditionalInfo additional_info{}; + IPC::ResponseBuilder rb{ctx, 10}; rb.Push(RESULT_SUCCESS); - rb.PushRaw(calendarTime); - rb.PushRaw(additionalInfo); + rb.PushRaw(calendar_time); + rb.PushRaw(additional_info); } }; -void TIME::GetStandardUserSystemClock(Kernel::HLERequestContext& ctx) { - auto client_port = std::make_shared<ISystemClock>()->CreatePort(); - auto session = client_port->Connect(); - if (session.Succeeded()) { - LOG_DEBUG(Service, "called, initialized ISystemClock -> session=%u", - (*session)->GetObjectId()); - IPC::RequestBuilder rb{ctx, 2, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushMoveObjects(std::move(session).Unwrap()); - } else { - UNIMPLEMENTED(); - } +void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ISystemClock>(); + LOG_DEBUG(Service_Time, "called"); } -void TIME::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) { - auto client_port = std::make_shared<ISystemClock>()->CreatePort(); - auto session = client_port->Connect(); - if (session.Succeeded()) { - LOG_DEBUG(Service, "called, initialized ISystemClock -> session=%u", - (*session)->GetObjectId()); - IPC::RequestBuilder rb{ctx, 2, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushMoveObjects(std::move(session).Unwrap()); - } else { - UNIMPLEMENTED(); - } +void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ISystemClock>(); + LOG_DEBUG(Service_Time, "called"); } -void TIME::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) { - auto client_port = std::make_shared<ISteadyClock>()->CreatePort(); - auto session = client_port->Connect(); - if (session.Succeeded()) { - LOG_DEBUG(Service, "called, initialized ISteadyClock -> session=%u", - (*session)->GetObjectId()); - IPC::RequestBuilder rb{ctx, 2, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushMoveObjects(std::move(session).Unwrap()); - } else { - UNIMPLEMENTED(); - } +void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ISteadyClock>(); + LOG_DEBUG(Service_Time, "called"); } -void TIME::GetTimeZoneService(Kernel::HLERequestContext& ctx) { - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; +void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ITimeZoneService>(); - LOG_DEBUG(Service, "called"); + LOG_DEBUG(Service_Time, "called"); } -TIME::TIME(const char* name) : ServiceFramework(name) { - static const FunctionInfo functions[] = { - {0x00000000, &TIME::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, - {0x00000001, &TIME::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"}, - {0x00000002, &TIME::GetStandardSteadyClock, "GetStandardSteadyClock"}, - {0x00000003, &TIME::GetTimeZoneService, "GetTimeZoneService"}, - }; - RegisterHandlers(functions); +void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ISystemClock>(); + LOG_DEBUG(Service_Time, "called"); } +Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) + : ServiceFramework(name), time(std::move(time)) {} + void InstallInterfaces(SM::ServiceManager& service_manager) { - std::make_shared<TIME>("time:a")->InstallAsService(service_manager); - std::make_shared<TIME>("time:r")->InstallAsService(service_manager); - std::make_shared<TIME>("time:s")->InstallAsService(service_manager); - std::make_shared<TIME>("time:u")->InstallAsService(service_manager); + auto time = std::make_shared<Module>(); + std::make_shared<TIME_S>(time)->InstallAsService(service_manager); + std::make_shared<TIME_U>(time)->InstallAsService(service_manager); } } // namespace Time diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h index 5f332d057..197029e7a 100644 --- a/src/core/hle/service/time/time.h +++ b/src/core/hle/service/time/time.h @@ -13,7 +13,7 @@ namespace Time { struct LocationName { INSERT_PADDING_BYTES(0x24); }; -static_assert(sizeof(LocationName) == 0x24, "LocationName structure has incorrect size"); +static_assert(sizeof(LocationName) == 0x24, "LocationName is incorrect size"); struct CalendarTime { u16_le year; @@ -33,16 +33,34 @@ struct CalendarAdditionalInfo { static_assert(sizeof(CalendarAdditionalInfo) == 0x18, "CalendarAdditionalInfo structure has incorrect size"); -class TIME final : public ServiceFramework<TIME> { +// TODO(bunnei) RE this structure +struct SystemClockContext { + INSERT_PADDING_BYTES(0x20); +}; +static_assert(sizeof(SystemClockContext) == 0x20, + "SystemClockContext structure has incorrect size"); + +struct SteadyClockTimePoint { + u64 value; + INSERT_PADDING_WORDS(4); +}; +static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size"); + +class Module final { public: - explicit TIME(const char* name); - ~TIME() = default; - -private: - void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx); - void GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx); - void GetStandardSteadyClock(Kernel::HLERequestContext& ctx); - void GetTimeZoneService(Kernel::HLERequestContext& ctx); + class Interface : public ServiceFramework<Interface> { + public: + Interface(std::shared_ptr<Module> time, const char* name); + + void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx); + void GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx); + void GetStandardSteadyClock(Kernel::HLERequestContext& ctx); + void GetTimeZoneService(Kernel::HLERequestContext& ctx); + void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx); + + protected: + std::shared_ptr<Module> time; + }; }; /// Registers all Time services with the specified service manager. diff --git a/src/core/hle/service/time/time_s.cpp b/src/core/hle/service/time/time_s.cpp new file mode 100644 index 000000000..b172b2bd6 --- /dev/null +++ b/src/core/hle/service/time/time_s.cpp @@ -0,0 +1,22 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/time/time_s.h" + +namespace Service { +namespace Time { + +TIME_S::TIME_S(std::shared_ptr<Module> time) : Module::Interface(std::move(time), "time:s") { + static const FunctionInfo functions[] = { + {0, &TIME_S::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, + {1, &TIME_S::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"}, + {2, &TIME_S::GetStandardSteadyClock, "GetStandardSteadyClock"}, + {3, &TIME_S::GetTimeZoneService, "GetTimeZoneService"}, + {4, &TIME_S::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, + }; + RegisterHandlers(functions); +} + +} // namespace Time +} // namespace Service diff --git a/src/core/hle/service/time/time_s.h b/src/core/hle/service/time/time_s.h new file mode 100644 index 000000000..abc2a8c5a --- /dev/null +++ b/src/core/hle/service/time/time_s.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/time/time.h" + +namespace Service { +namespace Time { + +class TIME_S final : public Module::Interface { +public: + explicit TIME_S(std::shared_ptr<Module> time); +}; + +} // namespace Time +} // namespace Service diff --git a/src/core/hle/service/time/time_u.cpp b/src/core/hle/service/time/time_u.cpp new file mode 100644 index 000000000..fc1ace325 --- /dev/null +++ b/src/core/hle/service/time/time_u.cpp @@ -0,0 +1,22 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/time/time_u.h" + +namespace Service { +namespace Time { + +TIME_U::TIME_U(std::shared_ptr<Module> time) : Module::Interface(std::move(time), "time:u") { + static const FunctionInfo functions[] = { + {0, &TIME_U::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, + {1, &TIME_U::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"}, + {2, &TIME_U::GetStandardSteadyClock, "GetStandardSteadyClock"}, + {3, &TIME_U::GetTimeZoneService, "GetTimeZoneService"}, + {4, &TIME_U::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, + }; + RegisterHandlers(functions); +} + +} // namespace Time +} // namespace Service diff --git a/src/core/hle/service/time/time_u.h b/src/core/hle/service/time/time_u.h new file mode 100644 index 000000000..f99d25057 --- /dev/null +++ b/src/core/hle/service/time/time_u.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/time/time.h" + +namespace Service { +namespace Time { + +class TIME_U final : public Module::Interface { +public: + explicit TIME_U(std::shared_ptr<Module> time); +}; + +} // namespace Time +} // namespace Service diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 108a635d7..06c34e979 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -3,23 +3,34 @@ // Refer to the license.txt file included. #include <algorithm> - +#include <array> +#include <memory> +#include <boost/optional.hpp> #include "common/alignment.h" #include "common/scope_exit.h" #include "core/core_timing.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" +#include "core/hle/kernel/event.h" #include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/nvflinger/buffer_queue.h" #include "core/hle/service/vi/vi.h" #include "core/hle/service/vi/vi_m.h" +#include "core/hle/service/vi/vi_s.h" +#include "core/hle/service/vi/vi_u.h" #include "video_core/renderer_base.h" #include "video_core/video_core.h" namespace Service { namespace VI { -constexpr size_t SCREEN_REFRESH_RATE = 60; -constexpr u64 frame_ticks = static_cast<u64>(BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); +struct DisplayInfo { + char display_name[0x40]{"Default"}; + u64 unknown_1{1}; + u64 unknown_2{1}; + u64 width{1920}; + u64 height{1080}; +}; +static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size"); class Parcel { public: @@ -31,6 +42,7 @@ public: template <typename T> T Read() { + ASSERT(read_index + sizeof(T) <= buffer.size()); T val; std::memcpy(&val, buffer.data() + read_index, sizeof(T)); read_index += sizeof(T); @@ -40,6 +52,7 @@ public: template <typename T> T ReadUnaligned() { + ASSERT(read_index + sizeof(T) <= buffer.size()); T val; std::memcpy(&val, buffer.data() + read_index, sizeof(T)); read_index += sizeof(T); @@ -47,6 +60,7 @@ public: } std::vector<u8> ReadBlock(size_t length) { + ASSERT(read_index + length <= buffer.size()); const u8* const begin = buffer.data() + read_index; const u8* const end = begin + length; std::vector<u8> data(begin, end); @@ -79,7 +93,18 @@ public: write_index = Common::AlignUp(write_index, 4); } + template <typename T> + void WriteObject(const T& val) { + u32_le size = static_cast<u32>(sizeof(val)); + Write(size); + // TODO(Subv): Support file descriptors. + Write<u32_le>(0); // Fd count. + Write(val); + } + void Deserialize() { + ASSERT(buffer.size() > sizeof(Header)); + Header header{}; std::memcpy(&header, buffer.data(), sizeof(Header)); @@ -94,8 +119,10 @@ public: SerializeData(); Header header{}; + header.data_size = static_cast<u32_le>(write_index - sizeof(Header)); header.data_offset = sizeof(Header); - header.data_size = write_index - sizeof(Header); + header.objects_size = 4; + header.objects_offset = sizeof(Header) + header.data_size; std::memcpy(buffer.data(), &header, sizeof(Header)); return buffer; @@ -135,11 +162,11 @@ protected: private: struct Data { u32_le magic = 2; - u32_le process_id; + u32_le process_id = 1; u32_le id; - INSERT_PADDING_BYTES(0xC); - std::array<u8, 8> dspdrv = {'d', 's', 'p', 'd', 'r', 'v'}; - INSERT_PADDING_BYTES(8); + INSERT_PADDING_WORDS(3); + std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'}; + INSERT_PADDING_WORDS(2); }; static_assert(sizeof(Data) == 0x28, "ParcelData has wrong size"); @@ -204,8 +231,7 @@ public: void DeserializeData() override { std::u16string token = ReadInterfaceToken(); data = Read<Data>(); - ASSERT(data.graphic_buffer_length == sizeof(IGBPBuffer)); - buffer = Read<IGBPBuffer>(); + buffer = Read<NVFlinger::IGBPBuffer>(); } struct Data { @@ -216,7 +242,7 @@ public: }; Data data; - IGBPBuffer buffer; + NVFlinger::IGBPBuffer buffer; }; class IGBPSetPreallocatedBufferResponseParcel : public Parcel { @@ -254,6 +280,12 @@ public: Data data; }; +struct BufferProducerFence { + u32 is_valid; + std::array<Nvidia::IoctlFence, 4> fences; +}; +static_assert(sizeof(BufferProducerFence) == 36, "BufferProducerFence has wrong size"); + class IGBPDequeueBufferResponseParcel : public Parcel { public: explicit IGBPDequeueBufferResponseParcel(u32 slot) : Parcel(), slot(slot) {} @@ -261,10 +293,15 @@ public: protected: void SerializeData() override { - Write(slot); // TODO(Subv): Find out how this Fence is used. - std::array<u32_le, 11> fence = {}; - Write(fence); + BufferProducerFence fence = {}; + fence.is_valid = 1; + for (auto& fence_ : fence.fences) + fence_.id = -1; + + Write(slot); + Write<u32_le>(1); + WriteObject(fence); Write<u32_le>(0); } @@ -288,23 +325,20 @@ public: class IGBPRequestBufferResponseParcel : public Parcel { public: - explicit IGBPRequestBufferResponseParcel(IGBPBuffer buffer) : Parcel(), buffer(buffer) {} + explicit IGBPRequestBufferResponseParcel(NVFlinger::IGBPBuffer buffer) + : Parcel(), buffer(buffer) {} ~IGBPRequestBufferResponseParcel() override = default; protected: void SerializeData() override { - // TODO(Subv): Find out what this all means + // TODO(Subv): Figure out what this value means, writing non-zero here will make libnx try + // to read an IGBPBuffer object from the parcel. Write<u32_le>(1); - - Write<u32_le>(sizeof(IGBPBuffer)); - Write<u32_le>(0); // Unknown - - Write(buffer); - + WriteObject(buffer); Write<u32_le>(0); } - IGBPBuffer buffer; + NVFlinger::IGBPBuffer buffer; }; class IGBPQueueBufferRequestParcel : public Parcel { @@ -319,13 +353,29 @@ public: data = Read<Data>(); } + struct Fence { + u32_le id; + u32_le value; + }; + static_assert(sizeof(Fence) == 8, "Fence has wrong size"); + struct Data { u32_le slot; - INSERT_PADDING_WORDS(2); + INSERT_PADDING_WORDS(3); u32_le timestamp; - INSERT_PADDING_WORDS(20); + s32_le is_auto_timestamp; + s32_le crop_left; + s32_le crop_top; + s32_le crop_right; + s32_le crop_bottom; + s32_le scaling_mode; + NVFlinger::BufferQueue::BufferTransformFlags transform; + u32_le sticky_transform; + INSERT_PADDING_WORDS(2); + u32_le fence_is_valid; + std::array<Fence, 2> fences; }; - static_assert(sizeof(Data) == 96, "ParcelData has wrong size"); + static_assert(sizeof(Data) == 80, "ParcelData has wrong size"); Data data; }; @@ -356,15 +406,44 @@ private: Data data{}; }; +class IGBPQueryRequestParcel : public Parcel { +public: + explicit IGBPQueryRequestParcel(const std::vector<u8>& buffer) : Parcel(buffer) { + Deserialize(); + } + ~IGBPQueryRequestParcel() override = default; + + void DeserializeData() override { + std::u16string token = ReadInterfaceToken(); + type = Read<u32_le>(); + } + + u32 type; +}; + +class IGBPQueryResponseParcel : public Parcel { +public: + explicit IGBPQueryResponseParcel(u32 value) : Parcel(), value(value) {} + ~IGBPQueryResponseParcel() override = default; + +protected: + void SerializeData() override { + Write(value); + } + +private: + u32_le value; +}; + class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> { public: - explicit IHOSBinderDriver(std::shared_ptr<NVFlinger> nv_flinger) + explicit IHOSBinderDriver(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) : ServiceFramework("IHOSBinderDriver"), nv_flinger(std::move(nv_flinger)) { static const FunctionInfo functions[] = { {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"}, {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"}, {2, &IHOSBinderDriver::GetNativeHandle, "GetNativeHandle"}, - {3, nullptr, "TransactParcelAuto"}, + {3, &IHOSBinderDriver::TransactParcel, "TransactParcelAuto"}, }; RegisterHandlers(functions); } @@ -393,64 +472,76 @@ private: u32 id = rp.Pop<u32>(); auto transaction = static_cast<TransactionId>(rp.Pop<u32>()); u32 flags = rp.Pop<u32>(); - - auto& input_buffer = ctx.BufferDescriptorA()[0]; - std::vector<u8> input_data(input_buffer.Size()); - Memory::ReadBlock(input_buffer.Address(), input_data.data(), input_buffer.Size()); - - auto& output_buffer = ctx.BufferDescriptorB()[0]; - auto buffer_queue = nv_flinger->GetBufferQueue(id); + LOG_DEBUG(Service_VI, "called, transaction=%x", static_cast<u32>(transaction)); + if (transaction == TransactionId::Connect) { - IGBPConnectRequestParcel request{input_data}; + IGBPConnectRequestParcel request{ctx.ReadBuffer()}; IGBPConnectResponseParcel response{1280, 720}; - auto response_buffer = response.Serialize(); - Memory::WriteBlock(output_buffer.Address(), response_buffer.data(), - output_buffer.Size()); + ctx.WriteBuffer(response.Serialize()); } else if (transaction == TransactionId::SetPreallocatedBuffer) { - IGBPSetPreallocatedBufferRequestParcel request{input_data}; + IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()}; buffer_queue->SetPreallocatedBuffer(request.data.slot, request.buffer); IGBPSetPreallocatedBufferResponseParcel response{}; - auto response_buffer = response.Serialize(); - Memory::WriteBlock(output_buffer.Address(), response_buffer.data(), - output_buffer.Size()); + ctx.WriteBuffer(response.Serialize()); } else if (transaction == TransactionId::DequeueBuffer) { - IGBPDequeueBufferRequestParcel request{input_data}; - - u32 slot = buffer_queue->DequeueBuffer(request.data.pixel_format, request.data.width, - request.data.height); - - IGBPDequeueBufferResponseParcel response{slot}; - auto response_buffer = response.Serialize(); - Memory::WriteBlock(output_buffer.Address(), response_buffer.data(), - output_buffer.Size()); + IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()}; + const u32 width{request.data.width}; + const u32 height{request.data.height}; + boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); + + if (slot != boost::none) { + // Buffer is available + IGBPDequeueBufferResponseParcel response{*slot}; + ctx.WriteBuffer(response.Serialize()); + } else { + // Wait the current thread until a buffer becomes available + auto wait_event = ctx.SleepClientThread( + Kernel::GetCurrentThread(), "IHOSBinderDriver::DequeueBuffer", -1, + [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, + ThreadWakeupReason reason) { + // Repeat TransactParcel DequeueBuffer when a buffer is available + auto buffer_queue = nv_flinger->GetBufferQueue(id); + boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); + IGBPDequeueBufferResponseParcel response{*slot}; + ctx.WriteBuffer(response.Serialize()); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + }); + buffer_queue->SetBufferWaitEvent(std::move(wait_event)); + } } else if (transaction == TransactionId::RequestBuffer) { - IGBPRequestBufferRequestParcel request{input_data}; + IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()}; auto& buffer = buffer_queue->RequestBuffer(request.slot); IGBPRequestBufferResponseParcel response{buffer}; - auto response_buffer = response.Serialize(); - Memory::WriteBlock(output_buffer.Address(), response_buffer.data(), - output_buffer.Size()); + ctx.WriteBuffer(response.Serialize()); } else if (transaction == TransactionId::QueueBuffer) { - IGBPQueueBufferRequestParcel request{input_data}; + IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()}; - buffer_queue->QueueBuffer(request.data.slot); + buffer_queue->QueueBuffer(request.data.slot, request.data.transform); IGBPQueueBufferResponseParcel response{1280, 720}; - auto response_buffer = response.Serialize(); - Memory::WriteBlock(output_buffer.Address(), response_buffer.data(), - output_buffer.Size()); + ctx.WriteBuffer(response.Serialize()); + } else if (transaction == TransactionId::Query) { + IGBPQueryRequestParcel request{ctx.ReadBuffer()}; + + u32 value = + buffer_queue->Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type)); + + IGBPQueryResponseParcel response{value}; + ctx.WriteBuffer(response.Serialize()); + } else if (transaction == TransactionId::CancelBuffer) { + LOG_WARNING(Service_VI, "(STUBBED) called, transaction=CancelBuffer"); } else { ASSERT_MSG(false, "Unimplemented"); } - LOG_WARNING(Service, "(STUBBED) called"); - IPC::RequestBuilder rb{ctx, 2}; + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -460,8 +551,8 @@ private: s32 addval = rp.PopRaw<s32>(); u32 type = rp.Pop<u32>(); - LOG_WARNING(Service, "(STUBBED) called id=%u, addval=%08X, type=%08X", id, addval, type); - IPC::RequestBuilder rb{ctx, 2}; + LOG_WARNING(Service_VI, "(STUBBED) called id=%u, addval=%08X, type=%08X", id, addval, type); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -474,14 +565,14 @@ private: // TODO(Subv): Find out what this actually is. - LOG_WARNING(Service, "(STUBBED) called id=%u, unknown=%08X", id, unknown); - IPC::RequestBuilder rb{ctx, 2, 1}; + LOG_WARNING(Service_VI, "(STUBBED) called id=%u, unknown=%08X", id, unknown); + IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(buffer_queue->GetNativeHandle()); } - std::shared_ptr<NVFlinger> nv_flinger; -}; + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; +}; // namespace VI class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> { public: @@ -496,19 +587,19 @@ public: private: void SetLayerZ(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; u64 layer_id = rp.Pop<u64>(); u64 z_value = rp.Pop<u64>(); - IPC::RequestBuilder rb = rp.MakeBuilder(2, 0, 0, 0); + IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); rb.Push(RESULT_SUCCESS); } }; class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> { public: - explicit IManagerDisplayService(std::shared_ptr<NVFlinger> nv_flinger) + explicit IManagerDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) : ServiceFramework("IManagerDisplayService"), nv_flinger(std::move(nv_flinger)) { static const FunctionInfo functions[] = { {1020, &IManagerDisplayService::CloseDisplay, "CloseDisplay"}, @@ -522,16 +613,16 @@ public: private: void CloseDisplay(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; u64 display = rp.Pop<u64>(); - IPC::RequestBuilder rb = rp.MakeBuilder(2, 0, 0, 0); + IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); rb.Push(RESULT_SUCCESS); } void CreateManagedLayer(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; u32 unknown = rp.Pop<u32>(); rp.Skip(1, false); @@ -540,163 +631,173 @@ private: u64 layer_id = nv_flinger->CreateLayer(display); - IPC::RequestBuilder rb = rp.MakeBuilder(4, 0, 0, 0); + IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); rb.Push(RESULT_SUCCESS); rb.Push(layer_id); } void AddToLayerStack(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + LOG_WARNING(Service_VI, "(STUBBED) called"); IPC::RequestParser rp{ctx}; u32 stack = rp.Pop<u32>(); u64 layer_id = rp.Pop<u64>(); - IPC::RequestBuilder rb = rp.MakeBuilder(2, 0, 0, 0); + IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); rb.Push(RESULT_SUCCESS); } - std::shared_ptr<NVFlinger> nv_flinger; + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; }; -void IApplicationDisplayService::GetRelayService(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); +class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { +public: + IApplicationDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); + ~IApplicationDisplayService() = default; - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IHOSBinderDriver>(nv_flinger); -} +private: + void GetRelayService(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_VI, "(STUBBED) called"); -void IApplicationDisplayService::GetSystemDisplayService(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IHOSBinderDriver>(nv_flinger); + } - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISystemDisplayService>(); -} + void GetSystemDisplayService(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_VI, "(STUBBED) called"); -void IApplicationDisplayService::GetManagerDisplayService(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ISystemDisplayService>(); + } - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IManagerDisplayService>(nv_flinger); -} + void GetManagerDisplayService(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_VI, "(STUBBED) called"); -void IApplicationDisplayService::GetIndirectDisplayTransactionService( - Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IManagerDisplayService>(nv_flinger); + } - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IHOSBinderDriver>(nv_flinger); -} + void GetIndirectDisplayTransactionService(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_VI, "(STUBBED) called"); -void IApplicationDisplayService::OpenDisplay(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - IPC::RequestParser rp{ctx}; - auto name_buf = rp.PopRaw<std::array<u8, 0x40>>(); - auto end = std::find(name_buf.begin(), name_buf.end(), '\0'); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IHOSBinderDriver>(nv_flinger); + } - std::string name(name_buf.begin(), end); + void OpenDisplay(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_VI, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + auto name_buf = rp.PopRaw<std::array<u8, 0x40>>(); + auto end = std::find(name_buf.begin(), name_buf.end(), '\0'); - ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet"); + std::string name(name_buf.begin(), end); - IPC::RequestBuilder rb = rp.MakeBuilder(4, 0, 0, 0); - rb.Push(RESULT_SUCCESS); - rb.Push<u64>(nv_flinger->OpenDisplay(name)); -} + ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet"); -void IApplicationDisplayService::CloseDisplay(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - IPC::RequestParser rp{ctx}; - u64 display_id = rp.Pop<u64>(); + IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(nv_flinger->OpenDisplay(name)); + } - IPC::RequestBuilder rb = rp.MakeBuilder(4, 0, 0, 0); - rb.Push(RESULT_SUCCESS); -} + void CloseDisplay(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_VI, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + u64 display_id = rp.Pop<u64>(); -void IApplicationDisplayService::OpenLayer(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - IPC::RequestParser rp{ctx}; - auto name_buf = rp.PopRaw<std::array<u8, 0x40>>(); - auto end = std::find(name_buf.begin(), name_buf.end(), '\0'); + IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); + rb.Push(RESULT_SUCCESS); + } - std::string display_name(name_buf.begin(), end); + void SetLayerScalingMode(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_VI, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + u32 scaling_mode = rp.Pop<u32>(); + u64 unknown = rp.Pop<u64>(); - u64 layer_id = rp.Pop<u64>(); - u64 aruid = rp.Pop<u64>(); + IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); + rb.Push(RESULT_SUCCESS); + } - auto& buffer = ctx.BufferDescriptorB()[0]; + void ListDisplays(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + DisplayInfo display_info; + ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); + IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(1); + LOG_WARNING(Service_VI, "(STUBBED) called"); + } - u64 display_id = nv_flinger->OpenDisplay(display_name); - u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id); + void OpenLayer(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_VI, "called"); + IPC::RequestParser rp{ctx}; + auto name_buf = rp.PopRaw<std::array<u8, 0x40>>(); + auto end = std::find(name_buf.begin(), name_buf.end(), '\0'); - NativeWindow native_window{buffer_queue_id}; - auto data = native_window.Serialize(); - Memory::WriteBlock(buffer.Address(), data.data(), data.size()); + std::string display_name(name_buf.begin(), end); - IPC::RequestBuilder rb = rp.MakeBuilder(4, 0, 0, 0); - rb.Push(RESULT_SUCCESS); - rb.Push<u64>(data.size()); -} - -void IApplicationDisplayService::CreateStrayLayer(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + u64 layer_id = rp.Pop<u64>(); + u64 aruid = rp.Pop<u64>(); - IPC::RequestParser rp{ctx}; - u32 flags = rp.Pop<u32>(); - u64 display_id = rp.Pop<u64>(); + u64 display_id = nv_flinger->OpenDisplay(display_name); + u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id); - auto& buffer = ctx.BufferDescriptorB()[0]; + NativeWindow native_window{buffer_queue_id}; + IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize())); + } - // TODO(Subv): What's the difference between a Stray and a Managed layer? + void CreateStrayLayer(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_VI, "called"); - u64 layer_id = nv_flinger->CreateLayer(display_id); - u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id); + IPC::RequestParser rp{ctx}; + u32 flags = rp.Pop<u32>(); + rp.Pop<u32>(); // padding + u64 display_id = rp.Pop<u64>(); - NativeWindow native_window{buffer_queue_id}; - auto data = native_window.Serialize(); - Memory::WriteBlock(buffer.Address(), data.data(), data.size()); + // TODO(Subv): What's the difference between a Stray and a Managed layer? - IPC::RequestBuilder rb = rp.MakeBuilder(6, 0, 0, 0); - rb.Push(RESULT_SUCCESS); - rb.Push(layer_id); - rb.Push<u64>(data.size()); -} + u64 layer_id = nv_flinger->CreateLayer(display_id); + u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id); -void IApplicationDisplayService::DestroyStrayLayer(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + NativeWindow native_window{buffer_queue_id}; + IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(layer_id); + rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize())); + } - IPC::RequestParser rp{ctx}; - u64 layer_id = rp.Pop<u64>(); + void DestroyStrayLayer(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_VI, "(STUBBED) called"); - IPC::RequestBuilder rb = rp.MakeBuilder(2, 0, 0, 0); - rb.Push(RESULT_SUCCESS); -} + IPC::RequestParser rp{ctx}; + u64 layer_id = rp.Pop<u64>(); -void IApplicationDisplayService::SetLayerScalingMode(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - IPC::RequestParser rp{ctx}; - u32 scaling_mode = rp.Pop<u32>(); - u64 unknown = rp.Pop<u64>(); + IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); + rb.Push(RESULT_SUCCESS); + } - IPC::RequestBuilder rb = rp.MakeBuilder(2, 0, 0, 0); - rb.Push(RESULT_SUCCESS); -} + void GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_VI, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + u64 display_id = rp.Pop<u64>(); -void IApplicationDisplayService::GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - IPC::RequestParser rp{ctx}; - u64 display_id = rp.Pop<u64>(); + auto vsync_event = nv_flinger->GetVsyncEvent(display_id); - auto vsync_event = nv_flinger->GetVsyncEvent(display_id); + IPC::ResponseBuilder rb = rp.MakeBuilder(2, 1, 0); + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(vsync_event); + } - IPC::RequestBuilder rb = rp.MakeBuilder(2, 1, 0, 0); - rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(vsync_event); -} + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; +}; -IApplicationDisplayService::IApplicationDisplayService(std::shared_ptr<NVFlinger> nv_flinger) +IApplicationDisplayService::IApplicationDisplayService( + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) : ServiceFramework("IApplicationDisplayService"), nv_flinger(std::move(nv_flinger)) { static const FunctionInfo functions[] = { {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"}, @@ -704,7 +805,7 @@ IApplicationDisplayService::IApplicationDisplayService(std::shared_ptr<NVFlinger {102, &IApplicationDisplayService::GetManagerDisplayService, "GetManagerDisplayService"}, {103, &IApplicationDisplayService::GetIndirectDisplayTransactionService, "GetIndirectDisplayTransactionService"}, - {1000, nullptr, "ListDisplays"}, + {1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"}, {1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"}, {1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"}, {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"}, @@ -716,212 +817,24 @@ IApplicationDisplayService::IApplicationDisplayService(std::shared_ptr<NVFlinger RegisterHandlers(functions); } -void InstallInterfaces(SM::ServiceManager& service_manager) { - std::make_shared<VI_M>()->InstallAsService(service_manager); -} - -NVFlinger::NVFlinger() { - // Add the different displays to the list of displays. - Display default_{0, "Default"}; - Display external{1, "External"}; - Display edid{2, "Edid"}; - Display internal{3, "Internal"}; - - displays.emplace_back(default_); - displays.emplace_back(external); - displays.emplace_back(edid); - displays.emplace_back(internal); - - // Schedule the screen composition events - composition_event = - CoreTiming::RegisterEvent("ScreenCompositioin", [this](u64 userdata, int cycles_late) { - Compose(); - CoreTiming::ScheduleEvent(frame_ticks - cycles_late, composition_event); - }); - - CoreTiming::ScheduleEvent(frame_ticks, composition_event); -} - -NVFlinger::~NVFlinger() { - CoreTiming::UnscheduleEvent(composition_event, 0); -} - -u64 NVFlinger::OpenDisplay(const std::string& name) { - LOG_WARNING(Service, "Opening display %s", name.c_str()); +Module::Interface::Interface(std::shared_ptr<Module> module, const char* name, + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) + : ServiceFramework(name), module(std::move(module)), nv_flinger(std::move(nv_flinger)) {} - // TODO(Subv): Currently we only support the Default display. - ASSERT(name == "Default"); - - auto itr = std::find_if(displays.begin(), displays.end(), - [&](const Display& display) { return display.name == name; }); - - ASSERT(itr != displays.end()); - - return itr->id; -} - -u64 NVFlinger::CreateLayer(u64 display_id) { - auto& display = GetDisplay(display_id); - - ASSERT_MSG(display.layers.empty(), "Only one layer is supported per display at the moment"); - - u64 layer_id = next_layer_id++; - u32 buffer_queue_id = next_buffer_queue_id++; - auto buffer_queue = std::make_shared<BufferQueue>(buffer_queue_id, layer_id); - display.layers.emplace_back(layer_id, buffer_queue); - buffer_queues.emplace_back(std::move(buffer_queue)); - return layer_id; -} - -u32 NVFlinger::GetBufferQueueId(u64 display_id, u64 layer_id) { - const auto& layer = GetLayer(display_id, layer_id); - return layer.buffer_queue->GetId(); -} +void Module::Interface::GetDisplayService(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_VI, "(STUBBED) called"); -Kernel::SharedPtr<Kernel::Event> NVFlinger::GetVsyncEvent(u64 display_id) { - const auto& display = GetDisplay(display_id); - return display.vsync_event; -} - -std::shared_ptr<BufferQueue> NVFlinger::GetBufferQueue(u32 id) const { - auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(), - [&](const auto& queue) { return queue->GetId() == id; }); - - ASSERT(itr != buffer_queues.end()); - return *itr; -} - -Display& NVFlinger::GetDisplay(u64 display_id) { - auto itr = std::find_if(displays.begin(), displays.end(), - [&](const Display& display) { return display.id == display_id; }); - - ASSERT(itr != displays.end()); - return *itr; -} - -Layer& NVFlinger::GetLayer(u64 display_id, u64 layer_id) { - auto& display = GetDisplay(display_id); - - auto itr = std::find_if(display.layers.begin(), display.layers.end(), - [&](const Layer& layer) { return layer.id == layer_id; }); - - ASSERT(itr != display.layers.end()); - return *itr; -} - -void NVFlinger::Compose() { - for (auto& display : displays) { - // Trigger vsync for this display at the end of drawing - SCOPE_EXIT({ display.vsync_event->Signal(); }); - - // Don't do anything for displays without layers. - if (display.layers.empty()) - continue; - - // TODO(Subv): Support more than 1 layer. - ASSERT_MSG(display.layers.size() == 1, "Max 1 layer per display is supported"); - - Layer& layer = display.layers[0]; - auto& buffer_queue = layer.buffer_queue; - - // Search for a queued buffer and acquire it - auto buffer = buffer_queue->AcquireBuffer(); - - if (buffer == boost::none) { - // There was no queued buffer to draw, render previous frame - VideoCore::g_renderer->SwapBuffers({}); - continue; - } - - auto& igbp_buffer = buffer->igbp_buffer; - - // Now send the buffer to the GPU for drawing. - auto nvdrv = Nvidia::nvdrv.lock(); - ASSERT(nvdrv); - - // TODO(Subv): Support more than just disp0. The display device selection is probably based - // on which display we're drawing (Default, Internal, External, etc) - auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0"); - ASSERT(nvdisp); - - nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format, - igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride); - - buffer_queue->ReleaseBuffer(buffer->slot); - } -} - -BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) { - native_handle = Kernel::Event::Create(Kernel::ResetType::OneShot, "BufferQueue NativeHandle"); -} - -void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) { - Buffer buffer{}; - buffer.slot = slot; - buffer.igbp_buffer = igbp_buffer; - buffer.status = Buffer::Status::Free; - - LOG_WARNING(Service, "Adding graphics buffer %u", slot); - - queue.emplace_back(buffer); -} - -u32 BufferQueue::DequeueBuffer(u32 pixel_format, u32 width, u32 height) { - auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { - // Only consider free buffers. Buffers become free once again after they've been Acquired - // and Released by the compositor, see the NVFlinger::Compose method. - if (buffer.status != Buffer::Status::Free) - return false; - - // Make sure that the parameters match. - auto& igbp_buffer = buffer.igbp_buffer; - return igbp_buffer.format == pixel_format && igbp_buffer.width == width && - igbp_buffer.height == height; - }); - ASSERT(itr != queue.end()); - - itr->status = Buffer::Status::Dequeued; - return itr->slot; -} - -const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const { - auto itr = std::find_if(queue.begin(), queue.end(), - [&](const Buffer& buffer) { return buffer.slot == slot; }); - ASSERT(itr != queue.end()); - ASSERT(itr->status == Buffer::Status::Dequeued); - return itr->igbp_buffer; -} - -void BufferQueue::QueueBuffer(u32 slot) { - auto itr = std::find_if(queue.begin(), queue.end(), - [&](const Buffer& buffer) { return buffer.slot == slot; }); - ASSERT(itr != queue.end()); - ASSERT(itr->status == Buffer::Status::Dequeued); - itr->status = Buffer::Status::Queued; -} - -boost::optional<const BufferQueue::Buffer&> BufferQueue::AcquireBuffer() { - auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) { - return buffer.status == Buffer::Status::Queued; - }); - if (itr == queue.end()) - return boost::none; - itr->status = Buffer::Status::Acquired; - return *itr; -} - -void BufferQueue::ReleaseBuffer(u32 slot) { - auto itr = std::find_if(queue.begin(), queue.end(), - [&](const Buffer& buffer) { return buffer.slot == slot; }); - ASSERT(itr != queue.end()); - ASSERT(itr->status == Buffer::Status::Acquired); - itr->status = Buffer::Status::Free; + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger); } -Layer::Layer(u64 id, std::shared_ptr<BufferQueue> queue) : id(id), buffer_queue(std::move(queue)) {} - -Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) { - vsync_event = Kernel::Event::Create(Kernel::ResetType::Pulse, "Display VSync Event"); +void InstallInterfaces(SM::ServiceManager& service_manager, + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) { + auto module = std::make_shared<Module>(); + std::make_shared<VI_M>(module, nv_flinger)->InstallAsService(service_manager); + std::make_shared<VI_S>(module, nv_flinger)->InstallAsService(service_manager); + std::make_shared<VI_U>(module, nv_flinger)->InstallAsService(service_manager); } } // namespace VI diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h index 81d4f3daa..985c9d27c 100644 --- a/src/core/hle/service/vi/vi.h +++ b/src/core/hle/service/vi/vi.h @@ -4,9 +4,7 @@ #pragma once -#include <memory> -#include <boost/optional.hpp> -#include "core/hle/kernel/event.h" +#include "core/hle/service/nvflinger/nvflinger.h" #include "core/hle/service/service.h" namespace CoreTiming { @@ -16,147 +14,24 @@ struct EventType; namespace Service { namespace VI { -struct IGBPBuffer { - u32_le magic; - u32_le width; - u32_le height; - u32_le stride; - u32_le format; - u32_le usage; - INSERT_PADDING_WORDS(1); - u32_le index; - INSERT_PADDING_WORDS(3); - u32_le gpu_buffer_id; - INSERT_PADDING_WORDS(17); - u32_le nvmap_handle; - u32_le offset; - INSERT_PADDING_WORDS(60); -}; - -static_assert(sizeof(IGBPBuffer) == 0x16C, "IGBPBuffer has wrong size"); - -class BufferQueue { +class Module final { public: - BufferQueue(u32 id, u64 layer_id); - ~BufferQueue() = default; + class Interface : public ServiceFramework<Interface> { + public: + Interface(std::shared_ptr<Module> module, const char* name, + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); - struct Buffer { - enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 }; + void GetDisplayService(Kernel::HLERequestContext& ctx); - u32 slot; - Status status = Status::Free; - IGBPBuffer igbp_buffer; + protected: + std::shared_ptr<Module> module; + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; }; - - void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer); - u32 DequeueBuffer(u32 pixel_format, u32 width, u32 height); - const IGBPBuffer& RequestBuffer(u32 slot) const; - void QueueBuffer(u32 slot); - boost::optional<const Buffer&> AcquireBuffer(); - void ReleaseBuffer(u32 slot); - - u32 GetId() const { - return id; - } - - Kernel::SharedPtr<Kernel::Event> GetNativeHandle() const { - return native_handle; - } - -private: - u32 id; - u64 layer_id; - - std::vector<Buffer> queue; - Kernel::SharedPtr<Kernel::Event> native_handle; -}; - -struct Layer { - Layer(u64 id, std::shared_ptr<BufferQueue> queue); - ~Layer() = default; - - u64 id; - std::shared_ptr<BufferQueue> buffer_queue; -}; - -struct Display { - Display(u64 id, std::string name); - ~Display() = default; - - u64 id; - std::string name; - - std::vector<Layer> layers; - Kernel::SharedPtr<Kernel::Event> vsync_event; -}; - -class NVFlinger { -public: - NVFlinger(); - ~NVFlinger(); - - /// Opens the specified display and returns the id. - u64 OpenDisplay(const std::string& name); - - /// Creates a layer on the specified display and returns the layer id. - u64 CreateLayer(u64 display_id); - - /// Gets the buffer queue id of the specified layer in the specified display. - u32 GetBufferQueueId(u64 display_id, u64 layer_id); - - /// Gets the vsync event for the specified display. - Kernel::SharedPtr<Kernel::Event> GetVsyncEvent(u64 display_id); - - /// Obtains a buffer queue identified by the id. - std::shared_ptr<BufferQueue> GetBufferQueue(u32 id) const; - - /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when - /// finished. - void Compose(); - -private: - /// Returns the display identified by the specified id. - Display& GetDisplay(u64 display_id); - - /// Returns the layer identified by the specified id in the desired display. - Layer& GetLayer(u64 display_id, u64 layer_id); - - std::vector<Display> displays; - std::vector<std::shared_ptr<BufferQueue>> buffer_queues; - - /// Id to use for the next layer that is created, this counter is shared among all displays. - u64 next_layer_id = 1; - /// Id to use for the next buffer queue that is created, this counter is shared among all - /// layers. - u32 next_buffer_queue_id = 1; - - /// CoreTiming event that handles screen composition. - CoreTiming::EventType* composition_event; -}; - -class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { -public: - IApplicationDisplayService(std::shared_ptr<NVFlinger> nv_flinger); - ~IApplicationDisplayService() = default; - -private: - void GetRelayService(Kernel::HLERequestContext& ctx); - void GetSystemDisplayService(Kernel::HLERequestContext& ctx); - void GetManagerDisplayService(Kernel::HLERequestContext& ctx); - void GetIndirectDisplayTransactionService(Kernel::HLERequestContext& ctx); - void OpenDisplay(Kernel::HLERequestContext& ctx); - void CloseDisplay(Kernel::HLERequestContext& ctx); - void SetLayerScalingMode(Kernel::HLERequestContext& ctx); - void OpenLayer(Kernel::HLERequestContext& ctx); - void CreateStrayLayer(Kernel::HLERequestContext& ctx); - void DestroyStrayLayer(Kernel::HLERequestContext& ctx); - void GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx); - - std::shared_ptr<NVFlinger> nv_flinger; }; /// Registers all VI services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(SM::ServiceManager& service_manager, + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); } // namespace VI } // namespace Service diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp index 1a5a28b0d..5781fa9ec 100644 --- a/src/core/hle/service/vi/vi_m.cpp +++ b/src/core/hle/service/vi/vi_m.cpp @@ -2,28 +2,18 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/logging/log.h" -#include "core/hle/ipc_helpers.h" -#include "core/hle/service/vi/vi.h" #include "core/hle/service/vi/vi_m.h" namespace Service { namespace VI { -void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - - IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; - rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger); -} - -VI_M::VI_M() : ServiceFramework("vi:m") { +VI_M::VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) + : Module::Interface(std::move(module), "vi:m", std::move(nv_flinger)) { static const FunctionInfo functions[] = { {2, &VI_M::GetDisplayService, "GetDisplayService"}, {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, }; RegisterHandlers(functions); - nv_flinger = std::make_shared<NVFlinger>(); } } // namespace VI diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h index 70ff7a2f3..0f7b799d6 100644 --- a/src/core/hle/service/vi/vi_m.h +++ b/src/core/hle/service/vi/vi_m.h @@ -4,21 +4,14 @@ #pragma once -#include <memory> -#include "core/hle/service/service.h" +#include "core/hle/service/vi/vi.h" namespace Service { namespace VI { -class VI_M final : public ServiceFramework<VI_M> { +class VI_M final : public Module::Interface { public: - VI_M(); - ~VI_M() = default; - -private: - void GetDisplayService(Kernel::HLERequestContext& ctx); - - std::shared_ptr<NVFlinger> nv_flinger; + explicit VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); }; } // namespace VI diff --git a/src/core/hle/service/vi/vi_s.cpp b/src/core/hle/service/vi/vi_s.cpp new file mode 100644 index 000000000..1f937b2a8 --- /dev/null +++ b/src/core/hle/service/vi/vi_s.cpp @@ -0,0 +1,20 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/vi/vi_s.h" + +namespace Service { +namespace VI { + +VI_S::VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) + : Module::Interface(std::move(module), "vi:s", std::move(nv_flinger)) { + static const FunctionInfo functions[] = { + {1, &VI_S::GetDisplayService, "GetDisplayService"}, + {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, + }; + RegisterHandlers(functions); +} + +} // namespace VI +} // namespace Service diff --git a/src/core/hle/service/vi/vi_s.h b/src/core/hle/service/vi/vi_s.h new file mode 100644 index 000000000..7b32fdddc --- /dev/null +++ b/src/core/hle/service/vi/vi_s.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/vi/vi.h" + +namespace Service { +namespace VI { + +class VI_S final : public Module::Interface { +public: + explicit VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); +}; + +} // namespace VI +} // namespace Service diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp new file mode 100644 index 000000000..14e375b86 --- /dev/null +++ b/src/core/hle/service/vi/vi_u.cpp @@ -0,0 +1,20 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/vi/vi_u.h" + +namespace Service { +namespace VI { + +VI_U::VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) + : Module::Interface(std::move(module), "vi:u", std::move(nv_flinger)) { + static const FunctionInfo functions[] = { + {0, &VI_U::GetDisplayService, "GetDisplayService"}, + {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, + }; + RegisterHandlers(functions); +} + +} // namespace VI +} // namespace Service diff --git a/src/core/hle/service/vi/vi_u.h b/src/core/hle/service/vi/vi_u.h new file mode 100644 index 000000000..c557a2235 --- /dev/null +++ b/src/core/hle/service/vi/vi_u.h @@ -0,0 +1,18 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/vi/vi.h" + +namespace Service { +namespace VI { + +class VI_U final : public Module::Interface { +public: + explicit VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); +}; + +} // namespace VI +} // namespace Service |
