diff --git a/Ryujinx.Common/Logging/LogClass.cs b/Ryujinx.Common/Logging/LogClass.cs index b056f3836..43efd8d58 100644 --- a/Ryujinx.Common/Logging/LogClass.cs +++ b/Ryujinx.Common/Logging/LogClass.cs @@ -26,6 +26,7 @@ namespace Ryujinx.Common.Logging ServiceFs, ServiceHid, ServiceIrs, + ServiceLdn, ServiceLdr, ServiceLm, ServiceMm, diff --git a/Ryujinx.HLE/HOS/Services/Ldn/IUserServiceCreator.cs b/Ryujinx.HLE/HOS/Services/Ldn/IUserServiceCreator.cs index 052727dd3..3fc9ce1c7 100644 --- a/Ryujinx.HLE/HOS/Services/Ldn/IUserServiceCreator.cs +++ b/Ryujinx.HLE/HOS/Services/Ldn/IUserServiceCreator.cs @@ -1,8 +1,19 @@ -namespace Ryujinx.HLE.HOS.Services.Ldn +using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator; + +namespace Ryujinx.HLE.HOS.Services.Ldn { [Service("ldn:u")] class IUserServiceCreator : IpcService { public IUserServiceCreator(ServiceCtx context) { } + + [Command(0)] + // CreateUserLocalCommunicationService() -> object + public ResultCode CreateUserLocalCommunicationService(ServiceCtx context) + { + MakeObject(context, new IUserLocalCommunicationService(context)); + + return ResultCode.Success; + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Ldn/NetworkInterface.cs b/Ryujinx.HLE/HOS/Services/Ldn/NetworkInterface.cs new file mode 100644 index 000000000..f06c66446 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Ldn/NetworkInterface.cs @@ -0,0 +1,59 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.HLE.HOS.Services.Ldn.Types; +using System.Net; + +namespace Ryujinx.HLE.HOS.Services.Ldn +{ + internal class NetworkInterface + { + public ResultCode NifmState { get; set; } + public KEvent StateChangeEvent { get; private set; } + + private NetworkState _state; + + public NetworkInterface(Horizon system) + { + // TODO(Ac_K): Determine where the internal state is set. + NifmState = ResultCode.Success; + StateChangeEvent = new KEvent(system); + + _state = NetworkState.None; + } + + public ResultCode Initialize(int unknown, int version, IPAddress ipv4Address, IPAddress subnetMaskAddress) + { + // TODO(Ac_K): Call nn::nifm::InitializeSystem(). + // If the call failed, it returns the result code. + // If the call succeed, it signal and clear an event then start a new thread named nn.ldn.NetworkInterfaceMonitor. + + Logger.PrintStub(LogClass.ServiceLdn, new { version }); + + // NOTE: Since we don't support ldn for now, we can return this following result code to make it disabled. + return ResultCode.DeviceDisabled; + } + + public ResultCode GetState(out NetworkState state) + { + // Return ResultCode.InvalidArgument if _state is null, doesn't occur in our case. + + state = _state; + + return ResultCode.Success; + } + + public ResultCode Finalize() + { + // TODO(Ac_K): Finalize nifm service then kill the thread named nn.ldn.NetworkInterfaceMonitor. + + _state = NetworkState.None; + + StateChangeEvent.WritableEvent.Signal(); + StateChangeEvent.WritableEvent.Clear(); + + Logger.PrintStub(LogClass.ServiceLdn); + + return ResultCode.Success; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Ldn/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Ldn/ResultCode.cs new file mode 100644 index 000000000..0c9f6209c --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Ldn/ResultCode.cs @@ -0,0 +1,16 @@ +namespace Ryujinx.HLE.HOS.Services.Ldn +{ + enum ResultCode + { + ModuleId = 203, + ErrorCodeShift = 9, + + Success = 0, + + DeviceDisabled = (22 << ErrorCodeShift) | ModuleId, + InvalidState = (32 << ErrorCodeShift) | ModuleId, + Unknown1 = (48 << ErrorCodeShift) | ModuleId, + InvalidArgument = (96 << ErrorCodeShift) | ModuleId, + InvalidOjbect = (97 << ErrorCodeShift) | ModuleId, + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Ldn/Types/NetworkState.cs b/Ryujinx.HLE/HOS/Services/Ldn/Types/NetworkState.cs new file mode 100644 index 000000000..6ac204833 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Ldn/Types/NetworkState.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.HLE.HOS.Services.Ldn.Types +{ + enum NetworkState + { + None, + Initialized, + AccessPoint, + AccessPointCreated, + Station, + StationConnected, + Error + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs b/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs new file mode 100644 index 000000000..b1ae2d6ee --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs @@ -0,0 +1,88 @@ +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.HLE.HOS.Services.Ldn.Types; +using System; +using System.Net; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator +{ + class IUserLocalCommunicationService : IpcService + { + // TODO(Ac_K): Determine what the hardcoded unknown value is. + private const int UnknownValue = 90; + + private NetworkInterface _networkInterface; + + private int _stateChangeEventHandle = 0; + + public IUserLocalCommunicationService(ServiceCtx context) + { + _networkInterface = new NetworkInterface(context.Device.System); + } + + [Command(0)] + // GetState() -> s32 state + public ResultCode GetState(ServiceCtx context) + { + if (_networkInterface.NifmState != ResultCode.Success) + { + context.ResponseData.Write((int)NetworkState.Error); + + return ResultCode.Success; + } + + ResultCode result = _networkInterface.GetState(out NetworkState state); + + if (result == ResultCode.Success) + { + context.ResponseData.Write((int)state); + } + + return result; + } + + [Command(100)] + // AttachStateChangeEvent() -> handle + public ResultCode AttachStateChangeEvent(ServiceCtx context) + { + if (_stateChangeEventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(_networkInterface.StateChangeEvent.ReadableEvent, out _stateChangeEventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_stateChangeEventHandle); + + // Return ResultCode.InvalidArgument if handle is null, doesn't occur in our case since we already throw an Exception. + + return ResultCode.Success; + } + + [Command(400)] + // InitializeOld(u64, pid) + public ResultCode InitializeOld(ServiceCtx context) + { + return _networkInterface.Initialize(UnknownValue, 0, null, null); + } + + [Command(401)] + // Finalize() + public ResultCode Finalize(ServiceCtx context) + { + return _networkInterface.Finalize(); + } + + [Command(402)] // 7.0.0+ + // Initialize(u64 ip_addresses, u64, pid) + public ResultCode Initialize(ServiceCtx context) + { + // TODO(Ac_K): Determine what addresses are. + IPAddress unknownAddress1 = new IPAddress(context.RequestData.ReadUInt32()); + IPAddress unknownAddress2 = new IPAddress(context.RequestData.ReadUInt32()); + + return _networkInterface.Initialize(UnknownValue, version: 1, unknownAddress1, unknownAddress2); + } + } +} \ No newline at end of file