Просмотр исходного кода

lbl: Migrate service to Horizon (#5628)

* lbl: Migrate service to Horizon

* Fix formatting

* Addresses gdkchan's feedback

* Fix comments
Ac_K 2 лет назад
Родитель
Сommit
e6700b314f

+ 5 - 4
src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs

@@ -5,6 +5,7 @@ using Ryujinx.HLE.HOS.Services.Settings.Types;
 using Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService;
 using Ryujinx.HLE.HOS.SystemState;
 using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.Sdk.Lbl;
 using System;
 
 namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy
@@ -15,7 +16,6 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
 
         private readonly Apm.ManagerServer _apmManagerServer;
         private readonly Apm.SystemManagerServer _apmSystemManagerServer;
-        private readonly Lbl.LblControllerServer _lblControllerServer;
 
         private bool _vrModeEnabled;
 #pragma warning disable CS0414, IDE0052 // Remove unread private member
@@ -34,7 +34,6 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
 
             _apmManagerServer = new Apm.ManagerServer(context);
             _apmSystemManagerServer = new Apm.SystemManagerServer(context);
-            _lblControllerServer = new Lbl.LblControllerServer(context);
 
             _acquiredSleepLockEvent = new KEvent(context.Device.System.KernelContext);
         }
@@ -215,13 +214,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
 
             _vrModeEnabled = vrModeEnabled;
 
+            using var lblApi = new LblApi();
+
             if (vrModeEnabled)
             {
-                _lblControllerServer.EnableVrMode();
+                lblApi.EnableVrMode().AbortOnFailure();
             }
             else
             {
-                _lblControllerServer.DisableVrMode();
+                lblApi.DisableVrMode().AbortOnFailure();
             }
 
             // TODO: It signals an internal event of ICommonStateGetter. We have to determine where this event is used.

+ 0 - 92
src/Ryujinx.HLE/HOS/Services/Lbl/ILblController.cs

@@ -1,92 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Lbl
-{
-    abstract class ILblController : IpcService
-    {
-        public ILblController(ServiceCtx context) { }
-
-        protected abstract void SetCurrentBrightnessSettingForVrMode(float currentBrightnessSettingForVrMode);
-        protected abstract float GetCurrentBrightnessSettingForVrMode();
-        internal abstract void EnableVrMode();
-        internal abstract void DisableVrMode();
-        protected abstract bool IsVrModeEnabled();
-
-        [CommandCmif(17)]
-        // SetBrightnessReflectionDelayLevel(float, float)
-        public ResultCode SetBrightnessReflectionDelayLevel(ServiceCtx context)
-        {
-            return ResultCode.Success;
-        }
-
-        [CommandCmif(18)]
-        // GetBrightnessReflectionDelayLevel(float) -> float
-        public ResultCode GetBrightnessReflectionDelayLevel(ServiceCtx context)
-        {
-            context.ResponseData.Write(0.0f);
-
-            return ResultCode.Success;
-        }
-
-        [CommandCmif(21)]
-        // SetCurrentAmbientLightSensorMapping(unknown<0xC>)
-        public ResultCode SetCurrentAmbientLightSensorMapping(ServiceCtx context)
-        {
-            return ResultCode.Success;
-        }
-
-        [CommandCmif(22)]
-        // GetCurrentAmbientLightSensorMapping() -> unknown<0xC>
-        public ResultCode GetCurrentAmbientLightSensorMapping(ServiceCtx context)
-        {
-            return ResultCode.Success;
-        }
-
-        [CommandCmif(24)] // 3.0.0+
-        // SetCurrentBrightnessSettingForVrMode(float)
-        public ResultCode SetCurrentBrightnessSettingForVrMode(ServiceCtx context)
-        {
-            float currentBrightnessSettingForVrMode = context.RequestData.ReadSingle();
-
-            SetCurrentBrightnessSettingForVrMode(currentBrightnessSettingForVrMode);
-
-            return ResultCode.Success;
-        }
-
-        [CommandCmif(25)] // 3.0.0+
-        // GetCurrentBrightnessSettingForVrMode() -> float
-        public ResultCode GetCurrentBrightnessSettingForVrMode(ServiceCtx context)
-        {
-            float currentBrightnessSettingForVrMode = GetCurrentBrightnessSettingForVrMode();
-
-            context.ResponseData.Write(currentBrightnessSettingForVrMode);
-
-            return ResultCode.Success;
-        }
-
-        [CommandCmif(26)] // 3.0.0+
-        // EnableVrMode()
-        public ResultCode EnableVrMode(ServiceCtx context)
-        {
-            EnableVrMode();
-
-            return ResultCode.Success;
-        }
-
-        [CommandCmif(27)] // 3.0.0+
-        // DisableVrMode()
-        public ResultCode DisableVrMode(ServiceCtx context)
-        {
-            DisableVrMode();
-
-            return ResultCode.Success;
-        }
-
-        [CommandCmif(28)] // 3.0.0+
-        // IsVrModeEnabled() -> bool
-        public ResultCode IsVrModeEnabled(ServiceCtx context)
-        {
-            context.ResponseData.Write(IsVrModeEnabled());
-
-            return ResultCode.Success;
-        }
-    }
-}

