diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidUtils.cs b/Ryujinx.HLE/HOS/Services/Hid/HidUtils.cs
index 64252ce80e..051be7a88a 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/HidUtils.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/HidUtils.cs
@@ -23,5 +23,24 @@ namespace Ryujinx.HLE.HOS.Services.Hid
                 default: throw new ArgumentOutOfRangeException(nameof(npadIdType));
             }
         }
+
+        public static NpadIdType GetNpadIdTypeFromIndex(HidControllerId index)
+        {
+            switch (index)
+            {
+                case HidControllerId.ControllerPlayer1:  return NpadIdType.Player1;
+                case HidControllerId.ControllerPlayer2:  return NpadIdType.Player2;
+                case HidControllerId.ControllerPlayer3:  return NpadIdType.Player3;
+                case HidControllerId.ControllerPlayer4:  return NpadIdType.Player4;
+                case HidControllerId.ControllerPlayer5:  return NpadIdType.Player5;
+                case HidControllerId.ControllerPlayer6:  return NpadIdType.Player6;
+                case HidControllerId.ControllerPlayer7:  return NpadIdType.Player7;
+                case HidControllerId.ControllerPlayer8:  return NpadIdType.Player8;
+                case HidControllerId.ControllerHandheld: return NpadIdType.Handheld;
+                case HidControllerId.ControllerUnknown:  return NpadIdType.Unknown;
+
+                default: throw new ArgumentOutOfRangeException(nameof(index));
+            }
+        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/Device.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/Device.cs
new file mode 100644
index 0000000000..a1f58dae1c
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/Device.cs
@@ -0,0 +1,20 @@
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.HLE.HOS.Services.Hid;
+using Ryujinx.HLE.Input;
+
+namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
+{
+    class Device
+    {
+        public KEvent ActivateEvent;
+        public int    ActivateEventHandle;
+
+        public KEvent DeactivateEvent;
+        public int    DeactivateEventHandle;
+
+        public DeviceState State = DeviceState.Unavailable;
+
+        public HidControllerId Handle;
+        public NpadIdType      NpadIdType;
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/DeviceState.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/DeviceState.cs
new file mode 100644
index 0000000000..09cff5f8d2
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/DeviceState.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
+{
+    enum DeviceState
+    {
+        Initialized     = 0,
+        SearchingForTag = 1,
+        TagFound        = 2,
+        TagRemoved      = 3,
+        TagMounted      = 4,
+        Unavailable     = 5,
+        Finalized       = 6
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IUser.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IUser.cs
new file mode 100644
index 0000000000..d4c977793b
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IUser.cs
@@ -0,0 +1,346 @@
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.Exceptions;
+using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.HLE.HOS.Kernel.Common;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.HLE.HOS.Services.Hid;
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
+{
+    class IUser : IpcService
+    {
+        private State _state = State.NonInitialized;
+
+        private KEvent _availabilityChangeEvent;
+        private int    _availabilityChangeEventHandle = 0;
+
+        private List<Device> _devices = new List<Device>();
+
+        private Dictionary<int, ServiceProcessRequest> _commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
+
+        public IUser()
+        {
+            _commands = new Dictionary<int, ServiceProcessRequest>
+            {
+                { 0,  Initialize                    },
+                { 1,  Finalize                      },
+                { 2,  ListDevices                   },
+                { 3,  StartDetection                },
+                { 4,  StopDetection                 },
+                { 5,  Mount                         },
+                { 6,  Unmount                       },
+                { 7,  OpenApplicationArea           },
+                { 8,  GetApplicationArea            },
+                { 9,  SetApplicationArea            },
+                { 10, Flush                         },
+                { 11, Restore                       },
+                { 12, CreateApplicationArea         },
+                { 13, GetTagInfo                    },
+                { 14, GetRegisterInfo               },
+                { 15, GetCommonInfo                 },
+                { 16, GetModelInfo                  },
+                { 17, AttachActivateEvent           },
+                { 18, AttachDeactivateEvent         },
+                { 19, GetState                      },
+                { 20, GetDeviceState                },
+                { 21, GetNpadId                     },
+                { 22, GetApplicationAreaSize        },
+                { 23, AttachAvailabilityChangeEvent }, // 3.0.0+
+                { 24, RecreateApplicationArea       }, // 3.0.0+
+            };
+        }
+
+        // Initialize(u64, u64, pid, buffer<unknown, 5>)
+        public long Initialize(ServiceCtx context)
+        {
+            long appletResourceUserId = context.RequestData.ReadInt64();
+            long mcuVersionData       = context.RequestData.ReadInt64();
+
+            long inputPosition = context.Request.SendBuff[0].Position;
+            long inputSize     = context.Request.SendBuff[0].Size;
+
+            byte[] unknownBuffer = context.Memory.ReadBytes(inputPosition, inputSize);
+
+            // NOTE: appletResourceUserId, mcuVersionData and the buffer are stored inside an internal struct.
+            //       The buffer seems to contains entries with a size of 0x40 bytes each.
+            //       Sadly, this internal struct doesn't seems to be used in retail.
+
+            // TODO: Add an instance of nn::nfc::server::Manager when it will be implemented.
+            //       Add an instance of nn::nfc::server::SaveData when it will be implemented.
+
+            // TODO: When we will be able to add multiple controllers add one entry by controller here.
+            Device device1 = new Device
+            {
+                NpadIdType = NpadIdType.Player1,
+                Handle     = HidUtils.GetIndexFromNpadIdType(NpadIdType.Player1),
+                State      = DeviceState.Initialized
+            };
+
+            _devices.Add(device1);
+
+            _state = State.Initialized;
+
+            return 0;
+        }
+
+        // Finalize()
+        public long Finalize(ServiceCtx context)
+        {
+            // TODO: Call StopDetection() and Unmount() when they will be implemented.
+            //       Remove the instance of nn::nfc::server::Manager when it will be implemented.
+            //       Remove the instance of nn::nfc::server::SaveData when it will be implemented.
+
+            _devices.Clear();
+
+            _state = State.NonInitialized;
+
+            return 0;
+        }
+
+        // ListDevices() -> (u32, buffer<unknown, 0xa>)
+        public long ListDevices(ServiceCtx context)
+        {
+            if (context.Request.RecvListBuff.Count == 0)
+            {
+                return ErrorCode.MakeError(ErrorModule.Nfp, NfpError.DevicesBufferIsNull);
+            }
+
+            long outputPosition = context.Request.RecvListBuff[0].Position;
+            long outputSize     = context.Request.RecvListBuff[0].Size;
+
+            if (_devices.Count == 0)
+            {
+                return ErrorCode.MakeError(ErrorModule.Nfp, NfpError.DeviceNotFound);
+            }
+
+            for (int i = 0; i < _devices.Count; i++)
+            {
+                context.Memory.WriteUInt32(outputPosition + (i * sizeof(long)), (uint)_devices[i].Handle);
+            }
+
+            context.ResponseData.Write(_devices.Count);
+
+            return 0;
+        }
+
+        // StartDetection(bytes<8, 4>)
+        public long StartDetection(ServiceCtx context)
+        {
+            throw new ServiceNotImplementedException(context);
+        }
+
+        // StopDetection(bytes<8, 4>)
+        public long StopDetection(ServiceCtx context)
+        {
+            throw new ServiceNotImplementedException(context);
+        }
+
+        // Mount(bytes<8, 4>, u32, u32)
+        public long Mount(ServiceCtx context)
+        {
+            throw new ServiceNotImplementedException(context);
+        }
+
+        // Unmount(bytes<8, 4>)
+        public long Unmount(ServiceCtx context)
+        {
+            throw new ServiceNotImplementedException(context);
+        }
+
+        // OpenApplicationArea(bytes<8, 4>, u32)
+        public long OpenApplicationArea(ServiceCtx context)
+        {
+            throw new ServiceNotImplementedException(context);
+        }
+
+        // GetApplicationArea(bytes<8, 4>) -> (u32, buffer<unknown, 6>)
+        public long GetApplicationArea(ServiceCtx context)
+        {
+            throw new ServiceNotImplementedException(context);
+        }
+
+        // SetApplicationArea(bytes<8, 4>, buffer<unknown, 5>)
+        public long SetApplicationArea(ServiceCtx context)
+        {
+            throw new ServiceNotImplementedException(context);
+        }
+
+        // Flush(bytes<8, 4>)
+        public long Flush(ServiceCtx context)
+        {
+            throw new ServiceNotImplementedException(context);
+        }
+
+        // Restore(bytes<8, 4>)
+        public long Restore(ServiceCtx context)
+        {
+            throw new ServiceNotImplementedException(context);
+        }
+
+        // CreateApplicationArea(bytes<8, 4>, u32, buffer<unknown, 5>)
+        public long CreateApplicationArea(ServiceCtx context)
+        {
+            throw new ServiceNotImplementedException(context);
+        }
+
+        // GetTagInfo(bytes<8, 4>) -> buffer<unknown<0x58>, 0x1a>
+        public long GetTagInfo(ServiceCtx context)
+        {
+            throw new ServiceNotImplementedException(context);
+        }
+
+        // GetRegisterInfo(bytes<8, 4>) -> buffer<unknown<0x100>, 0x1a>
+        public long GetRegisterInfo(ServiceCtx context)
+        {
+            throw new ServiceNotImplementedException(context);
+        }
+
+        // GetCommonInfo(bytes<8, 4>) -> buffer<unknown<0x40>, 0x1a>
+        public long GetCommonInfo(ServiceCtx context)
+        {
+            throw new ServiceNotImplementedException(context);
+        }
+
+        // GetModelInfo(bytes<8, 4>) -> buffer<unknown<0x40>, 0x1a>
+        public long GetModelInfo(ServiceCtx context)
+        {
+            throw new ServiceNotImplementedException(context);
+        }
+
+        // AttachActivateEvent(bytes<8, 4>) -> handle<copy>
+        public long AttachActivateEvent(ServiceCtx context)
+        {
+            uint deviceHandle = context.RequestData.ReadUInt32();
+
+            for (int i = 0; i < _devices.Count; i++)
+            {
+                if ((uint)_devices[i].Handle == deviceHandle)
+                {
+                    if (_devices[i].ActivateEventHandle == 0)
+                    {
+                        _devices[i].ActivateEvent = new KEvent(context.Device.System);
+
+                        if (context.Process.HandleTable.GenerateHandle(_devices[i].ActivateEvent.ReadableEvent, out _devices[i].ActivateEventHandle) != KernelResult.Success)
+                        {
+                            throw new InvalidOperationException("Out of handles!");
+                        }
+                    }
+
+                    context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_devices[i].ActivateEventHandle);
+
+                    return 0;
+                }
+            }
+
+            return ErrorCode.MakeError(ErrorModule.Nfp, NfpError.DeviceNotFound);
+        }
+
+        // AttachDeactivateEvent(bytes<8, 4>) -> handle<copy>
+        public long AttachDeactivateEvent(ServiceCtx context)
+        {
+            uint deviceHandle = context.RequestData.ReadUInt32();
+
+            for (int i = 0; i < _devices.Count; i++)
+            {
+                if ((uint)_devices[i].Handle == deviceHandle)
+                {
+                    if (_devices[i].DeactivateEventHandle == 0)
+                    {
+                        _devices[i].DeactivateEvent = new KEvent(context.Device.System);
+
+                        if (context.Process.HandleTable.GenerateHandle(_devices[i].DeactivateEvent.ReadableEvent, out _devices[i].DeactivateEventHandle) != KernelResult.Success)
+                        {
+                            throw new InvalidOperationException("Out of handles!");
+                        }
+                    }
+
+                    context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_devices[i].DeactivateEventHandle);
+
+                    return 0;
+                }
+            }
+
+            return ErrorCode.MakeError(ErrorModule.Nfp, NfpError.DeviceNotFound);
+        }
+
+        // GetState() -> u32
+        public long GetState(ServiceCtx context)
+        {
+            context.ResponseData.Write((int)_state);
+
+            return 0;
+        }
+
+        // GetDeviceState(bytes<8, 4>) -> u32
+        public long GetDeviceState(ServiceCtx context)
+        {
+            uint deviceHandle = context.RequestData.ReadUInt32();
+
+            for (int i = 0; i < _devices.Count; i++)
+            {
+                if ((uint)_devices[i].Handle == deviceHandle)
+                {
+                    context.ResponseData.Write((uint)_devices[i].State);
+
+                    return 0;
+                }
+            }
+
+            context.ResponseData.Write((uint)DeviceState.Unavailable);
+
+            return ErrorCode.MakeError(ErrorModule.Nfp, NfpError.DeviceNotFound);
+        }
+
+        // GetNpadId(bytes<8, 4>) -> u32
+        public long GetNpadId(ServiceCtx context)
+        {
+            uint deviceHandle = context.RequestData.ReadUInt32();
+
+            for (int i = 0; i < _devices.Count; i++)
+            {
+                if ((uint)_devices[i].Handle == deviceHandle)
+                {
+                    context.ResponseData.Write((uint)HidUtils.GetNpadIdTypeFromIndex(_devices[i].Handle));
+
+                    return 0;
+                }
+            }
+
+            return ErrorCode.MakeError(ErrorModule.Nfp, NfpError.DeviceNotFound);
+        }
+
+        // GetApplicationAreaSize(bytes<8, 4>) -> u32
+        public long GetApplicationAreaSize(ServiceCtx context)
+        {
+            throw new ServiceNotImplementedException(context);
+        }
+
+        // AttachAvailabilityChangeEvent() -> handle<copy>
+        public long AttachAvailabilityChangeEvent(ServiceCtx context)
+        {
+            if (_availabilityChangeEventHandle == 0)
+            {
+                _availabilityChangeEvent = new KEvent(context.Device.System);
+
+                if (context.Process.HandleTable.GenerateHandle(_availabilityChangeEvent.ReadableEvent, out _availabilityChangeEventHandle) != KernelResult.Success)
+                {
+                    throw new InvalidOperationException("Out of handles!");
+                }
+            }
+
+            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_availabilityChangeEventHandle);
+
+            return 0;
+        }
+
+        // RecreateApplicationArea(bytes<8, 4>, u32, buffer<unknown, 5>)
+        public long RecreateApplicationArea(ServiceCtx context)
+        {
+            throw new ServiceNotImplementedException(context);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Nfp/IUserManager.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IUserManager.cs
similarity index 84%
rename from Ryujinx.HLE/HOS/Services/Nfp/IUserManager.cs
rename to Ryujinx.HLE/HOS/Services/Nfc/Nfp/IUserManager.cs
index 1bf937466f..dad1026ecf 100644
--- a/Ryujinx.HLE/HOS/Services/Nfp/IUserManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IUserManager.cs
@@ -1,7 +1,7 @@
 using Ryujinx.HLE.HOS.Ipc;
 using System.Collections.Generic;
 
-namespace Ryujinx.HLE.HOS.Services.Nfp
+namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
 {
     class IUserManager : IpcService
     {
@@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfp
 
         public long GetUserInterface(ServiceCtx context)
         {
-            MakeObject(context, new IUser(context.Device.System));
+            MakeObject(context, new IUser());
 
             return 0;
         }
diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpError.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpError.cs
new file mode 100644
index 0000000000..f9bfecc09b
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpError.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
+{
+    static class NfpError
+    {
+        public const int DeviceNotFound      = 64;
+        public const int DevicesBufferIsNull = 65;
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Nfp/State.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/State.cs
similarity index 64%
rename from Ryujinx.HLE/HOS/Services/Nfp/State.cs
rename to Ryujinx.HLE/HOS/Services/Nfc/Nfp/State.cs
index 0b4b3c1b05..166e5d7e73 100644
--- a/Ryujinx.HLE/HOS/Services/Nfp/State.cs
+++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/State.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.HLE.HOS.Services.Nfp
+namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
 {
     enum State
     {
diff --git a/Ryujinx.HLE/HOS/Services/Nfp/DeviceState.cs b/Ryujinx.HLE/HOS/Services/Nfp/DeviceState.cs
deleted file mode 100644
index 0452789362..0000000000
--- a/Ryujinx.HLE/HOS/Services/Nfp/DeviceState.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Nfp
-{
-    enum DeviceState
-    {
-        Initialized = 0
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Nfp/IUser.cs b/Ryujinx.HLE/HOS/Services/Nfp/IUser.cs
deleted file mode 100644
index 66bff1a7e1..0000000000
--- a/Ryujinx.HLE/HOS/Services/Nfp/IUser.cs
+++ /dev/null
@@ -1,125 +0,0 @@
-using Ryujinx.Common.Logging;
-using Ryujinx.HLE.HOS.Ipc;
-using Ryujinx.HLE.HOS.Kernel.Common;
-using Ryujinx.HLE.HOS.Kernel.Threading;
-using Ryujinx.HLE.Input;
-using System;
-using System.Collections.Generic;
-
-namespace Ryujinx.HLE.HOS.Services.Nfp
-{
-    class IUser : IpcService
-    {
-        private Dictionary<int, ServiceProcessRequest> _commands;
-
-        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
-
-        private const HidControllerId NpadId = HidControllerId.ControllerPlayer1;
-
-        private State _state = State.NonInitialized;
-
-        private DeviceState _deviceState = DeviceState.Initialized;
-
-        private KEvent _activateEvent;
-
-        private KEvent _deactivateEvent;
-
-        private KEvent _availabilityChangeEvent;
-
-        public IUser(Horizon system)
-        {
-            _commands = new Dictionary<int, ServiceProcessRequest>
-            {
-                { 0,  Initialize                    },
-                { 17, AttachActivateEvent           },
-                { 18, AttachDeactivateEvent         },
-                { 19, GetState                      },
-                { 20, GetDeviceState                },
-                { 21, GetNpadId                     },
-                { 23, AttachAvailabilityChangeEvent }
-            };
-
-            _activateEvent           = new KEvent(system);
-            _deactivateEvent         = new KEvent(system);
-            _availabilityChangeEvent = new KEvent(system);
-        }
-
-        public long Initialize(ServiceCtx context)
-        {
-            Logger.PrintStub(LogClass.ServiceNfp);
-
-            _state = State.Initialized;
-
-            return 0;
-        }
-
-        public long AttachActivateEvent(ServiceCtx context)
-        {
-            Logger.PrintStub(LogClass.ServiceNfp);
-
-            if (context.Process.HandleTable.GenerateHandle(_activateEvent.ReadableEvent, out int handle) != KernelResult.Success)
-            {
-                throw new InvalidOperationException("Out of handles!");
-            }
-
-            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
-
-            return 0;
-        }
-
-        public long AttachDeactivateEvent(ServiceCtx context)
-        {
-            Logger.PrintStub(LogClass.ServiceNfp);
-
-            if (context.Process.HandleTable.GenerateHandle(_deactivateEvent.ReadableEvent, out int handle) != KernelResult.Success)
-            {
-                throw new InvalidOperationException("Out of handles!");
-            }
-
-            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
-
-            return 0;
-        }
-
-        public long GetState(ServiceCtx context)
-        {
-            context.ResponseData.Write((int)_state);
-
-            Logger.PrintStub(LogClass.ServiceNfp);
-
-            return 0;
-        }
-
-        public long GetDeviceState(ServiceCtx context)
-        {
-            context.ResponseData.Write((int)_deviceState);
-
-            Logger.PrintStub(LogClass.ServiceNfp);
-
-            return 0;
-        }
-
-        public long GetNpadId(ServiceCtx context)
-        {
-            context.ResponseData.Write((int)NpadId);
-
-            Logger.PrintStub(LogClass.ServiceNfp);
-
-            return 0;
-        }
-
-        public long AttachAvailabilityChangeEvent(ServiceCtx context)
-        {
-            Logger.PrintStub(LogClass.ServiceNfp);
-
-            if (context.Process.HandleTable.GenerateHandle(_availabilityChangeEvent.ReadableEvent, out int handle) != KernelResult.Success)
-            {
-                throw new InvalidOperationException("Out of handles!");
-            }
-
-            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
-
-            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 63488d2178..7cd943e026 100644
--- a/Ryujinx.HLE/HOS/Services/ServiceFactory.cs
+++ b/Ryujinx.HLE/HOS/Services/ServiceFactory.cs
@@ -13,7 +13,7 @@ using Ryujinx.HLE.HOS.Services.Ldr;
 using Ryujinx.HLE.HOS.Services.Lm;
 using Ryujinx.HLE.HOS.Services.Mm;
 using Ryujinx.HLE.HOS.Services.Ncm;
-using Ryujinx.HLE.HOS.Services.Nfp;
+using Ryujinx.HLE.HOS.Services.Nfc.Nfp;
 using Ryujinx.HLE.HOS.Services.Ns;
 using Ryujinx.HLE.HOS.Services.Nv;
 using Ryujinx.HLE.HOS.Services.Pctl;