nifm.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "core/core.h"
  4. #include "core/hle/ipc_helpers.h"
  5. #include "core/hle/kernel/k_event.h"
  6. #include "core/hle/service/kernel_helpers.h"
  7. #include "core/hle/service/nifm/nifm.h"
  8. namespace {
  9. // Avoids name conflict with Windows' CreateEvent macro.
  10. [[nodiscard]] Kernel::KEvent* CreateKEvent(Service::KernelHelpers::ServiceContext& service_context,
  11. std::string&& name) {
  12. return service_context.CreateEvent(std::move(name));
  13. }
  14. } // Anonymous namespace
  15. #include "core/internal_network/network.h"
  16. #include "core/internal_network/network_interface.h"
  17. namespace Service::NIFM {
  18. enum class RequestState : u32 {
  19. NotSubmitted = 1,
  20. Error = 1, ///< The duplicate 1 is intentional; it means both not submitted and error on HW.
  21. Pending = 2,
  22. Connected = 3,
  23. };
  24. enum class InternetConnectionType : u8 {
  25. WiFi = 1,
  26. Ethernet = 2,
  27. };
  28. enum class InternetConnectionStatus : u8 {
  29. ConnectingUnknown1,
  30. ConnectingUnknown2,
  31. ConnectingUnknown3,
  32. ConnectingUnknown4,
  33. Connected,
  34. };
  35. struct IpAddressSetting {
  36. bool is_automatic{};
  37. Network::IPv4Address current_address{};
  38. Network::IPv4Address subnet_mask{};
  39. Network::IPv4Address gateway{};
  40. };
  41. static_assert(sizeof(IpAddressSetting) == 0xD, "IpAddressSetting has incorrect size.");
  42. struct DnsSetting {
  43. bool is_automatic{};
  44. Network::IPv4Address primary_dns{};
  45. Network::IPv4Address secondary_dns{};
  46. };
  47. static_assert(sizeof(DnsSetting) == 0x9, "DnsSetting has incorrect size.");
  48. struct ProxySetting {
  49. bool enabled{};
  50. INSERT_PADDING_BYTES(1);
  51. u16 port{};
  52. std::array<char, 0x64> proxy_server{};
  53. bool automatic_auth_enabled{};
  54. std::array<char, 0x20> user{};
  55. std::array<char, 0x20> password{};
  56. INSERT_PADDING_BYTES(1);
  57. };
  58. static_assert(sizeof(ProxySetting) == 0xAA, "ProxySetting has incorrect size.");
  59. struct IpSettingData {
  60. IpAddressSetting ip_address_setting{};
  61. DnsSetting dns_setting{};
  62. ProxySetting proxy_setting{};
  63. u16 mtu{};
  64. };
  65. static_assert(sizeof(IpSettingData) == 0xC2, "IpSettingData has incorrect size.");
  66. struct SfWirelessSettingData {
  67. u8 ssid_length{};
  68. std::array<char, 0x20> ssid{};
  69. u8 unknown_1{};
  70. u8 unknown_2{};
  71. u8 unknown_3{};
  72. std::array<char, 0x41> passphrase{};
  73. };
  74. static_assert(sizeof(SfWirelessSettingData) == 0x65, "SfWirelessSettingData has incorrect size.");
  75. struct NifmWirelessSettingData {
  76. u8 ssid_length{};
  77. std::array<char, 0x21> ssid{};
  78. u8 unknown_1{};
  79. INSERT_PADDING_BYTES(1);
  80. u32 unknown_2{};
  81. u32 unknown_3{};
  82. std::array<char, 0x41> passphrase{};
  83. INSERT_PADDING_BYTES(3);
  84. };
  85. static_assert(sizeof(NifmWirelessSettingData) == 0x70,
  86. "NifmWirelessSettingData has incorrect size.");
  87. #pragma pack(push, 1)
  88. struct SfNetworkProfileData {
  89. IpSettingData ip_setting_data{};
  90. u128 uuid{};
  91. std::array<char, 0x40> network_name{};
  92. u8 unknown_1{};
  93. u8 unknown_2{};
  94. u8 unknown_3{};
  95. u8 unknown_4{};
  96. SfWirelessSettingData wireless_setting_data{};
  97. INSERT_PADDING_BYTES(1);
  98. };
  99. static_assert(sizeof(SfNetworkProfileData) == 0x17C, "SfNetworkProfileData has incorrect size.");
  100. struct NifmNetworkProfileData {
  101. u128 uuid{};
  102. std::array<char, 0x40> network_name{};
  103. u32 unknown_1{};
  104. u32 unknown_2{};
  105. u8 unknown_3{};
  106. u8 unknown_4{};
  107. INSERT_PADDING_BYTES(2);
  108. NifmWirelessSettingData wireless_setting_data{};
  109. IpSettingData ip_setting_data{};
  110. };
  111. static_assert(sizeof(NifmNetworkProfileData) == 0x18E,
  112. "NifmNetworkProfileData has incorrect size.");
  113. #pragma pack(pop)
  114. class IScanRequest final : public ServiceFramework<IScanRequest> {
  115. public:
  116. explicit IScanRequest(Core::System& system_) : ServiceFramework{system_, "IScanRequest"} {
  117. // clang-format off
  118. static const FunctionInfo functions[] = {
  119. {0, nullptr, "Submit"},
  120. {1, nullptr, "IsProcessing"},
  121. {2, nullptr, "GetResult"},
  122. {3, nullptr, "GetSystemEventReadableHandle"},
  123. {4, nullptr, "SetChannels"},
  124. };
  125. // clang-format on
  126. RegisterHandlers(functions);
  127. }
  128. };
  129. class IRequest final : public ServiceFramework<IRequest> {
  130. public:
  131. explicit IRequest(Core::System& system_)
  132. : ServiceFramework{system_, "IRequest"}, service_context{system_, "IRequest"} {
  133. static const FunctionInfo functions[] = {
  134. {0, &IRequest::GetRequestState, "GetRequestState"},
  135. {1, &IRequest::GetResult, "GetResult"},
  136. {2, &IRequest::GetSystemEventReadableHandles, "GetSystemEventReadableHandles"},
  137. {3, &IRequest::Cancel, "Cancel"},
  138. {4, &IRequest::Submit, "Submit"},
  139. {5, nullptr, "SetRequirement"},
  140. {6, nullptr, "SetRequirementPreset"},
  141. {8, nullptr, "SetPriority"},
  142. {9, nullptr, "SetNetworkProfileId"},
  143. {10, nullptr, "SetRejectable"},
  144. {11, &IRequest::SetConnectionConfirmationOption, "SetConnectionConfirmationOption"},
  145. {12, nullptr, "SetPersistent"},
  146. {13, nullptr, "SetInstant"},
  147. {14, nullptr, "SetSustainable"},
  148. {15, nullptr, "SetRawPriority"},
  149. {16, nullptr, "SetGreedy"},
  150. {17, nullptr, "SetSharable"},
  151. {18, nullptr, "SetRequirementByRevision"},
  152. {19, nullptr, "GetRequirement"},
  153. {20, nullptr, "GetRevision"},
  154. {21, &IRequest::GetAppletInfo, "GetAppletInfo"},
  155. {22, nullptr, "GetAdditionalInfo"},
  156. {23, nullptr, "SetKeptInSleep"},
  157. {24, nullptr, "RegisterSocketDescriptor"},
  158. {25, nullptr, "UnregisterSocketDescriptor"},
  159. };
  160. RegisterHandlers(functions);
  161. event1 = CreateKEvent(service_context, "IRequest:Event1");
  162. event2 = CreateKEvent(service_context, "IRequest:Event2");
  163. }
  164. ~IRequest() override {
  165. service_context.CloseEvent(event1);
  166. service_context.CloseEvent(event2);
  167. }
  168. private:
  169. void Submit(Kernel::HLERequestContext& ctx) {
  170. LOG_WARNING(Service_NIFM, "(STUBBED) called");
  171. IPC::ResponseBuilder rb{ctx, 2};
  172. rb.Push(ResultSuccess);
  173. }
  174. void GetRequestState(Kernel::HLERequestContext& ctx) {
  175. LOG_WARNING(Service_NIFM, "(STUBBED) called");
  176. IPC::ResponseBuilder rb{ctx, 3};
  177. rb.Push(ResultSuccess);
  178. if (Network::GetHostIPv4Address().has_value()) {
  179. rb.PushEnum(RequestState::Connected);
  180. } else {
  181. rb.PushEnum(RequestState::NotSubmitted);
  182. }
  183. }
  184. void GetResult(Kernel::HLERequestContext& ctx) {
  185. LOG_WARNING(Service_NIFM, "(STUBBED) called");
  186. IPC::ResponseBuilder rb{ctx, 2};
  187. rb.Push(ResultSuccess);
  188. }
  189. void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) {
  190. LOG_WARNING(Service_NIFM, "(STUBBED) called");
  191. IPC::ResponseBuilder rb{ctx, 2, 2};
  192. rb.Push(ResultSuccess);
  193. rb.PushCopyObjects(event1->GetReadableEvent(), event2->GetReadableEvent());
  194. }
  195. void Cancel(Kernel::HLERequestContext& ctx) {
  196. LOG_WARNING(Service_NIFM, "(STUBBED) called");
  197. IPC::ResponseBuilder rb{ctx, 2};
  198. rb.Push(ResultSuccess);
  199. }
  200. void SetConnectionConfirmationOption(Kernel::HLERequestContext& ctx) {
  201. LOG_WARNING(Service_NIFM, "(STUBBED) called");
  202. IPC::ResponseBuilder rb{ctx, 2};
  203. rb.Push(ResultSuccess);
  204. }
  205. void GetAppletInfo(Kernel::HLERequestContext& ctx) {
  206. LOG_WARNING(Service_NIFM, "(STUBBED) called");
  207. std::vector<u8> out_buffer(ctx.GetWriteBufferSize());
  208. ctx.WriteBuffer(out_buffer);
  209. IPC::ResponseBuilder rb{ctx, 5};
  210. rb.Push(ResultSuccess);
  211. rb.Push<u32>(0);
  212. rb.Push<u32>(0);
  213. rb.Push<u32>(0);
  214. }
  215. KernelHelpers::ServiceContext service_context;
  216. Kernel::KEvent* event1;
  217. Kernel::KEvent* event2;
  218. };
  219. class INetworkProfile final : public ServiceFramework<INetworkProfile> {
  220. public:
  221. explicit INetworkProfile(Core::System& system_) : ServiceFramework{system_, "INetworkProfile"} {
  222. static const FunctionInfo functions[] = {
  223. {0, nullptr, "Update"},
  224. {1, nullptr, "PersistOld"},
  225. {2, nullptr, "Persist"},
  226. };
  227. RegisterHandlers(functions);
  228. }
  229. };
  230. void IGeneralService::GetClientId(Kernel::HLERequestContext& ctx) {
  231. static constexpr u32 client_id = 1;
  232. LOG_WARNING(Service_NIFM, "(STUBBED) called");
  233. IPC::ResponseBuilder rb{ctx, 4};
  234. rb.Push(ResultSuccess);
  235. rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid
  236. }
  237. void IGeneralService::CreateScanRequest(Kernel::HLERequestContext& ctx) {
  238. LOG_DEBUG(Service_NIFM, "called");
  239. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  240. rb.Push(ResultSuccess);
  241. rb.PushIpcInterface<IScanRequest>(system);
  242. }
  243. void IGeneralService::CreateRequest(Kernel::HLERequestContext& ctx) {
  244. LOG_DEBUG(Service_NIFM, "called");
  245. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  246. rb.Push(ResultSuccess);
  247. rb.PushIpcInterface<IRequest>(system);
  248. }
  249. void IGeneralService::GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
  250. LOG_WARNING(Service_NIFM, "(STUBBED) called");
  251. const auto net_iface = Network::GetSelectedNetworkInterface();
  252. SfNetworkProfileData network_profile_data = [&net_iface] {
  253. if (!net_iface) {
  254. return SfNetworkProfileData{};
  255. }
  256. return SfNetworkProfileData{
  257. .ip_setting_data{
  258. .ip_address_setting{
  259. .is_automatic{true},
  260. .current_address{Network::TranslateIPv4(net_iface->ip_address)},
  261. .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
  262. .gateway{Network::TranslateIPv4(net_iface->gateway)},
  263. },
  264. .dns_setting{
  265. .is_automatic{true},
  266. .primary_dns{1, 1, 1, 1},
  267. .secondary_dns{1, 0, 0, 1},
  268. },
  269. .proxy_setting{
  270. .enabled{false},
  271. .port{},
  272. .proxy_server{},
  273. .automatic_auth_enabled{},
  274. .user{},
  275. .password{},
  276. },
  277. .mtu{1500},
  278. },
  279. .uuid{0xdeadbeef, 0xdeadbeef},
  280. .network_name{"yuzu Network"},
  281. .wireless_setting_data{
  282. .ssid_length{12},
  283. .ssid{"yuzu Network"},
  284. .passphrase{"yuzupassword"},
  285. },
  286. };
  287. }();
  288. // When we're connected to a room, spoof the hosts IP address
  289. if (auto room_member = network.GetRoomMember().lock()) {
  290. if (room_member->IsConnected()) {
  291. network_profile_data.ip_setting_data.ip_address_setting.current_address =
  292. room_member->GetFakeIpAddress();
  293. }
  294. }
  295. ctx.WriteBuffer(network_profile_data);
  296. IPC::ResponseBuilder rb{ctx, 2};
  297. rb.Push(ResultSuccess);
  298. }
  299. void IGeneralService::RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
  300. LOG_WARNING(Service_NIFM, "(STUBBED) called");
  301. IPC::ResponseBuilder rb{ctx, 2};
  302. rb.Push(ResultSuccess);
  303. }
  304. void IGeneralService::GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
  305. LOG_WARNING(Service_NIFM, "(STUBBED) called");
  306. auto ipv4 = Network::GetHostIPv4Address();
  307. if (!ipv4) {
  308. LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0");
  309. ipv4.emplace(Network::IPv4Address{0, 0, 0, 0});
  310. }
  311. // When we're connected to a room, spoof the hosts IP address
  312. if (auto room_member = network.GetRoomMember().lock()) {
  313. if (room_member->IsConnected()) {
  314. ipv4 = room_member->GetFakeIpAddress();
  315. }
  316. }
  317. IPC::ResponseBuilder rb{ctx, 3};
  318. rb.Push(ResultSuccess);
  319. rb.PushRaw(*ipv4);
  320. }
  321. void IGeneralService::CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
  322. LOG_DEBUG(Service_NIFM, "called");
  323. ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, "SfNetworkProfileData is not the correct size");
  324. u128 uuid{};
  325. auto buffer = ctx.ReadBuffer();
  326. std::memcpy(&uuid, buffer.data() + 8, sizeof(u128));
  327. IPC::ResponseBuilder rb{ctx, 6, 0, 1};
  328. rb.Push(ResultSuccess);
  329. rb.PushIpcInterface<INetworkProfile>(system);
  330. rb.PushRaw<u128>(uuid);
  331. }
  332. void IGeneralService::GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) {
  333. LOG_WARNING(Service_NIFM, "(STUBBED) called");
  334. struct IpConfigInfo {
  335. IpAddressSetting ip_address_setting{};
  336. DnsSetting dns_setting{};
  337. };
  338. static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
  339. "IpConfigInfo has incorrect size.");
  340. const auto net_iface = Network::GetSelectedNetworkInterface();
  341. IpConfigInfo ip_config_info = [&net_iface] {
  342. if (!net_iface) {
  343. return IpConfigInfo{};
  344. }
  345. return IpConfigInfo{
  346. .ip_address_setting{
  347. .is_automatic{true},
  348. .current_address{Network::TranslateIPv4(net_iface->ip_address)},
  349. .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
  350. .gateway{Network::TranslateIPv4(net_iface->gateway)},
  351. },
  352. .dns_setting{
  353. .is_automatic{true},
  354. .primary_dns{1, 1, 1, 1},
  355. .secondary_dns{1, 0, 0, 1},
  356. },
  357. };
  358. }();
  359. // When we're connected to a room, spoof the hosts IP address
  360. if (auto room_member = network.GetRoomMember().lock()) {
  361. if (room_member->IsConnected()) {
  362. ip_config_info.ip_address_setting.current_address = room_member->GetFakeIpAddress();
  363. }
  364. }
  365. IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)};
  366. rb.Push(ResultSuccess);
  367. rb.PushRaw<IpConfigInfo>(ip_config_info);
  368. }
  369. void IGeneralService::IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) {
  370. LOG_WARNING(Service_NIFM, "(STUBBED) called");
  371. IPC::ResponseBuilder rb{ctx, 3};
  372. rb.Push(ResultSuccess);
  373. rb.Push<u8>(1);
  374. }
  375. void IGeneralService::GetInternetConnectionStatus(Kernel::HLERequestContext& ctx) {
  376. LOG_WARNING(Service_NIFM, "(STUBBED) called");
  377. struct Output {
  378. InternetConnectionType type{InternetConnectionType::WiFi};
  379. u8 wifi_strength{3};
  380. InternetConnectionStatus state{InternetConnectionStatus::Connected};
  381. };
  382. static_assert(sizeof(Output) == 0x3, "Output has incorrect size.");
  383. constexpr Output out{};
  384. IPC::ResponseBuilder rb{ctx, 3};
  385. rb.Push(ResultSuccess);
  386. rb.PushRaw(out);
  387. }
  388. void IGeneralService::IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) {
  389. LOG_WARNING(Service_NIFM, "(STUBBED) called");
  390. IPC::ResponseBuilder rb{ctx, 3};
  391. rb.Push(ResultSuccess);
  392. if (Network::GetHostIPv4Address().has_value()) {
  393. rb.Push<u8>(1);
  394. } else {
  395. rb.Push<u8>(0);
  396. }
  397. }
  398. void IGeneralService::IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
  399. LOG_ERROR(Service_NIFM, "(STUBBED) called");
  400. IPC::ResponseBuilder rb{ctx, 3};
  401. rb.Push(ResultSuccess);
  402. if (Network::GetHostIPv4Address().has_value()) {
  403. rb.Push<u8>(1);
  404. } else {
  405. rb.Push<u8>(0);
  406. }
  407. }
  408. IGeneralService::IGeneralService(Core::System& system_)
  409. : ServiceFramework{system_, "IGeneralService"}, network{system_.GetRoomNetwork()} {
  410. // clang-format off
  411. static const FunctionInfo functions[] = {
  412. {1, &IGeneralService::GetClientId, "GetClientId"},
  413. {2, &IGeneralService::CreateScanRequest, "CreateScanRequest"},
  414. {4, &IGeneralService::CreateRequest, "CreateRequest"},
  415. {5, &IGeneralService::GetCurrentNetworkProfile, "GetCurrentNetworkProfile"},
  416. {6, nullptr, "EnumerateNetworkInterfaces"},
  417. {7, nullptr, "EnumerateNetworkProfiles"},
  418. {8, nullptr, "GetNetworkProfile"},
  419. {9, nullptr, "SetNetworkProfile"},
  420. {10, &IGeneralService::RemoveNetworkProfile, "RemoveNetworkProfile"},
  421. {11, nullptr, "GetScanDataOld"},
  422. {12, &IGeneralService::GetCurrentIpAddress, "GetCurrentIpAddress"},
  423. {13, nullptr, "GetCurrentAccessPointOld"},
  424. {14, &IGeneralService::CreateTemporaryNetworkProfile, "CreateTemporaryNetworkProfile"},
  425. {15, &IGeneralService::GetCurrentIpConfigInfo, "GetCurrentIpConfigInfo"},
  426. {16, nullptr, "SetWirelessCommunicationEnabled"},
  427. {17, &IGeneralService::IsWirelessCommunicationEnabled, "IsWirelessCommunicationEnabled"},
  428. {18, &IGeneralService::GetInternetConnectionStatus, "GetInternetConnectionStatus"},
  429. {19, nullptr, "SetEthernetCommunicationEnabled"},
  430. {20, &IGeneralService::IsEthernetCommunicationEnabled, "IsEthernetCommunicationEnabled"},
  431. {21, &IGeneralService::IsAnyInternetRequestAccepted, "IsAnyInternetRequestAccepted"},
  432. {22, nullptr, "IsAnyForegroundRequestAccepted"},
  433. {23, nullptr, "PutToSleep"},
  434. {24, nullptr, "WakeUp"},
  435. {25, nullptr, "GetSsidListVersion"},
  436. {26, nullptr, "SetExclusiveClient"},
  437. {27, nullptr, "GetDefaultIpSetting"},
  438. {28, nullptr, "SetDefaultIpSetting"},
  439. {29, nullptr, "SetWirelessCommunicationEnabledForTest"},
  440. {30, nullptr, "SetEthernetCommunicationEnabledForTest"},
  441. {31, nullptr, "GetTelemetorySystemEventReadableHandle"},
  442. {32, nullptr, "GetTelemetryInfo"},
  443. {33, nullptr, "ConfirmSystemAvailability"},
  444. {34, nullptr, "SetBackgroundRequestEnabled"},
  445. {35, nullptr, "GetScanData"},
  446. {36, nullptr, "GetCurrentAccessPoint"},
  447. {37, nullptr, "Shutdown"},
  448. {38, nullptr, "GetAllowedChannels"},
  449. {39, nullptr, "NotifyApplicationSuspended"},
  450. {40, nullptr, "SetAcceptableNetworkTypeFlag"},
  451. {41, nullptr, "GetAcceptableNetworkTypeFlag"},
  452. {42, nullptr, "NotifyConnectionStateChanged"},
  453. {43, nullptr, "SetWowlDelayedWakeTime"},
  454. };
  455. // clang-format on
  456. RegisterHandlers(functions);
  457. }
  458. IGeneralService::~IGeneralService() = default;
  459. class NetworkInterface final : public ServiceFramework<NetworkInterface> {
  460. public:
  461. explicit NetworkInterface(const char* name, Core::System& system_)
  462. : ServiceFramework{system_, name} {
  463. static const FunctionInfo functions[] = {
  464. {4, &NetworkInterface::CreateGeneralServiceOld, "CreateGeneralServiceOld"},
  465. {5, &NetworkInterface::CreateGeneralService, "CreateGeneralService"},
  466. };
  467. RegisterHandlers(functions);
  468. }
  469. private:
  470. void CreateGeneralServiceOld(Kernel::HLERequestContext& ctx) {
  471. LOG_DEBUG(Service_NIFM, "called");
  472. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  473. rb.Push(ResultSuccess);
  474. rb.PushIpcInterface<IGeneralService>(system);
  475. }
  476. void CreateGeneralService(Kernel::HLERequestContext& ctx) {
  477. LOG_DEBUG(Service_NIFM, "called");
  478. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  479. rb.Push(ResultSuccess);
  480. rb.PushIpcInterface<IGeneralService>(system);
  481. }
  482. };
  483. void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
  484. std::make_shared<NetworkInterface>("nifm:a", system)->InstallAsService(service_manager);
  485. std::make_shared<NetworkInterface>("nifm:s", system)->InstallAsService(service_manager);
  486. std::make_shared<NetworkInterface>("nifm:u", system)->InstallAsService(service_manager);
  487. }
  488. } // namespace Service::NIFM