service: mm: Refactor Memory Management service implementation

Refactors the MM (Memory Management) service implementation with improved:

- Session management using a vector of Session objects instead of global vars
- Type safety with proper enums and member encapsulation
- Error handling with consistent patterns
- Modern C++ features (std::erase_if, std::find_if, emplace_back)
- Logging with detailed debug messages
- Code organization and const correctness

Key changes:
- Add Session class with proper encapsulation
- Add EventClearMode enum (Manual = 0, Auto = 1)
- Update Module enum naming for consistency
- Implement proper session tracking and lookup
- Add detailed parameter logging
- Use modern C++ algorithms for session management

The implementation now properly handles multiple sessions and matches
the documented nn::mmnv::IRequest interface more closely.

Refs: switchbrew.org/wiki/Display_services#mm:u

service/mm: add missing vector include and update copyright

- Add missing <vector> include for std::vector usage
- Add Citron copyright notice
- Maintain existing include ordering

This is a minor cleanup change to ensure proper header inclusion
and copyright attribution.
This commit is contained in:
Zephyron 2025-01-25 14:55:50 +10:00 committed by Mike Lothian
parent 49b17b42d0
commit 6f30786173
2 changed files with 106 additions and 35 deletions

View File

@ -2,6 +2,8 @@
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <vector>
#include "common/logging/log.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/mm/mm_u.h"
@ -25,7 +27,6 @@ public:
{7, &MM_U::Get, "Get"},
};
// clang-format on
RegisterHandlers(functions);
}
@ -33,11 +34,13 @@ private:
void InitializeOld(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto module = rp.PopEnum<Module>();
const auto priority = rp.Pop<Priority>();
const auto event_clear_mode = rp.PopEnum<EventClearMode>();
[[maybe_unused]] const auto priority = rp.Pop<u32>();
const auto clear_mode = static_cast<EventClearMode>(rp.Pop<u32>());
LOG_WARNING(Service_MM, "(STUBBED) called, module={:d}, priority={:d}, event_clear_mode={:d}",
static_cast<u32>(module), priority, static_cast<u32>(event_clear_mode));
LOG_DEBUG(Service_MM, "called. module={:d}, clear_mode={:d}",
static_cast<u32>(module), static_cast<u32>(clear_mode));
sessions.emplace_back(module, next_request_id++, clear_mode);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@ -47,7 +50,11 @@ private:
IPC::RequestParser rp{ctx};
const auto module = rp.PopEnum<Module>();
LOG_WARNING(Service_MM, "(STUBBED) called, module={:d}", static_cast<u32>(module));
LOG_DEBUG(Service_MM, "called. module={:d}", static_cast<u32>(module));
std::erase_if(sessions, [&module](const auto& session) {
return session.GetModule() == module;
});
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@ -58,13 +65,20 @@ private:
IPC::RequestParser rp{ctx};
const auto module = rp.PopEnum<Module>();
min = rp.Pop<Setting>();
max = rp.Pop<Setting>();
const auto min = rp.Pop<u32>();
const auto max = rp.Pop<s32>();
LOG_DEBUG(Service_MM, "(STUBBED) called, module={:d}, min=0x{:X}, max=0x{:X}",
LOG_DEBUG(Service_MM, "called. module={:d}, min=0x{:X}, max=0x{:X}",
static_cast<u32>(module), min, max);
current = min;
if (auto it = std::find_if(sessions.begin(), sessions.end(),
[&module](const auto& session) {
return session.GetModule() == module;
});
it != sessions.end()) {
it->SetAndWait(min, max);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
@ -73,31 +87,48 @@ private:
IPC::RequestParser rp{ctx};
const auto module = rp.PopEnum<Module>();
LOG_DEBUG(Service_MM, "(STUBBED) called, module={:d}", static_cast<u32>(module));
LOG_DEBUG(Service_MM, "called. module={:d}", static_cast<u32>(module));
u32 current_min = 0;
if (auto it = std::find_if(sessions.begin(), sessions.end(),
[&module](const auto& session) {
return session.GetModule() == module;
});
it != sessions.end()) {
current_min = it->GetMin();
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(current);
rb.Push(current_min);
}
void Initialize(HLERequestContext& ctx) {
LOG_WARNING(Service_MM, "(STUBBED) called.");
IPC::RequestParser rp{ctx};
module = rp.PopEnum<Module>();
priority = rp.Pop<Priority>();
event_clear_mode = rp.Pop<u32>();
const auto module = rp.PopEnum<Module>();
[[maybe_unused]] const auto priority = rp.Pop<u32>();
const auto clear_mode = static_cast<EventClearMode>(rp.Pop<u32>());
LOG_DEBUG(Service_MM, "called. module={:d}, clear_mode={:d}",
static_cast<u32>(module), static_cast<u32>(clear_mode));
const u32 current_id = next_request_id++;
sessions.emplace_back(module, current_id, clear_mode);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(request_id);
rb.Push(current_id);
}
void Finalize(HLERequestContext& ctx) {
LOG_WARNING(Service_MM, "(STUBBED) called.");
IPC::RequestParser rp{ctx};
request_id = rp.Pop<u32>();
const auto request_id = rp.Pop<u32>();
LOG_DEBUG(Service_MM, "called. request_id={:d}", request_id);
std::erase_if(sessions, [&request_id](const auto& session) {
return session.GetRequestId() == request_id;
});
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@ -107,35 +138,51 @@ private:
LOG_WARNING(Service_MM, "(STUBBED) called.");
IPC::RequestParser rp{ctx};
request_id = rp.Pop<u32>();
min = rp.Pop<Setting>();
max = rp.Pop<Setting>();
const auto request_id = rp.Pop<u32>();
const auto min = rp.Pop<u32>();
const auto max = rp.Pop<s32>();
LOG_DEBUG(Service_MM, "called. request_id={:d}, min=0x{:X}, max=0x{:X}",
request_id, min, max);
if (auto it = std::find_if(sessions.begin(), sessions.end(),
[&request_id](const auto& session) {
return session.GetRequestId() == request_id;
});
it != sessions.end()) {
it->SetAndWait(min, max);
}
current = min;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Get(HLERequestContext& ctx) {
LOG_WARNING(Service_MM, "(STUBBED) called.");
IPC::RequestParser rp{ctx};
request_id = rp.Pop<u32>();
const auto request_id = rp.Pop<u32>();
LOG_DEBUG(Service_MM, "called. request_id={:d}", request_id);
u32 current_min = 0;
if (auto it = std::find_if(sessions.begin(), sessions.end(),
[&request_id](const auto& session) {
return session.GetRequestId() == request_id;
});
it != sessions.end()) {
current_min = it->GetMin();
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(current);
rb.Push(current_min);
}
Module module{Module::TEST};
Priority priority{0};
Setting min{0}, max{0}, current{0};
u32 request_id{0}, event_clear_mode{0};
std::vector<Session> sessions;
u32 next_request_id{1};
};
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("mm:u", std::make_shared<MM_U>(system));
ServerManager::RunServer(std::move(server_manager));
}

View File

@ -4,6 +4,7 @@
#pragma once
#include <vector>
#include "common/common_types.h"
namespace Core {
@ -28,7 +29,8 @@ using Priority = u32;
using Setting = u32;
enum class EventClearMode : u32 {
// TODO: Add specific clear mode values when documented
Manual = 0,
Auto = 1,
};
// Consolidate settings into a struct for better organization
@ -39,6 +41,28 @@ struct Settings {
u32 id{1}; // Used by newer API versions
};
class Session {
public:
explicit Session(Module module_, u32 request_id_, EventClearMode clear_mode_)
: module{module_}, request_id{request_id_}, clear_mode{clear_mode_} {}
void SetAndWait(u32 min_, s32 max_) {
min = min_;
max = max_;
}
[[nodiscard]] u32 GetMin() const { return min; }
[[nodiscard]] Module GetModule() const { return module; }
[[nodiscard]] u32 GetRequestId() const { return request_id; }
private:
Module module;
u32 request_id{0};
u32 min{0};
s32 max{-1};
EventClearMode clear_mode;
};
void LoopProcess(Core::System& system);
} // namespace Service::MM