diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index be16802b..753ccc56 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -192,6 +192,7 @@ add_library(skyline SHARED ${source_DIR}/skyline/services/ssl/ISslService.cpp ${source_DIR}/skyline/services/ssl/ISslContext.cpp ${source_DIR}/skyline/services/prepo/IPrepoService.cpp + ${source_DIR}/skyline/services/mmnv/IRequest.cpp ) # target_precompile_headers(skyline PRIVATE ${source_DIR}/skyline/common.h) # PCH will currently break Intellisense target_link_libraries(skyline android perfetto fmt lz4_static tzcode oboe mbedtls::mbedcrypto) diff --git a/app/src/main/cpp/skyline/services/mmnv/IRequest.cpp b/app/src/main/cpp/skyline/services/mmnv/IRequest.cpp new file mode 100644 index 00000000..ae449e13 --- /dev/null +++ b/app/src/main/cpp/skyline/services/mmnv/IRequest.cpp @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#include +#include "IRequest.h" + +namespace skyline::service::mmnv { + IRequest::IRequest(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {} + + IRequest::Allocation IRequest::AllocateRequest() { + // Search existing requests first + for (u32 i{}; i < requests.size(); i++) { + auto &req = requests[i]; + if (!req) { + return {req.emplace(), i}; + } + } + + // Allocate a new request otherwise + return {requests.emplace_back().emplace(), static_cast(requests.size() - 1)}; + } + + Result IRequest::InitializeOld(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + auto module{request.Pop()}; + request.Skip(); // Unknown unused param in HOS + //u32 autoClear{request.Pop()}; + + std::lock_guard lock(requestsMutex); + AllocateRequest().request.module = module; + + return {}; + } + + Result IRequest::FinalizeOld(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + auto module{request.Pop()}; + + std::lock_guard lock(requestsMutex); + for (auto &req : requests) { + if (req && req->module == module) { + req.reset(); + break; + } + } + + return {}; + } + + Result IRequest::SetAndWaitOld(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + auto module{request.Pop()}; + u32 freqHz{request.Pop()}; + + std::lock_guard lock(requestsMutex); + for (auto &req : requests) { + if (req && req->module == module) { + req->freqHz = freqHz; + state.logger->Debug("Set frequency for module {}: {} Hz", static_cast(module), freqHz); + return {}; + } + } + + // This doesn't return any errors in HOS + state.logger->Warn("Tried to set frequency to {} Hz for unregistered module {}", freqHz, static_cast(module)); + + return {}; + } + + Result IRequest::GetOld(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + auto module{request.Pop()}; + + std::lock_guard lock(requestsMutex); + for (auto &req : requests) { + if (req && req->module == module) { + state.logger->Debug("Get frequency for module {}: {} Hz", static_cast(module), req->freqHz); + response.Push(req->freqHz); + return {}; + } + } + + // This doesn't return any errors in HOS + state.logger->Warn("Tried to get frequency of unregistered module {}", static_cast(module)); + response.Push(0); + return {}; + } + + Result IRequest::Initialize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + auto module{request.Pop()}; + request.Skip(); // Unknown unused param in HOS + //u32 autoClear{request.Pop()}; + + std::lock_guard lock(requestsMutex); + auto req{AllocateRequest()}; + req.request.module = module; + response.Push(req.id); + return {}; + } + + Result IRequest::Finalize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + u32 id{request.Pop()}; + + std::lock_guard lock(requestsMutex); + if (id >= requests.size()) + return {}; + + requests[id].reset(); + return {}; + }; + + Result IRequest::SetAndWait(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + u32 id{request.Pop()}; + u32 freqHz{request.Pop()}; + + std::lock_guard lock(requestsMutex); + if (id < requests.size()) { + auto &req{requests[id]}; + if (req) { + req->freqHz = freqHz; + state.logger->Debug("Set frequency for request {}: {} Hz", id, freqHz); + return {}; + } + } + + // This doesn't return any errors in HOS + state.logger->Warn("Tried to set frequency for unregistered request {}", id); + return {}; + } + + Result IRequest::Get(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + u32 id{request.Pop()}; + u32 freqHz{request.Pop()}; + + std::lock_guard lock(requestsMutex); + if (id >= requests.size()) { + auto &req{requests[id]}; + if (req) { + state.logger->Debug("Get frequency for request {}: {} Hz", id, req->freqHz); + response.Push(req->freqHz); + return {}; + } + } + + // This doesn't return any errors in HOS + state.logger->Warn("Tried to get frequency of unregistered request {}", id); + response.Push(0); + return {}; + } +} diff --git a/app/src/main/cpp/skyline/services/mmnv/IRequest.h b/app/src/main/cpp/skyline/services/mmnv/IRequest.h new file mode 100644 index 00000000..b72e7783 --- /dev/null +++ b/app/src/main/cpp/skyline/services/mmnv/IRequest.h @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include +#include + +namespace skyline::service::mmnv { + /** + * @brief IRequest or mm:u is used to control clocks and powergating of various hardware modules + * @url https://switchbrew.org/wiki/Display_services#mm:u + */ + class IRequest : public BaseService { + private: + /** + * @brief Enumerates the modules that can be controlled by mmnv, these are passed directly to FGM services + */ + enum class ModuleType : u32 { + Ram = 2, + NvEnc = 5, + NvDec = 6, + NvJpg = 7 + }; + + /** + * @brief Holds a single mmnv request, detailing its target module and current frequency + */ + struct Request { + ModuleType module; + u32 freqHz; + }; + + std::mutex requestsMutex; // Protects accesses to requests + std::vector> requests; //!< Holds allocated requests with the index corresponding to the request ID + + /** + * @brief Holds the result of a request allocation + */ + struct Allocation { + Request &request; + u32 id; + }; + + /* + * @note requestsMutex should be locked when calling this + * @return A reference to an empty request + */ + Allocation AllocateRequest(); + + public: + IRequest(const DeviceState &state, ServiceManager &manager); + + /** + * @brief Initialises the request for the given module ID + */ + Result InitializeOld(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief Finalises the request for the given module ID + */ + Result FinalizeOld(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief Sets the target frequency in HZ for the given module and waits for it to be applied + */ + Result SetAndWaitOld(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief Gets the frequency in HZ for the given module + */ + Result GetOld(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief Initialises a new request for the given module ID and returns a new request ID + */ + Result Initialize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief Finalises the request with the given ID + */ + Result Finalize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief Sets the target frequency in HZ for the request with the given ID and waits for it to be applied + */ + Result SetAndWait(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief Gets the frequency in HZ for the request with the given ID + */ + Result Get(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + SERVICE_DECL( + SFUNC(0x0, IRequest, InitializeOld), + SFUNC(0x1, IRequest, FinalizeOld), + SFUNC(0x2, IRequest, SetAndWaitOld), + SFUNC(0x3, IRequest, GetOld), + SFUNC(0x4, IRequest, Initialize), + SFUNC(0x5, IRequest, Finalize), + SFUNC(0x6, IRequest, SetAndWait), + SFUNC(0x7, IRequest, Get) + ) + }; +} diff --git a/app/src/main/cpp/skyline/services/serviceman.cpp b/app/src/main/cpp/skyline/services/serviceman.cpp index 79bd735f..8b955941 100644 --- a/app/src/main/cpp/skyline/services/serviceman.cpp +++ b/app/src/main/cpp/skyline/services/serviceman.cpp @@ -30,6 +30,7 @@ #include "socket/bsd/IClient.h" #include "ssl/ISslService.h" #include "prepo/IPrepoService.h" +#include "mmnv/IRequest.h" #include "serviceman.h" #define SERVICE_CASE(class, name, ...) \ @@ -90,6 +91,7 @@ namespace skyline::service { SERVICE_CASE(socket::IClient, "bsd:u") SERVICE_CASE(ssl::ISslService, "ssl") SERVICE_CASE(prepo::IPrepoService, "prepo:u") + SERVICE_CASE(mmnv::IRequest, "mm:u") default: std::string_view nameString(span(reinterpret_cast(&name), sizeof(name)).as_string(true)); throw std::out_of_range(fmt::format("CreateService called with an unknown service name: {}", nameString));