From 7920dc1d2f1eaeba1d1308b63443349dc9a799f1 Mon Sep 17 00:00:00 2001
From: Ac_K <Acoustik666@gmail.com>
Date: Sun, 21 Oct 2018 06:01:22 +0000
Subject: [PATCH] Implement basic psm service (#467)

* Implement basic psm service

- Add `IPsmServer`
- Stub `GetBatteryChargePercentage` & `GetChargerType`

* Fix wrong sorting

* Add IPsmSession

- Add `IPsmSession` by Thog
- Implement `OpenSession`
---
 Ryujinx.Common/Logging/LogClass.cs          |  1 +
 Ryujinx.HLE/HOS/Services/Psm/IPsmServer.cs  | 60 +++++++++++++
 Ryujinx.HLE/HOS/Services/Psm/IPsmSession.cs | 96 +++++++++++++++++++++
 Ryujinx.HLE/HOS/Services/ServiceFactory.cs  |  4 +
 4 files changed, 161 insertions(+)
 create mode 100644 Ryujinx.HLE/HOS/Services/Psm/IPsmServer.cs
 create mode 100644 Ryujinx.HLE/HOS/Services/Psm/IPsmSession.cs

diff --git a/Ryujinx.Common/Logging/LogClass.cs b/Ryujinx.Common/Logging/LogClass.cs
index 97ffb31477..8739fbc677 100644
--- a/Ryujinx.Common/Logging/LogClass.cs
+++ b/Ryujinx.Common/Logging/LogClass.cs
@@ -33,6 +33,7 @@ namespace Ryujinx.Common.Logging
         ServicePctl,
         ServicePl,
         ServicePrepo,
+        ServicePsm,
         ServiceSet,
         ServiceSfdnsres,
         ServiceSm,
diff --git a/Ryujinx.HLE/HOS/Services/Psm/IPsmServer.cs b/Ryujinx.HLE/HOS/Services/Psm/IPsmServer.cs
new file mode 100644
index 0000000000..0b45e3c9df
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Psm/IPsmServer.cs
@@ -0,0 +1,60 @@
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Services.Psm
+{
+    class IPsmServer : IpcService
+    {
+        enum ChargerType : int
+        {
+            None,
+            ChargerOrDock,
+            UsbC
+        }
+
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        public IPsmServer()
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+                { 0, GetBatteryChargePercentage },
+                { 1, GetChargerType             },
+                { 7, OpenSession                }
+            };
+        }
+
+        // GetBatteryChargePercentage() -> u32
+        public static long GetBatteryChargePercentage(ServiceCtx Context)
+        {
+            int ChargePercentage = 100;
+
+            Context.ResponseData.Write(ChargePercentage);
+
+            Logger.PrintStub(LogClass.ServicePsm, $"Stubbed. ChargePercentage: {ChargePercentage}");
+
+            return 0;
+        }
+
+        // GetChargerType() -> u32
+        public static long GetChargerType(ServiceCtx Context)
+        {
+            Context.ResponseData.Write((int)ChargerType.ChargerOrDock);
+
+            Logger.PrintStub(LogClass.ServicePsm, $"Stubbed. ChargerType: {ChargerType.ChargerOrDock}");
+
+            return 0;
+        }
+
+        // OpenSession() -> IPsmSession
+        public long OpenSession(ServiceCtx Context)
+        {
+            MakeObject(Context, new IPsmSession(Context.Device.System));
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Psm/IPsmSession.cs b/Ryujinx.HLE/HOS/Services/Psm/IPsmSession.cs
new file mode 100644
index 0000000000..30e5d53220
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Psm/IPsmSession.cs
@@ -0,0 +1,96 @@
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.HLE.HOS.Kernel;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Services.Psm
+{
+    class IPsmSession : IpcService
+    {
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        private KEvent StateChangeEvent;
+        private int    StateChangeEventHandle;
+
+        public IPsmSession(Horizon System)
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+                { 0, BindStateChangeEvent                     },
+                { 1, UnbindStateChangeEvent                   },
+                { 2, SetChargerTypeChangeEventEnabled         },
+                { 3, SetPowerSupplyChangeEventEnabled         },
+                { 4, SetBatteryVoltageStateChangeEventEnabled }
+            };
+
+            StateChangeEvent       = new KEvent(System);
+            StateChangeEventHandle = -1;
+        }
+
+        // BindStateChangeEvent() -> KObject
+        public long BindStateChangeEvent(ServiceCtx Context)
+        {
+            if (StateChangeEventHandle == -1)
+            {
+                KernelResult ResultCode = Context.Process.HandleTable.GenerateHandle(StateChangeEvent, out int StateChangeEventHandle);
+
+                if (ResultCode != KernelResult.Success)
+                {
+                    return (long)ResultCode;
+                }
+            }
+
+            Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(StateChangeEventHandle);
+
+            Logger.PrintStub(LogClass.ServicePsm, "Stubbed.");
+
+            return 0;
+        }
+
+        // UnbindStateChangeEvent()
+        public long UnbindStateChangeEvent(ServiceCtx Context)
+        {
+            if (StateChangeEventHandle != -1)
+            {
+                Context.Process.HandleTable.CloseHandle(StateChangeEventHandle);
+                StateChangeEventHandle = -1;
+            }
+
+            Logger.PrintStub(LogClass.ServicePsm, "Stubbed.");
+
+            return 0;
+        }
+
+        // SetChargerTypeChangeEventEnabled(u8)
+        public long SetChargerTypeChangeEventEnabled(ServiceCtx Context)
+        {
+            bool ChargerTypeChangeEventEnabled = Context.RequestData.ReadBoolean();
+
+            Logger.PrintStub(LogClass.ServicePsm, $"Stubbed. ChargerTypeChangeEventEnabled: {ChargerTypeChangeEventEnabled}");
+
+            return 0;
+        }
+
+        // SetPowerSupplyChangeEventEnabled(u8)
+        public long SetPowerSupplyChangeEventEnabled(ServiceCtx Context)
+        {
+            bool PowerSupplyChangeEventEnabled = Context.RequestData.ReadBoolean();
+
+            Logger.PrintStub(LogClass.ServicePsm, $"Stubbed. PowerSupplyChangeEventEnabled: {PowerSupplyChangeEventEnabled}");
+
+            return 0;
+        }
+
+        // SetBatteryVoltageStateChangeEventEnabled(u8)
+        public long SetBatteryVoltageStateChangeEventEnabled(ServiceCtx Context)
+        {
+            bool BatteryVoltageStateChangeEventEnabled = Context.RequestData.ReadBoolean();
+
+            Logger.PrintStub(LogClass.ServicePsm, $"Stubbed. BatteryVoltageStateChangeEventEnabled: {BatteryVoltageStateChangeEventEnabled}");
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/ServiceFactory.cs b/Ryujinx.HLE/HOS/Services/ServiceFactory.cs
index 29f1c0e775..5908e81082 100644
--- a/Ryujinx.HLE/HOS/Services/ServiceFactory.cs
+++ b/Ryujinx.HLE/HOS/Services/ServiceFactory.cs
@@ -16,6 +16,7 @@ using Ryujinx.HLE.HOS.Services.Nv;
 using Ryujinx.HLE.HOS.Services.Pctl;
 using Ryujinx.HLE.HOS.Services.Pl;
 using Ryujinx.HLE.HOS.Services.Prepo;
+using Ryujinx.HLE.HOS.Services.Psm;
 using Ryujinx.HLE.HOS.Services.Set;
 using Ryujinx.HLE.HOS.Services.Sfdnsres;
 using Ryujinx.HLE.HOS.Services.Sm;
@@ -152,6 +153,9 @@ namespace Ryujinx.HLE.HOS.Services
                 case "prepo:u":
                     return new IPrepoService();
 
+                case "psm":
+                    return new IPsmServer();
+
                 case "set":
                     return new ISettingsServer();