From b0b7843d5c7372db60211255be0edfcb97dcb56d Mon Sep 17 00:00:00 2001
From: Ac_K <Acoustik666@gmail.com>
Date: Thu, 17 Aug 2023 14:59:05 +0200
Subject: [PATCH] mm: Migrate service in Horizon project (#5580)

* mm: Migrate service in Horizon project

This PR migrate the `mm:u` service to the Horizon project, things were checked by some RE aswell, that's why some vars are renamed, the logic should be the same as before.

Tests are welcome.

* Lock _sessionList instead

* Fix comment

* Fix Session fields order
---
 src/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs   | 196 ------------------
 .../Mm/Types/MultiMediaOperationType.cs       |  10 -
 .../Services/Mm/Types/MultiMediaSession.cs    |  24 ---
 src/Ryujinx.Horizon/MmNv/Ipc/Request.cs       | 160 ++++++++++++++
 src/Ryujinx.Horizon/MmNv/MmNvIpcServer.cs     |  43 ++++
 src/Ryujinx.Horizon/MmNv/MmNvMain.cs          |  17 ++
 src/Ryujinx.Horizon/Sdk/MmNv/IRequest.cs      |  17 ++
 src/Ryujinx.Horizon/Sdk/MmNv/Module.cs        |  15 ++
 src/Ryujinx.Horizon/Sdk/MmNv/Session.cs       |  26 +++
 src/Ryujinx.Horizon/ServiceTable.cs           |   2 +
 10 files changed, 280 insertions(+), 230 deletions(-)
 delete mode 100644 src/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs
 delete mode 100644 src/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaOperationType.cs
 delete mode 100644 src/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaSession.cs
 create mode 100644 src/Ryujinx.Horizon/MmNv/Ipc/Request.cs
 create mode 100644 src/Ryujinx.Horizon/MmNv/MmNvIpcServer.cs
 create mode 100644 src/Ryujinx.Horizon/MmNv/MmNvMain.cs
 create mode 100644 src/Ryujinx.Horizon/Sdk/MmNv/IRequest.cs
 create mode 100644 src/Ryujinx.Horizon/Sdk/MmNv/Module.cs
 create mode 100644 src/Ryujinx.Horizon/Sdk/MmNv/Session.cs

diff --git a/src/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs b/src/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs
deleted file mode 100644
index 31d325a94..000000000
--- a/src/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs
+++ /dev/null
@@ -1,196 +0,0 @@
-using Ryujinx.Common.Logging;
-using Ryujinx.HLE.HOS.Services.Mm.Types;
-using System.Collections.Generic;
-
-namespace Ryujinx.HLE.HOS.Services.Mm
-{
-    [Service("mm:u")]
-    class IRequest : IpcService
-    {
-        private readonly object _sessionListLock = new();
-        private readonly List<MultiMediaSession> _sessionList = new();
-
-        private uint _uniqueId = 1;
-
-        public IRequest(ServiceCtx context) { }
-
-        [CommandCmif(0)]
-        // InitializeOld(u32, u32, u32)
-        public ResultCode InitializeOld(ServiceCtx context)
-        {
-            MultiMediaOperationType operationType = (MultiMediaOperationType)context.RequestData.ReadUInt32();
-            int fgmId = context.RequestData.ReadInt32();
-            bool isAutoClearEvent = context.RequestData.ReadInt32() != 0;
-
-            Logger.Stub?.PrintStub(LogClass.ServiceMm, new { operationType, fgmId, isAutoClearEvent });
-
-            Register(operationType, fgmId, isAutoClearEvent);
-
-            return ResultCode.Success;
-        }
-
-        [CommandCmif(1)]
-        // FinalizeOld(u32)
-        public ResultCode FinalizeOld(ServiceCtx context)
-        {
-            MultiMediaOperationType operationType = (MultiMediaOperationType)context.RequestData.ReadUInt32();
-
-            Logger.Stub?.PrintStub(LogClass.ServiceMm, new { operationType });
-
-            lock (_sessionListLock)
-            {
-                _sessionList.Remove(GetSessionByType(operationType));
-            }
-
-            return ResultCode.Success;
-        }
-
-        [CommandCmif(2)]
-        // SetAndWaitOld(u32, u32, u32)
-        public ResultCode SetAndWaitOld(ServiceCtx context)
-        {
-            MultiMediaOperationType operationType = (MultiMediaOperationType)context.RequestData.ReadUInt32();
-            uint frequenceHz = context.RequestData.ReadUInt32();
-            int timeout = context.RequestData.ReadInt32();
-
-            Logger.Stub?.PrintStub(LogClass.ServiceMm, new { operationType, frequenceHz, timeout });
-
-            lock (_sessionListLock)
-            {
-                GetSessionByType(operationType)?.SetAndWait(frequenceHz, timeout);
-            }
-
-            return ResultCode.Success;
-        }
-
-        [CommandCmif(3)]
-        // GetOld(u32) -> u32
-        public ResultCode GetOld(ServiceCtx context)
-        {
-            MultiMediaOperationType operationType = (MultiMediaOperationType)context.RequestData.ReadUInt32();
-
-            Logger.Stub?.PrintStub(LogClass.ServiceMm, new { operationType });
-
-            lock (_sessionListLock)
-            {
-                MultiMediaSession session = GetSessionByType(operationType);
-
-                uint currentValue = session == null ? 0 : session.CurrentValue;
-
-                context.ResponseData.Write(currentValue);
-            }
-
-            return ResultCode.Success;
-        }
-
-        [CommandCmif(4)]
-        // Initialize(u32, u32, u32) -> u32
-        public ResultCode Initialize(ServiceCtx context)
-        {
-            MultiMediaOperationType operationType = (MultiMediaOperationType)context.RequestData.ReadUInt32();
-            int fgmId = context.RequestData.ReadInt32();
-            bool isAutoClearEvent = context.RequestData.ReadInt32() != 0;
-
-            Logger.Stub?.PrintStub(LogClass.ServiceMm, new { operationType, fgmId, isAutoClearEvent });
-
-            uint id = Register(operationType, fgmId, isAutoClearEvent);
-
-            context.ResponseData.Write(id);
-
-            return ResultCode.Success;
-        }
-
-        [CommandCmif(5)]
-        // Finalize(u32)
-        public ResultCode Finalize(ServiceCtx context)
-        {
-            uint id = context.RequestData.ReadUInt32();
-
-            Logger.Stub?.PrintStub(LogClass.ServiceMm, new { id });
-
-            lock (_sessionListLock)
-            {
-                _sessionList.Remove(GetSessionById(id));
-            }
-
-            return ResultCode.Success;
-        }
-
-        [CommandCmif(6)]
-        // SetAndWait(u32, u32, u32)
-        public ResultCode SetAndWait(ServiceCtx context)
-        {
-            uint id = context.RequestData.ReadUInt32();
-            uint frequenceHz = context.RequestData.ReadUInt32();
-            int timeout = context.RequestData.ReadInt32();
-
-            Logger.Stub?.PrintStub(LogClass.ServiceMm, new { id, frequenceHz, timeout });
-
-            lock (_sessionListLock)
-            {
-                GetSessionById(id)?.SetAndWait(frequenceHz, timeout);
-            }
-
-            return ResultCode.Success;
-        }
-
-        [CommandCmif(7)]
-        // Get(u32) -> u32
-        public ResultCode Get(ServiceCtx context)
-        {
-            uint id = context.RequestData.ReadUInt32();
-
-            Logger.Stub?.PrintStub(LogClass.ServiceMm, new { id });
-
-            lock (_sessionListLock)
-            {
-                MultiMediaSession session = GetSessionById(id);
-
-                uint currentValue = session == null ? 0 : session.CurrentValue;
-
-                context.ResponseData.Write(currentValue);
-            }
-
-            return ResultCode.Success;
-        }
-
-        private MultiMediaSession GetSessionById(uint id)
-        {
-            foreach (MultiMediaSession session in _sessionList)
-            {
-                if (session.Id == id)
-                {
-                    return session;
-                }
-            }
-
-            return null;
-        }
-
-        private MultiMediaSession GetSessionByType(MultiMediaOperationType type)
-        {
-            foreach (MultiMediaSession session in _sessionList)
-            {
-                if (session.Type == type)
-                {
-                    return session;
-                }
-            }
-
-            return null;
-        }
-
-        private uint Register(MultiMediaOperationType type, int fgmId, bool isAutoClearEvent)
-        {
-            lock (_sessionListLock)
-            {
-                // Nintendo ignore the fgm id as the other interfaces were deprecated.
-                MultiMediaSession session = new(_uniqueId++, type, isAutoClearEvent);
-
-                _sessionList.Add(session);
-
-                return session.Id;
-            }
-        }
-    }
-}
diff --git a/src/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaOperationType.cs b/src/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaOperationType.cs
deleted file mode 100644
index 095dbfc31..000000000
--- a/src/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaOperationType.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Mm.Types
-{
-    enum MultiMediaOperationType : uint
-    {
-        Ram = 2,
-        NvEnc = 5,
-        NvDec = 6,
-        NvJpg = 7,
-    }
-}
diff --git a/src/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaSession.cs b/src/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaSession.cs
deleted file mode 100644
index 32b52ca54..000000000
--- a/src/Ryujinx.HLE/HOS/Services/Mm/Types/MultiMediaSession.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Mm.Types
-{
-    class MultiMediaSession
-    {
-        public MultiMediaOperationType Type { get; }
-
-        public bool IsAutoClearEvent { get; }
-        public uint Id { get; }
-        public uint CurrentValue { get; private set; }
-
-        public MultiMediaSession(uint id, MultiMediaOperationType type, bool isAutoClearEvent)
-        {
-            Type = type;
-            Id = id;
-            IsAutoClearEvent = isAutoClearEvent;
-            CurrentValue = 0;
-        }
-
-        public void SetAndWait(uint value, int timeout)
-        {
-            CurrentValue = value;
-        }
-    }
-}
diff --git a/src/Ryujinx.Horizon/MmNv/Ipc/Request.cs b/src/Ryujinx.Horizon/MmNv/Ipc/Request.cs
new file mode 100644
index 000000000..9a24c75e8
--- /dev/null
+++ b/src/Ryujinx.Horizon/MmNv/Ipc/Request.cs
@@ -0,0 +1,160 @@
+using Ryujinx.Common.Logging;
+using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.Sdk.MmNv;
+using Ryujinx.Horizon.Sdk.Sf;
+using System.Collections.Generic;
+
+namespace Ryujinx.Horizon.MmNv.Ipc
+{
+    partial class Request : IRequest
+    {
+        private readonly List<Session> _sessionList = new();
+
+        private uint _uniqueId = 1;
+
+        [CmifCommand(0)]
+        public Result InitializeOld(Module module, uint fgmPriority, uint autoClearEvent)
+        {
+            bool isAutoClearEvent = autoClearEvent != 0;
+
+            Logger.Stub?.PrintStub(LogClass.ServiceMm, new { module, fgmPriority, isAutoClearEvent });
+
+            Register(module, fgmPriority, isAutoClearEvent);
+
+            return Result.Success;
+        }
+
+        [CmifCommand(1)]
+        public Result FinalizeOld(Module module)
+        {
+            Logger.Stub?.PrintStub(LogClass.ServiceMm, new { module });
+
+            lock (_sessionList)
+            {
+                _sessionList.Remove(GetSessionByModule(module));
+            }
+
+            return Result.Success;
+        }
+
+        [CmifCommand(2)]
+        public Result SetAndWaitOld(Module module, uint clockRateMin, int clockRateMax)
+        {
+            Logger.Stub?.PrintStub(LogClass.ServiceMm, new { module, clockRateMin, clockRateMax });
+
+            lock (_sessionList)
+            {
+                GetSessionByModule(module)?.SetAndWait(clockRateMin, clockRateMax);
+            }
+
+            return Result.Success;
+        }
+
+        [CmifCommand(3)]
+        public Result GetOld(out uint clockRateActual, Module module)
+        {
+            Logger.Stub?.PrintStub(LogClass.ServiceMm, new { module });
+
+            lock (_sessionList)
+            {
+                Session session = GetSessionByModule(module);
+
+                clockRateActual = session == null ? 0 : session.ClockRateMin;
+            }
+
+            return Result.Success;
+        }
+
+        [CmifCommand(4)]
+        public Result Initialize(out uint requestId, Module module, uint fgmPriority, uint autoClearEvent)
+        {
+            bool isAutoClearEvent = autoClearEvent != 0;
+
+            Logger.Stub?.PrintStub(LogClass.ServiceMm, new { module, fgmPriority, isAutoClearEvent });
+
+            requestId = Register(module, fgmPriority, isAutoClearEvent);
+
+            return Result.Success;
+        }
+
+        [CmifCommand(5)]
+        public Result Finalize(uint requestId)
+        {
+            Logger.Stub?.PrintStub(LogClass.ServiceMm, new { requestId });
+
+            lock (_sessionList)
+            {
+                _sessionList.Remove(GetSessionById(requestId));
+            }
+
+            return Result.Success;
+        }
+
+        [CmifCommand(6)]
+        public Result SetAndWait(uint requestId, uint clockRateMin, int clockRateMax)
+        {
+            Logger.Stub?.PrintStub(LogClass.ServiceMm, new { requestId, clockRateMin, clockRateMax });
+
+            lock (_sessionList)
+            {
+                GetSessionById(requestId)?.SetAndWait(clockRateMin, clockRateMax);
+            }
+
+            return Result.Success;
+        }
+
+        [CmifCommand(7)]
+        public Result Get(out uint clockRateActual, uint requestId)
+        {
+            Logger.Stub?.PrintStub(LogClass.ServiceMm, new { requestId });
+
+            lock (_sessionList)
+            {
+                Session session = GetSessionById(requestId);
+
+                clockRateActual = session == null ? 0 : session.ClockRateMin;
+            }
+
+            return Result.Success;
+        }
+
+        private Session GetSessionById(uint id)
+        {
+            foreach (Session session in _sessionList)
+            {
+                if (session.Id == id)
+                {
+                    return session;
+                }
+            }
+
+            return null;
+        }
+
+        private Session GetSessionByModule(Module module)
+        {
+            foreach (Session session in _sessionList)
+            {
+                if (session.Module == module)
+                {
+                    return session;
+                }
+            }
+
+            return null;
+        }
+
+        private uint Register(Module module, uint fgmPriority, bool isAutoClearEvent)
+        {
+            lock (_sessionList)
+            {
+                // Nintendo ignores the fgm priority as the other services were deprecated.
+                Session session = new(_uniqueId++, module, isAutoClearEvent);
+
+                _sessionList.Add(session);
+
+                return session.Id;
+            }
+        }
+    }
+}
diff --git a/src/Ryujinx.Horizon/MmNv/MmNvIpcServer.cs b/src/Ryujinx.Horizon/MmNv/MmNvIpcServer.cs
new file mode 100644
index 000000000..e60b25581
--- /dev/null
+++ b/src/Ryujinx.Horizon/MmNv/MmNvIpcServer.cs
@@ -0,0 +1,43 @@
+using Ryujinx.Horizon.MmNv.Ipc;
+using Ryujinx.Horizon.Sdk.Sf.Hipc;
+using Ryujinx.Horizon.Sdk.Sm;
+
+namespace Ryujinx.Horizon.MmNv
+{
+    class MmNvIpcServer
+    {
+        private const int MmNvMaxSessionsCount = 9;
+
+        private const int PointerBufferSize = 0;
+        private const int MaxDomains = 0;
+        private const int MaxDomainObjects = 0;
+        private const int MaxPortsCount = 1;
+
+        private static readonly ManagerOptions _mmNvOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
+
+        private SmApi _sm;
+        private ServerManager _serverManager;
+
+        public void Initialize()
+        {
+            HeapAllocator allocator = new();
+
+            _sm = new SmApi();
+            _sm.Initialize().AbortOnFailure();
+
+            _serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _mmNvOptions, MmNvMaxSessionsCount);
+
+            _serverManager.RegisterObjectForServer(new Request(), ServiceName.Encode("mm:u"), MmNvMaxSessionsCount);
+        }
+
+        public void ServiceRequests()
+        {
+            _serverManager.ServiceRequests();
+        }
+
+        public void Shutdown()
+        {
+            _serverManager.Dispose();
+        }
+    }
+}
diff --git a/src/Ryujinx.Horizon/MmNv/MmNvMain.cs b/src/Ryujinx.Horizon/MmNv/MmNvMain.cs
new file mode 100644
index 000000000..ac5eff1a9
--- /dev/null
+++ b/src/Ryujinx.Horizon/MmNv/MmNvMain.cs
@@ -0,0 +1,17 @@
+namespace Ryujinx.Horizon.MmNv
+{
+    class MmNvMain : IService
+    {
+        public static void Main(ServiceTable serviceTable)
+        {
+            MmNvIpcServer ipcServer = new();
+
+            ipcServer.Initialize();
+
+            serviceTable.SignalServiceReady();
+
+            ipcServer.ServiceRequests();
+            ipcServer.Shutdown();
+        }
+    }
+}
diff --git a/src/Ryujinx.Horizon/Sdk/MmNv/IRequest.cs b/src/Ryujinx.Horizon/Sdk/MmNv/IRequest.cs
new file mode 100644
index 000000000..300b957fd
--- /dev/null
+++ b/src/Ryujinx.Horizon/Sdk/MmNv/IRequest.cs
@@ -0,0 +1,17 @@
+using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.Sdk.Sf;
+
+namespace Ryujinx.Horizon.Sdk.MmNv
+{
+    interface IRequest : IServiceObject
+    {
+        Result InitializeOld(Module module, uint fgmPriority, uint autoClearEvent);
+        Result FinalizeOld(Module module);
+        Result SetAndWaitOld(Module module, uint clockRateMin, int clockRateMax);
+        Result GetOld(out uint clockRateActual, Module module);
+        Result Initialize(out uint requestId, Module module, uint fgmPriority, uint autoClearEvent);
+        Result Finalize(uint requestId);
+        Result SetAndWait(uint requestId, uint clockRateMin, int clockRateMax);
+        Result Get(out uint clockRateActual, uint requestId);
+    }
+}
diff --git a/src/Ryujinx.Horizon/Sdk/MmNv/Module.cs b/src/Ryujinx.Horizon/Sdk/MmNv/Module.cs
new file mode 100644
index 000000000..e029d037b
--- /dev/null
+++ b/src/Ryujinx.Horizon/Sdk/MmNv/Module.cs
@@ -0,0 +1,15 @@
+namespace Ryujinx.Horizon.Sdk.MmNv
+{
+    enum Module : uint
+    {
+        Cpu,
+        Gpu,
+        Emc,
+        SysBus,
+        MSelect,
+        NvDec,
+        NvEnc,
+        NvJpg,
+        Test,
+    }
+}
diff --git a/src/Ryujinx.Horizon/Sdk/MmNv/Session.cs b/src/Ryujinx.Horizon/Sdk/MmNv/Session.cs
new file mode 100644
index 000000000..b91585693
--- /dev/null
+++ b/src/Ryujinx.Horizon/Sdk/MmNv/Session.cs
@@ -0,0 +1,26 @@
+namespace Ryujinx.Horizon.Sdk.MmNv
+{
+    class Session
+    {
+        public Module Module { get; }
+        public uint Id { get; }
+        public bool IsAutoClearEvent { get; }
+        public uint ClockRateMin { get; private set; }
+        public int ClockRateMax { get; private set; }
+
+        public Session(uint id, Module module, bool isAutoClearEvent)
+        {
+            Module = module;
+            Id = id;
+            IsAutoClearEvent = isAutoClearEvent;
+            ClockRateMin = 0;
+            ClockRateMax = -1;
+        }
+
+        public void SetAndWait(uint clockRateMin, int clockRateMax)
+        {
+            ClockRateMin = clockRateMin;
+            ClockRateMax = clockRateMax;
+        }
+    }
+}
diff --git a/src/Ryujinx.Horizon/ServiceTable.cs b/src/Ryujinx.Horizon/ServiceTable.cs
index d47f91bf9..8dfacbebd 100644
--- a/src/Ryujinx.Horizon/ServiceTable.cs
+++ b/src/Ryujinx.Horizon/ServiceTable.cs
@@ -1,5 +1,6 @@
 using Ryujinx.Horizon.Bcat;
 using Ryujinx.Horizon.LogManager;
+using Ryujinx.Horizon.MmNv;
 using Ryujinx.Horizon.Prepo;
 using System.Collections.Generic;
 using System.Threading;
@@ -25,6 +26,7 @@ namespace Ryujinx.Horizon
             RegisterService<LmMain>();
             RegisterService<PrepoMain>();
             RegisterService<BcatMain>();
+            RegisterService<MmNvMain>();
 
             _totalServices = entries.Count;