+ 0 - 54
src/Ryujinx.HLE/HOS/Services/Lbl/LblControllerServer.cs

@@ -1,54 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Lbl
-{
-    [Service("lbl")]
-    class LblControllerServer : ILblController
-    {
-        private bool _vrModeEnabled;
-        private float _currentBrightnessSettingForVrMode;
-
-        public LblControllerServer(ServiceCtx context) : base(context) { }
-
-        protected override void SetCurrentBrightnessSettingForVrMode(float currentBrightnessSettingForVrMode)
-        {
-            if (float.IsNaN(currentBrightnessSettingForVrMode) || float.IsInfinity(currentBrightnessSettingForVrMode))
-            {
-                _currentBrightnessSettingForVrMode = 0.0f;
-
-                return;
-            }
-
-            _currentBrightnessSettingForVrMode = currentBrightnessSettingForVrMode;
-        }
-
-        protected override float GetCurrentBrightnessSettingForVrMode()
-        {
-            if (float.IsNaN(_currentBrightnessSettingForVrMode) || float.IsInfinity(_currentBrightnessSettingForVrMode))
-            {
-                return 0.0f;
-            }
-
-            return _currentBrightnessSettingForVrMode;
-        }
-
-        internal override void EnableVrMode()
-        {
-            _vrModeEnabled = true;
-
-            // NOTE: Service check _vrModeEnabled field value in a thread and then change the screen brightness.
-            //       Since we don't support that. It's fine to do nothing.
-        }
-
-        internal override void DisableVrMode()
-        {
-            _vrModeEnabled = false;
-
-            // NOTE: Service check _vrModeEnabled field value in a thread and then change the screen brightness.
-            //       Since we don't support that. It's fine to do nothing.
-        }
-
-        protected override bool IsVrModeEnabled()
-        {
-            return _vrModeEnabled;
-        }
-    }
-}

+ 8 - 0
src/Ryujinx.HLE/HOS/Services/ServerBase.cs

@@ -6,6 +6,7 @@ using Ryujinx.HLE.HOS.Kernel;
 using Ryujinx.HLE.HOS.Kernel.Ipc;
 using Ryujinx.HLE.HOS.Kernel.Process;
 using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.Horizon;
 using Ryujinx.Horizon.Common;
 using System;
 using System.Buffers;
@@ -172,6 +173,13 @@ namespace Ryujinx.HLE.HOS.Services
             _selfProcess = KernelStatic.GetCurrentProcess();
             _selfThread = KernelStatic.GetCurrentThread();
 
