diff --git a/src/core/hle/service/ac_u.cpp b/src/core/hle/service/ac_u.cpp index 12d94f37a..18026975f 100644 --- a/src/core/hle/service/ac_u.cpp +++ b/src/core/hle/service/ac_u.cpp @@ -11,11 +11,85 @@ namespace AC_U { +struct ACConfig { + std::array data; +}; + +static ACConfig default_config{}; + +static bool ac_connected = false; + +static Kernel::SharedPtr close_event; +static Kernel::SharedPtr connect_event; +static Kernel::SharedPtr disconnect_event; + +/** + * AC_U::CreateDefaultConfig service function + * Inputs: + * 64 : ACConfig size << 14 | 2 + * 65 : pointer to ACConfig struct + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void CreateDefaultConfig(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 ac_config_addr = cmd_buff[65]; + + ASSERT_MSG(cmd_buff[64] == (sizeof(ACConfig) << 14 | 2), + "Output buffer size not equal ACConfig size"); + + Memory::WriteBlock(ac_config_addr, &default_config, sizeof(ACConfig)); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_WARNING(Service_AC, "(STUBBED) called"); +} + +/** + * AC_U::ConnectAsync service function + * Inputs: + * 1 : ProcessId Header + * 3 : Copy Handle Header + * 4 : Connection Event handle + * 5 : ACConfig size << 14 | 2 + * 6 : pointer to ACConfig struct + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void ConnectAsync(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + connect_event = Kernel::g_handle_table.Get(cmd_buff[4]); + if (connect_event) { + connect_event->name = "AC_U:connect_event"; + connect_event->Signal(); + ac_connected = true; + } + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_WARNING(Service_AC, "(STUBBED) called"); +} + +/** + * AC_U::GetConnectResult service function + * Inputs: + * 1 : ProcessId Header + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void GetConnectResult(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_WARNING(Service_AC, "(STUBBED) called"); +} + /** * AC_U::CloseAsync service function * Inputs: - * 1 : Always 0x20 - * 3 : Always 0 + * 1 : ProcessId Header + * 3 : Copy Handle Header * 4 : Event handle, should be signaled when AC connection is closed * Outputs: * 1 : Result of function, 0 on success, otherwise error code @@ -23,16 +97,37 @@ namespace AC_U { static void CloseAsync(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - auto evt = Kernel::g_handle_table.Get(cmd_buff[4]); - - if (evt) { - evt->name = "AC_U:close_event"; - evt->Signal(); + if (ac_connected && disconnect_event) { + disconnect_event->Signal(); } + + close_event = Kernel::g_handle_table.Get(cmd_buff[4]); + if (close_event) { + close_event->name = "AC_U:close_event"; + close_event->Signal(); + } + + ac_connected = false; + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_AC, "(STUBBED) called"); +} + +/** + * AC_U::GetCloseResult service function + * Inputs: + * 1 : ProcessId Header + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void GetCloseResult(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error LOG_WARNING(Service_AC, "(STUBBED) called"); } + /** * AC_U::GetWifiStatus service function * Outputs: @@ -51,6 +146,75 @@ static void GetWifiStatus(Service::Interface* self) { LOG_WARNING(Service_AC, "(STUBBED) called"); } +/** + * AC_U::GetInfraPriority service function + * Inputs: + * 1 : ACConfig size << 14 | 2 + * 2 : pointer to ACConfig struct + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Infra Priority + */ +static void GetInfraPriority(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = 0; // Infra Priority, default 0 + + LOG_WARNING(Service_AC, "(STUBBED) called"); +} + +/** + * AC_U::SetRequestEulaVersion service function + * Inputs: + * 1 : Eula Version major + * 2 : Eula Version minor + * 3 : ACConfig size << 14 | 2 + * 4 : Input pointer to ACConfig struct + * 64 : ACConfig size << 14 | 2 + * 65 : Output pointer to ACConfig struct + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Infra Priority + */ +static void SetRequestEulaVersion(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 major = cmd_buff[1] & 0xFF; + u32 minor = cmd_buff[2] & 0xFF; + + ASSERT_MSG(cmd_buff[3] == (sizeof(ACConfig) << 14 | 2), + "Input buffer size not equal ACConfig size"); + ASSERT_MSG(cmd_buff[64] == (sizeof(ACConfig) << 14 | 2), + "Output buffer size not equal ACConfig size"); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = 0; // Infra Priority + + LOG_WARNING(Service_AC, "(STUBBED) called, major=%u, minor=%u", major, minor); +} + +/** + * AC_U::RegisterDisconnectEvent service function + * Inputs: + * 1 : ProcessId Header + * 3 : Copy Handle Header + * 4 : Event handle, should be signaled when AC connection is closed + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void RegisterDisconnectEvent(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + disconnect_event = Kernel::g_handle_table.Get(cmd_buff[4]); + if (disconnect_event) { + disconnect_event->name = "AC_U:disconnect_event"; + } + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_WARNING(Service_AC, "(STUBBED) called"); +} + /** * AC_U::IsConnected service function * Outputs: @@ -61,26 +225,29 @@ static void IsConnected(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = false; // Not connected to ac:u service + cmd_buff[2] = ac_connected; LOG_WARNING(Service_AC, "(STUBBED) called"); } const Interface::FunctionInfo FunctionTable[] = { - {0x00010000, nullptr, "CreateDefaultConfig"}, - {0x00040006, nullptr, "ConnectAsync"}, - {0x00050002, nullptr, "GetConnectResult"}, + {0x00010000, CreateDefaultConfig, "CreateDefaultConfig"}, + {0x00040006, ConnectAsync, "ConnectAsync"}, + {0x00050002, GetConnectResult, "GetConnectResult"}, + {0x00070002, nullptr, "CancelConnectAsync"}, {0x00080004, CloseAsync, "CloseAsync"}, - {0x00090002, nullptr, "GetCloseResult"}, + {0x00090002, GetCloseResult, "GetCloseResult"}, {0x000A0000, nullptr, "GetLastErrorCode"}, + {0x000C0000, nullptr, "GetStatus"}, {0x000D0000, GetWifiStatus, "GetWifiStatus"}, {0x000E0042, nullptr, "GetCurrentAPInfo"}, {0x00100042, nullptr, "GetCurrentNZoneInfo"}, {0x00110042, nullptr, "GetNZoneApNumService"}, + {0x001D0042, nullptr, "ScanAPs"}, {0x00240042, nullptr, "AddDenyApType"}, - {0x00270002, nullptr, "GetInfraPriority"}, - {0x002D0082, nullptr, "SetRequestEulaVersion"}, - {0x00300004, nullptr, "RegisterDisconnectEvent"}, + {0x00270002, GetInfraPriority, "GetInfraPriority"}, + {0x002D0082, SetRequestEulaVersion, "SetRequestEulaVersion"}, + {0x00300004, RegisterDisconnectEvent, "RegisterDisconnectEvent"}, {0x003C0042, nullptr, "GetAPSSIDList"}, {0x003E0042, IsConnected, "IsConnected"}, {0x00400042, nullptr, "SetClientVersion"}, @@ -91,6 +258,18 @@ const Interface::FunctionInfo FunctionTable[] = { Interface::Interface() { Register(FunctionTable); + + ac_connected = false; + + close_event = nullptr; + connect_event = nullptr; + disconnect_event = nullptr; +} + +Interface::~Interface() { + close_event = nullptr; + connect_event = nullptr; + disconnect_event = nullptr; } } // namespace diff --git a/src/core/hle/service/ac_u.h b/src/core/hle/service/ac_u.h index f1d26ebe8..6592b21c9 100644 --- a/src/core/hle/service/ac_u.h +++ b/src/core/hle/service/ac_u.h @@ -16,6 +16,7 @@ namespace AC_U { class Interface : public Service::Interface { public: Interface(); + ~Interface(); std::string GetPortName() const override { return "ac:u";