+            HorizonStatic.Register(
+                default,
+                _context.Syscall,
+                _selfProcess.CpuMemory,
+                _selfThread.ThreadContext,
+                (int)_selfThread.ThreadContext.GetX(1));
+
             if (SmObjectFactory != null)
             {
                 _context.Syscall.ManageNamedPort(out int serverPortHandle, "sm:", 50);

+ 8 - 8
src/Ryujinx.Horizon/Bcat/BcatIpcServer.cs

@@ -6,8 +6,8 @@ namespace Ryujinx.Horizon.Bcat
 {
     internal class BcatIpcServer
     {
-        private const int BcatMaxSessionsCount = 8;
-        private const int BcatTotalMaxSessionsCount = BcatMaxSessionsCount * 4;
+        private const int MaxSessionsCount = 8;
+        private const int TotalMaxSessionsCount = MaxSessionsCount * 4;
 
         private const int PointerBufferSize = 0x400;
         private const int MaxDomains = 64;
@@ -17,7 +17,7 @@ namespace Ryujinx.Horizon.Bcat
         private SmApi _sm;
         private BcatServerManager _serverManager;
 
-        private static readonly ManagerOptions _bcatManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
+        private static readonly ManagerOptions _managerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
 
         internal void Initialize()
         {
@@ -26,13 +26,13 @@ namespace Ryujinx.Horizon.Bcat
             _sm = new SmApi();
             _sm.Initialize().AbortOnFailure();
 
-            _serverManager = new BcatServerManager(allocator, _sm, MaxPortsCount, _bcatManagerOptions, BcatTotalMaxSessionsCount);
+            _serverManager = new BcatServerManager(allocator, _sm, MaxPortsCount, _managerOptions, TotalMaxSessionsCount);
 
 #pragma warning disable IDE0055 // Disable formatting
-            _serverManager.RegisterServer((int)BcatPortIndex.Admin,   ServiceName.Encode("bcat:a"), BcatMaxSessionsCount);
-            _serverManager.RegisterServer((int)BcatPortIndex.Manager, ServiceName.Encode("bcat:m"), BcatMaxSessionsCount);
-            _serverManager.RegisterServer((int)BcatPortIndex.User,    ServiceName.Encode("bcat:u"), BcatMaxSessionsCount);
-            _serverManager.RegisterServer((int)BcatPortIndex.System,  ServiceName.Encode("bcat:s"), BcatMaxSessionsCount);
+            _serverManager.RegisterServer((int)BcatPortIndex.Admin,   ServiceName.Encode("bcat:a"), MaxSessionsCount);
+            _serverManager.RegisterServer((int)BcatPortIndex.Manager, ServiceName.Encode("bcat:m"), MaxSessionsCount);
+            _serverManager.RegisterServer((int)BcatPortIndex.User,    ServiceName.Encode("bcat:u"), MaxSessionsCount);
+            _serverManager.RegisterServer((int)BcatPortIndex.System,  ServiceName.Encode("bcat:s"), MaxSessionsCount);
 #pragma warning restore IDE0055
         }
 

+ 1 - 1
src/Ryujinx.Horizon/HorizonStatic.cs

@@ -4,7 +4,7 @@ using System;
 
 namespace Ryujinx.Horizon
 {
-    static class HorizonStatic
+    public static class HorizonStatic
     {
         [ThreadStatic]
         private static HorizonOptions _options;

+ 130 - 0
src/Ryujinx.Horizon/Lbl/Ipc/LblController.cs

@@ -0,0 +1,130 @@
+using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.Sdk.Lbl;
+using Ryujinx.Horizon.Sdk.Sf;
+
+namespace Ryujinx.Horizon.Lbl.Ipc
+{
+    partial class LblController : ILblController
+    {
+        private bool _vrModeEnabled;
+        private float _currentBrightnessSettingForVrMode;
+
+        [CmifCommand(17)]
+        public Result SetBrightnessReflectionDelayLevel(float unknown0, float unknown1)
+        {
+            // NOTE: Stubbed in system module.
+
+            return Result.Success;
+        }
+
+        [CmifCommand(18)]
+        public Result GetBrightnessReflectionDelayLevel(out float unknown1, float unknown0)
+        {
+            // NOTE: Stubbed in system module.
+
+            unknown1 = 0.0f;
+
+            return Result.Success;
+        }
+
+        [CmifCommand(19)]
+        public Result SetCurrentBrightnessMapping(float unknown0, float unknown1, float unknown2)
+        {
+            // NOTE: Stubbed in system module.
+
+            return Result.Success;
+        }
+
+        [CmifCommand(20)]
+        public Result GetCurrentBrightnessMapping(out float unknown0, out float unknown1, out float unknown2)
+        {
+            // NOTE: Stubbed in system module.
+
+            unknown0 = 0.0f;
+            unknown1 = 0.0f;
+            unknown2 = 0.0f;
+
+            return Result.Success;
+        }
+
+        [CmifCommand(21)]
+        public Result SetCurrentAmbientLightSensorMapping(float unknown0, float unknown1, float unknown2)
+        {
+            // NOTE: Stubbed in system module.
+
+            return Result.Success;
+        }
+
+        [CmifCommand(22)]
+        public Result GetCurrentAmbientLightSensorMapping(out float unknown0, out float unknown1, out float unknown2)
+        {
+            // NOTE: Stubbed in system module.
+
+            unknown0 = 0.0f;
+            unknown1 = 0.0f;
+            unknown2 = 0.0f;
+
+            return Result.Success;
+        }
+
+        [CmifCommand(24)]
+        public Result SetCurrentBrightnessSettingForVrMode(float currentBrightnessSettingForVrMode)
+        {
+            if (float.IsNaN(currentBrightnessSettingForVrMode) || float.IsInfinity(currentBrightnessSettingForVrMode))
+            {
+                _currentBrightnessSettingForVrMode = 0.0f;
+            }
+            else
+            {
+                _currentBrightnessSettingForVrMode = currentBrightnessSettingForVrMode;
+            }
+
+            return Result.Success;
+        }
+
+        [CmifCommand(25)]
+        public Result GetCurrentBrightnessSettingForVrMode(out float currentBrightnessSettingForVrMode)
+        {
+            if (float.IsNaN(_currentBrightnessSettingForVrMode) || float.IsInfinity(_currentBrightnessSettingForVrMode))
+            {
+                currentBrightnessSettingForVrMode = 0.0f;
+            }
+            else
+            {
+                currentBrightnessSettingForVrMode = _currentBrightnessSettingForVrMode;
+            }
+
+            return Result.Success;
+        }
+
+        [CmifCommand(26)]
+        public Result EnableVrMode()
+        {
+            _vrModeEnabled = true;
+
+            // NOTE: The service checks _vrModeEnabled field value in a thread and then changes the screen brightness.
+            //       Since we don't support that, it's fine to do nothing.
+
+            return Result.Success;
+        }
+
+        [CmifCommand(27)]
+        public Result DisableVrMode()
+        {
+            _vrModeEnabled = false;
+
+            // NOTE: The service checks _vrModeEnabled field value in a thread and then changes the screen brightness.
+            //       Since we don't support that, it's fine to do nothing.
+
+            return Result.Success;
+        }
+
+        [CmifCommand(28)]
+        public Result IsVrModeEnabled(out bool vrModeEnabled)
+        {
+            vrModeEnabled = _vrModeEnabled;
+
+            return Result.Success;
+        }
+    }
+}

+ 43 - 0
src/Ryujinx.Horizon/Lbl/LblIpcServer.cs

@@ -0,0 +1,43 @@
+using Ryujinx.Horizon.Lbl.Ipc;
+using Ryujinx.Horizon.Sdk.Sf.Hipc;
+using Ryujinx.Horizon.Sdk.Sm;
+
+namespace Ryujinx.Horizon.Lbl
+{
+    class LblIpcServer
+    {
+        private const int MaxSessionsCount = 5;
+
+        private const int PointerBufferSize = 0;
+        private const int MaxDomains = 0;
+        private const int MaxDomainObjects = 0;
+        private const int MaxPortsCount = 1;
+
+        private static readonly ManagerOptions _managerOptions = 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, _managerOptions, MaxSessionsCount);
+
+            _serverManager.RegisterObjectForServer(new LblController(), ServiceName.Encode("lbl"), MaxSessionsCount);
+        }
+
+        public void ServiceRequests()
+        {
+            _serverManager.ServiceRequests();
+        }
+
+        public void Shutdown()
+        {
+            _serverManager.Dispose();
+        }
+    }
+}

+ 17 - 0
src/Ryujinx.Horizon/Lbl/LblMain.cs

@@ -0,0 +1,17 @@
+namespace Ryujinx.Horizon.Lbl
+{
+    class LblMain : IService
+    {
+        public static void Main(ServiceTable serviceTable)
+        {
+            LblIpcServer ipcServer = new();
+
+            ipcServer.Initialize();
+
+            serviceTable.SignalServiceReady();
+
+            ipcServer.ServiceRequests();
+            ipcServer.Shutdown();
+        }
+    }
+}

+ 4 - 4
src/Ryujinx.Horizon/LogManager/LmIpcServer.cs

@@ -6,14 +6,14 @@ namespace Ryujinx.Horizon.LogManager
 {
     class LmIpcServer
     {
-        private const int LogMaxSessionsCount = 42;
+        private const int MaxSessionsCount = 42;
 
         private const int PointerBufferSize = 0x400;
         private const int MaxDomains = 31;
         private const int MaxDomainObjects = 61;
         private const int MaxPortsCount = 1;
 
-        private static readonly ManagerOptions _logManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
+        private static readonly ManagerOptions _managerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
 
         private SmApi _sm;
         private ServerManager _serverManager;
@@ -25,9 +25,9 @@ namespace Ryujinx.Horizon.LogManager
             _sm = new SmApi();
             _sm.Initialize().AbortOnFailure();
 
-            _serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _logManagerOptions, LogMaxSessionsCount);
+            _serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _managerOptions, MaxSessionsCount);
 
-            _serverManager.RegisterObjectForServer(new LogService(), ServiceName.Encode("lm"), LogMaxSessionsCount);
+            _serverManager.RegisterObjectForServer(new LogService(), ServiceName.Encode("lm"), MaxSessionsCount);
         }
 
         public void ServiceRequests()

+ 4 - 4
src/Ryujinx.Horizon/MmNv/MmNvIpcServer.cs

@@ -6,14 +6,14 @@ namespace Ryujinx.Horizon.MmNv
 {
     class MmNvIpcServer
     {
-        private const int MmNvMaxSessionsCount = 9;
+        private const int MaxSessionsCount = 40;
 
         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 static readonly ManagerOptions _managerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
 
         private SmApi _sm;
         private ServerManager _serverManager;
@@ -25,9 +25,9 @@ namespace Ryujinx.Horizon.MmNv
             _sm = new SmApi();
             _sm.Initialize().AbortOnFailure();
 
-            _serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _mmNvOptions, MmNvMaxSessionsCount);
+            _serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _managerOptions, MaxSessionsCount);
 
-            _serverManager.RegisterObjectForServer(new Request(), ServiceName.Encode("mm:u"), MmNvMaxSessionsCount);
+            _serverManager.RegisterObjectForServer(new Request(), ServiceName.Encode("mm:u"), MaxSessionsCount);
         }
 
         public void ServiceRequests()

+ 10 - 10
src/Ryujinx.Horizon/Prepo/PrepoIpcServer.cs

@@ -6,15 +6,15 @@ namespace Ryujinx.Horizon.Prepo
 {
     class PrepoIpcServer
     {
-        private const int PrepoMaxSessionsCount = 12;
-        private const int PrepoTotalMaxSessionsCount = PrepoMaxSessionsCount * 6;
+        private const int MaxSessionsCount = 12;
+        private const int TotalMaxSessionsCount = MaxSessionsCount * 6;
 
         private const int PointerBufferSize = 0x80;
         private const int MaxDomains = 64;
         private const int MaxDomainObjects = 16;
         private const int MaxPortsCount = 6;
 
-        private static readonly ManagerOptions _prepoManagerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
+        private static readonly ManagerOptions _managerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
 
         private SmApi _sm;
         private PrepoServerManager _serverManager;
@@ -26,15 +26,15 @@ namespace Ryujinx.Horizon.Prepo
             _sm = new SmApi();
             _sm.Initialize().AbortOnFailure();
 
-            _serverManager = new PrepoServerManager(allocator, _sm, MaxPortsCount, _prepoManagerOptions, PrepoTotalMaxSessionsCount);
+            _serverManager = new PrepoServerManager(allocator, _sm, MaxPortsCount, _managerOptions, TotalMaxSessionsCount);
 
 #pragma warning disable IDE0055 // Disable formatting
-            _serverManager.RegisterServer((int)PrepoPortIndex.Admin,   ServiceName.Encode("prepo:a"),  PrepoMaxSessionsCount); // 1.0.0-5.1.0
-            _serverManager.RegisterServer((int)PrepoPortIndex.Admin2,  ServiceName.Encode("prepo:a2"), PrepoMaxSessionsCount); // 6.0.0+
-            _serverManager.RegisterServer((int)PrepoPortIndex.Manager, ServiceName.Encode("prepo:m"),  PrepoMaxSessionsCount);
-            _serverManager.RegisterServer((int)PrepoPortIndex.User,    ServiceName.Encode("prepo:u"),  PrepoMaxSessionsCount);
-            _serverManager.RegisterServer((int)PrepoPortIndex.System,  ServiceName.Encode("prepo:s"),  PrepoMaxSessionsCount);
-            _serverManager.RegisterServer((int)PrepoPortIndex.Debug,   ServiceName.Encode("prepo:d"),  PrepoMaxSessionsCount); // 1.0.0
+            _serverManager.RegisterServer((int)PrepoPortIndex.Admin,   ServiceName.Encode("prepo:a"),  MaxSessionsCount); // 1.0.0-5.1.0
+            _serverManager.RegisterServer((int)PrepoPortIndex.Admin2,  ServiceName.Encode("prepo:a2"), MaxSessionsCount); // 6.0.0+
+            _serverManager.RegisterServer((int)PrepoPortIndex.Manager, ServiceName.Encode("prepo:m"),  MaxSessionsCount);
+            _serverManager.RegisterServer((int)PrepoPortIndex.User,    ServiceName.Encode("prepo:u"),  MaxSessionsCount);
+            _serverManager.RegisterServer((int)PrepoPortIndex.System,  ServiceName.Encode("prepo:s"),  MaxSessionsCount);
+            _serverManager.RegisterServer((int)PrepoPortIndex.Debug,   ServiceName.Encode("prepo:d"),  MaxSessionsCount); // 1.0.0
 #pragma warning restore IDE0055
         }
 

+ 20 - 0
src/Ryujinx.Horizon/Sdk/Lbl/ILblController.cs

@@ -0,0 +1,20 @@
+using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.Sdk.Sf;
+
+namespace Ryujinx.Horizon.Sdk.Lbl
+{
+    interface ILblController : IServiceObject
+    {
+        Result SetBrightnessReflectionDelayLevel(float unknown0, float unknown1);
+        Result GetBrightnessReflectionDelayLevel(out float unknown1, float unknown0);
+        Result SetCurrentBrightnessMapping(float unknown0, float unknown1, float unknown2);
+        Result GetCurrentBrightnessMapping(out float unknown0, out float unknown1, out float unknown2);
+        Result SetCurrentAmbientLightSensorMapping(float unknown0, float unknown1, float unknown2);
+        Result GetCurrentAmbientLightSensorMapping(out float unknown0, out float unknown1, out float unknown2);
+        Result SetCurrentBrightnessSettingForVrMode(float currentBrightnessSettingForVrMode);
+        Result GetCurrentBrightnessSettingForVrMode(out float currentBrightnessSettingForVrMode);
+        Result EnableVrMode();
+        Result DisableVrMode();
+        Result IsVrModeEnabled(out bool vrModeEnabled);
+    }
+}

+ 43 - 0
src/Ryujinx.Horizon/Sdk/Lbl/LblApi.cs

@@ -0,0 +1,43 @@
+using Ryujinx.Horizon.Common;
+using Ryujinx.Horizon.Sdk.Sm;
+using System;
+
+namespace Ryujinx.Horizon.Sdk.Lbl
+{
+    public class LblApi : IDisposable
+    {
+        private const string LblName = "lbl";
+
+        private int _sessionHandle;
+
+        public LblApi()
+        {
+            using var smApi = new SmApi();
+
+            smApi.Initialize();
+            smApi.GetServiceHandle(out _sessionHandle, ServiceName.Encode(LblName)).AbortOnFailure();
+        }
+
+        public Result EnableVrMode()
+        {
+            return ServiceUtil.SendRequest(out _, _sessionHandle, 26, sendPid: false, ReadOnlySpan<byte>.Empty);
+        }
+
+        public Result DisableVrMode()
+        {
+            return ServiceUtil.SendRequest(out _, _sessionHandle, 27, sendPid: false, ReadOnlySpan<byte>.Empty);
+        }
+
+        public void Dispose()
+        {
+            if (_sessionHandle != 0)
+            {
+                HorizonStatic.Syscall.CloseHandle(_sessionHandle);
+
+                _sessionHandle = 0;
+            }
+
+            GC.SuppressFinalize(this);
+        }
+    }
+}

+ 1 - 1
src/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs

@@ -4,7 +4,7 @@ using System.Runtime.InteropServices;
 namespace Ryujinx.Horizon.Sdk.Sm
 {
     [StructLayout(LayoutKind.Sequential, Pack = 1)]
-    readonly struct ServiceName
+    public readonly struct ServiceName
     {
         public static ServiceName Invalid { get; } = new(0);
 

+ 13 - 1
src/Ryujinx.Horizon/Sdk/Sm/SmApi.cs

@@ -5,7 +5,7 @@ using System;
 
 namespace Ryujinx.Horizon.Sdk.Sm
 {
-    class SmApi
+    public class SmApi : IDisposable
     {
         private const string SmName = "sm:";
 
@@ -109,5 +109,17 @@ namespace Ryujinx.Horizon.Sdk.Sm
 
             return ServiceUtil.SendRequest(out _, _portHandle, 4, sendPid: true, data);
         }
+
+        public void Dispose()
+        {
+            if (_portHandle != 0)
+            {
+                HorizonStatic.Syscall.CloseHandle(_portHandle);
+
+                _portHandle = 0;
+            }
+
+            GC.SuppressFinalize(this);
+        }
     }
 }

+ 4 - 2
src/Ryujinx.Horizon/ServiceTable.cs

@@ -1,4 +1,5 @@
 using Ryujinx.Horizon.Bcat;
+using Ryujinx.Horizon.Lbl;
 using Ryujinx.Horizon.LogManager;
 using Ryujinx.Horizon.MmNv;
 using Ryujinx.Horizon.Prepo;
@@ -23,10 +24,11 @@ namespace Ryujinx.Horizon
                 entries.Add(new ServiceEntry(T.Main, this, options));
             }
 
-            RegisterService<LmMain>();
-            RegisterService<PrepoMain>();
             RegisterService<BcatMain>();
+            RegisterService<LblMain>();
+            RegisterService<LmMain>();
             RegisterService<MmNvMain>();
+            RegisterService<PrepoMain>();
 
             _totalServices = entries.Count;