summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Subv2017-03-13 16:53:20 -0500
committerGravatar Subv2017-03-26 08:17:51 -0500
commit9771615f1638c2a548067717c12426539ad60541 (patch)
treef917581b6d94df36dd79655d338be40205447c18
parentMerge pull request #2614 from Schplee/patch-1 (diff)
downloadyuzu-9771615f1638c2a548067717c12426539ad60541.tar.gz
yuzu-9771615f1638c2a548067717c12426539ad60541.tar.xz
yuzu-9771615f1638c2a548067717c12426539ad60541.zip
Services/UDS: Initial support for hosting local-wlan networks.
Currently it will let games create a network as hosts, but will not broadcast it anywhere and will not allow clients to connect.
-rw-r--r--src/core/hle/service/nwm/nwm_uds.cpp319
-rw-r--r--src/core/hle/service/nwm/nwm_uds.h61
2 files changed, 336 insertions, 44 deletions
diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp
index 08fade320..789476418 100644
--- a/src/core/hle/service/nwm/nwm_uds.cpp
+++ b/src/core/hle/service/nwm/nwm_uds.cpp
@@ -1,16 +1,48 @@
1// Copyright 2014 Citra Emulator Project 1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstring>
6#include <unordered_map>
7#include <vector>
5#include "common/common_types.h" 8#include "common/common_types.h"
6#include "common/logging/log.h" 9#include "common/logging/log.h"
7#include "core/hle/kernel/event.h" 10#include "core/hle/kernel/event.h"
11#include "core/hle/kernel/shared_memory.h"
12#include "core/hle/result.h"
8#include "core/hle/service/nwm/nwm_uds.h" 13#include "core/hle/service/nwm/nwm_uds.h"
14#include "core/memory.h"
9 15
10namespace Service { 16namespace Service {
11namespace NWM { 17namespace NWM {
12 18
13static Kernel::SharedPtr<Kernel::Event> uds_handle_event; 19// Event that is signaled every time the connection status changes.
20static Kernel::SharedPtr<Kernel::Event> connection_status_event;
21
22// Shared memory provided by the application to store the receive buffer.
23// This is not currently used.
24static Kernel::SharedPtr<Kernel::SharedMemory> recv_buffer_memory;
25
26// Connection status of this 3DS.
27static ConnectionStatus connection_status{};
28
29// Node information about the current 3DS.
30// TODO(Subv): Keep an array of all nodes connected to the network,
31// that data has to be retransmitted in every beacon frame.
32static NodeInfo node_info;
33
34// Mapping of bind node ids to their respective events.
35static std::unordered_map<u32, Kernel::SharedPtr<Kernel::Event>> bind_node_events;
36
37// The wifi network channel that the network is currently on.
38// Since we're not actually interacting with physical radio waves, this is just a dummy value.
39static u8 network_channel = DefaultNetworkChannel;
40
41// The identifier of the network kind, this is used to filter away networks that we're not interested in.
42static u32 wlan_comm_id = 0;
43
44// Application data that is sent when broadcasting the beacon frames.
45static std::vector<u8> application_data;
14 46
15/** 47/**
16 * NWM_UDS::Shutdown service function 48 * NWM_UDS::Shutdown service function
@@ -32,14 +64,14 @@ static void Shutdown(Interface* self) {
32 64
33/** 65/**
34 * NWM_UDS::RecvBeaconBroadcastData service function 66 * NWM_UDS::RecvBeaconBroadcastData service function
67 * Returns the raw beacon data for nearby networks that match the supplied WlanCommId.
35 * Inputs: 68 * Inputs:
36 * 1 : Output buffer max size 69 * 1 : Output buffer max size
37 * 2 : Unknown 70 * 2-3 : Unknown
38 * 3 : Unknown 71 * 4-5 : Host MAC address.
39 * 4 : MAC address? 72 * 6-14 : Unused
40 * 6-14 : Unknown, usually zero / uninitialized? 73 * 15 : WLan Comm Id
41 * 15 : WLan Comm ID 74 * 16 : Id
42 * 16 : This is the ID also located at offset 0xE in the CTR-generation structure.
43 * 17 : Value 0 75 * 17 : Value 0
44 * 18 : Input handle 76 * 18 : Input handle
45 * 19 : (Size<<4) | 12 77 * 19 : (Size<<4) | 12
@@ -77,42 +109,234 @@ static void RecvBeaconBroadcastData(Interface* self) {
77/** 109/**
78 * NWM_UDS::Initialize service function 110 * NWM_UDS::Initialize service function
79 * Inputs: 111 * Inputs:
80 * 1 : Unknown 112 * 1 : Shared memory size
81 * 2-11 : Input Structure 113 * 2-11 : Input NodeInfo Structure
82 * 12 : Unknown u16 114 * 12 : 2-byte Version
83 * 13 : Value 0 115 * 13 : Value 0
84 * 14 : Handle 116 * 14 : Shared memory handle
85 * Outputs: 117 * Outputs:
86 * 0 : Return header 118 * 0 : Return header
87 * 1 : Result of function, 0 on success, otherwise error code 119 * 1 : Result of function, 0 on success, otherwise error code
88 * 2 : Value 0 120 * 2 : Value 0
89 * 3 : Output handle 121 * 3 : Output event handle
90 */ 122 */
91static void InitializeWithVersion(Interface* self) { 123static void InitializeWithVersion(Interface* self) {
92 u32* cmd_buff = Kernel::GetCommandBuffer(); 124 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1B, 12, 2);
93 u32 unk1 = cmd_buff[1]; 125
94 u32 unk2 = cmd_buff[12]; 126 u32 sharedmem_size = rp.Pop<u32>();
95 u32 value = cmd_buff[13]; 127
96 u32 handle = cmd_buff[14]; 128 // Update the node information with the data the game gave us.
97 129 rp.PopRaw(node_info);
98 // Because NWM service is not implemented at all, we stub the Initialize function with an error 130
99 // code instead of success to prevent games from using the service and from causing more issues. 131 u16 version;
100 // The error code is from a real 3DS with wifi off, thus believed to be "network disabled". 132 rp.PopRaw(version);
101 /* 133 Kernel::Handle sharedmem_handle = rp.PopHandle();
102 cmd_buff[1] = RESULT_SUCCESS.raw; 134
103 cmd_buff[2] = 0; 135 recv_buffer_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(sharedmem_handle);
104 cmd_buff[3] = Kernel::g_handle_table.Create(uds_handle_event) 136
105 .MoveFrom(); // TODO(purpasmart): Verify if this is a event handle 137 ASSERT_MSG(recv_buffer_memory->size == sharedmem_size, "Invalid shared memory size.");
106 */ 138
107 cmd_buff[0] = IPC::MakeHeader(0x1B, 1, 2); 139 // Reset the connection status, it contains all zeros after initialization,
108 cmd_buff[1] = ResultCode(static_cast<ErrorDescription>(2), ErrorModule::UDS, 140 // except for the actual status value.
109 ErrorSummary::StatusChanged, ErrorLevel::Status) 141 connection_status = {};
110 .raw; 142 connection_status.status = static_cast<u32>(NetworkStatus::NotConnected);
111 cmd_buff[2] = 0; 143
112 cmd_buff[3] = 0; 144 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
113 145 rb.Push(RESULT_SUCCESS);
114 LOG_WARNING(Service_NWM, "(STUBBED) called unk1=0x%08X, unk2=0x%08X, value=%u, handle=0x%08X", 146 rb.PushCopyHandles(Kernel::g_handle_table.Create(connection_status_event).MoveFrom());
115 unk1, unk2, value, handle); 147
148 LOG_DEBUG(Service_NWM, "called sharedmem_size=0x%08X, version=0x%08X, sharedmem_handle=0x%08X",
149 sharedmem_size, version, sharedmem_handle);
150}
151
152/**
153 * NWM_UDS::GetConnectionStatus service function.
154 * Returns the connection status structure for the currently open network connection.
155 * This structure contains information about the connection,
156 * like the number of connected nodes, etc.
157 * Inputs:
158 * 0 : Command header.
159 * Outputs:
160 * 0 : Return header
161 * 1 : Result of function, 0 on success, otherwise error code
162 * 2-13 : Channel of the current WiFi network connection.
163 */
164static void GetConnectionStatus(Interface* self) {
165 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xB, 0, 0);
166 IPC::RequestBuilder rb = rp.MakeBuilder(13, 0);
167
168 rb.Push(RESULT_SUCCESS);
169 rb.PushRaw(connection_status);
170
171 LOG_DEBUG(Service_NWM, "called");
172}
173
174/**
175 * NWM_UDS::Bind service function.
176 * Binds a BindNodeId to a data channel and retrieves a data event.
177 * Inputs:
178 * 1 : BindNodeId
179 * 2 : Receive buffer size.
180 * 3 : u8 Data channel to bind to.
181 * 4 : Network node id.
182 * Outputs:
183 * 0 : Return header
184 * 1 : Result of function, 0 on success, otherwise error code
185 * 2 : Copy handle descriptor.
186 * 3 : Data available event handle.
187 */
188static void Bind(Interface* self) {
189 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x12, 4, 0);
190
191 u32 bind_node_id = rp.Pop<u32>();
192 u32 recv_buffer_size = rp.Pop<u32>();
193 u8 data_channel;
194 rp.PopRaw(data_channel);
195 u16 network_node_id;
196 rp.PopRaw(network_node_id);
197
198 // TODO(Subv): Store the data channel and verify it when receiving data frames.
199
200 LOG_DEBUG(Service_NWM, "called");
201
202 if (data_channel == 0) {
203 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
204 rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS,
205 ErrorSummary::WrongArgument, ErrorLevel::Usage));
206 return;
207 }
208
209 // Create a new event for this bind node.
210 // TODO(Subv): Signal this event when new data is received on this data channel.
211 auto event = Kernel::Event::Create(Kernel::ResetType::OneShot,
212 "NWM::BindNodeEvent" + std::to_string(bind_node_id));
213 bind_node_events[bind_node_id] = event;
214
215 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
216
217 rb.Push(RESULT_SUCCESS);
218 rb.PushCopyHandles(Kernel::g_handle_table.Create(event).MoveFrom());
219}
220
221/**
222 * NWM_UDS::BeginHostingNetwork service function.
223 * Creates a network and starts broadcasting its presence.
224 * Inputs:
225 * 1 : Passphrase buffer size.
226 * 3 : VAddr of the NetworkInfo structure.
227 * 5 : VAddr of the passphrase.
228 * Outputs:
229 * 0 : Return header
230 * 1 : Result of function, 0 on success, otherwise error code
231 */
232static void BeginHostingNetwork(Interface* self) {
233 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1D, 1, 4);
234
235 const u32 passphrase_size = rp.Pop<u32>();
236
237 size_t desc_size;
238 const VAddr network_info_address = rp.PopStaticBuffer(&desc_size, false);
239 ASSERT(desc_size == sizeof(NetworkInfo));
240 const VAddr passphrase_address = rp.PopStaticBuffer(&desc_size, false);
241 ASSERT(desc_size == passphrase_size);
242
243 // TODO(Subv): Store the passphrase and verify it when attempting a connection.
244
245 LOG_DEBUG(Service_NWM, "called");
246
247 NetworkInfo network_info;
248 Memory::ReadBlock(network_info_address, &network_info, sizeof(NetworkInfo));
249
250 connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsHost);
251 connection_status.max_nodes = network_info.max_nodes;
252 wlan_comm_id = network_info.wlan_comm_id;
253
254 // There's currently only one node in the network (the host).
255 connection_status.total_nodes = 1;
256 // The host is always the first node
257 connection_status.network_node_id = 1;
258 node_info.network_node_id = 1;
259 // Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken.
260 connection_status.node_bitmask |= 1;
261
262 // If the game has a preferred channel, use that instead.
263 if (network_info.channel != 0)
264 network_channel = network_info.channel;
265
266 // Clear the pre-existing application data.
267 application_data.clear();
268
269 connection_status_event->Signal();
270
271 // TODO(Subv): Start broadcasting the network, send a beacon frame every 102.4ms.
272
273 LOG_WARNING(Service_NWM,
274 "An UDS network has been created, but broadcasting it is unimplemented.");
275
276 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
277 rb.Push(RESULT_SUCCESS);
278}
279
280/**
281 * NWM_UDS::GetChannel service function.
282 * Returns the WiFi channel in which the network we're connected to is transmitting.
283 * Inputs:
284 * 0 : Command header.
285 * Outputs:
286 * 0 : Return header
287 * 1 : Result of function, 0 on success, otherwise error code
288 * 2 : Channel of the current WiFi network connection.
289 */
290static void GetChannel(Interface* self) {
291 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0);
292 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
293
294 u8 channel = network_channel;
295
296 if (connection_status.status == static_cast<u32>(NetworkStatus::NotConnected))
297 channel = 0;
298
299 rb.Push(RESULT_SUCCESS);
300 rb.PushRaw(channel);
301
302 LOG_DEBUG(Service_NWM, "called");
303}
304
305/**
306 * NWM_UDS::SetApplicationData service function.
307 * Updates the application data that is being broadcast in the beacon frames
308 * for the network that we're hosting.
309 * Inputs:
310 * 1 : Data size.
311 * 3 : VAddr of the data.
312 * Outputs:
313 * 0 : Return header
314 * 1 : Result of function, 0 on success, otherwise error code
315 * 2 : Channel of the current WiFi network connection.
316 */
317static void SetApplicationData(Interface* self) {
318 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 1, 2);
319
320 u32 size = rp.Pop<u32>();
321
322 size_t desc_size;
323 const VAddr address = rp.PopStaticBuffer(&desc_size, false);
324 ASSERT(desc_size == size);
325
326 LOG_DEBUG(Service_NWM, "called");
327
328 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
329
330 if (size > ApplicationDataSize) {
331 rb.Push(ResultCode(ErrorDescription::TooLarge, ErrorModule::UDS,
332 ErrorSummary::WrongArgument, ErrorLevel::Usage));
333 return;
334 }
335
336 application_data.resize(size);
337 Memory::ReadBlock(address, application_data.data(), size);
338
339 rb.Push(RESULT_SUCCESS);
116} 340}
117 341
118const Interface::FunctionInfo FunctionTable[] = { 342const Interface::FunctionInfo FunctionTable[] = {
@@ -126,20 +350,20 @@ const Interface::FunctionInfo FunctionTable[] = {
126 {0x00080000, nullptr, "DestroyNetwork"}, 350 {0x00080000, nullptr, "DestroyNetwork"},
127 {0x00090442, nullptr, "ConnectNetwork (deprecated)"}, 351 {0x00090442, nullptr, "ConnectNetwork (deprecated)"},
128 {0x000A0000, nullptr, "DisconnectNetwork"}, 352 {0x000A0000, nullptr, "DisconnectNetwork"},
129 {0x000B0000, nullptr, "GetConnectionStatus"}, 353 {0x000B0000, GetConnectionStatus, "GetConnectionStatus"},
130 {0x000D0040, nullptr, "GetNodeInformation"}, 354 {0x000D0040, nullptr, "GetNodeInformation"},
131 {0x000E0006, nullptr, "DecryptBeaconData (deprecated)"}, 355 {0x000E0006, nullptr, "DecryptBeaconData (deprecated)"},
132 {0x000F0404, RecvBeaconBroadcastData, "RecvBeaconBroadcastData"}, 356 {0x000F0404, RecvBeaconBroadcastData, "RecvBeaconBroadcastData"},
133 {0x00100042, nullptr, "SetApplicationData"}, 357 {0x00100042, SetApplicationData, "SetApplicationData"},
134 {0x00110040, nullptr, "GetApplicationData"}, 358 {0x00110040, nullptr, "GetApplicationData"},
135 {0x00120100, nullptr, "Bind"}, 359 {0x00120100, Bind, "Bind"},
136 {0x00130040, nullptr, "Unbind"}, 360 {0x00130040, nullptr, "Unbind"},
137 {0x001400C0, nullptr, "PullPacket"}, 361 {0x001400C0, nullptr, "PullPacket"},
138 {0x00150080, nullptr, "SetMaxSendDelay"}, 362 {0x00150080, nullptr, "SetMaxSendDelay"},
139 {0x00170182, nullptr, "SendTo"}, 363 {0x00170182, nullptr, "SendTo"},
140 {0x001A0000, nullptr, "GetChannel"}, 364 {0x001A0000, GetChannel, "GetChannel"},
141 {0x001B0302, InitializeWithVersion, "InitializeWithVersion"}, 365 {0x001B0302, InitializeWithVersion, "InitializeWithVersion"},
142 {0x001D0044, nullptr, "BeginHostingNetwork"}, 366 {0x001D0044, BeginHostingNetwork, "BeginHostingNetwork"},
143 {0x001E0084, nullptr, "ConnectToNetwork"}, 367 {0x001E0084, nullptr, "ConnectToNetwork"},
144 {0x001F0006, nullptr, "DecryptBeaconData"}, 368 {0x001F0006, nullptr, "DecryptBeaconData"},
145 {0x00200040, nullptr, "Flush"}, 369 {0x00200040, nullptr, "Flush"},
@@ -148,13 +372,20 @@ const Interface::FunctionInfo FunctionTable[] = {
148}; 372};
149 373
150NWM_UDS::NWM_UDS() { 374NWM_UDS::NWM_UDS() {
151 uds_handle_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "NWM::uds_handle_event"); 375 connection_status_event =
376 Kernel::Event::Create(Kernel::ResetType::OneShot, "NWM::connection_status_event");
152 377
153 Register(FunctionTable); 378 Register(FunctionTable);
154} 379}
155 380
156NWM_UDS::~NWM_UDS() { 381NWM_UDS::~NWM_UDS() {
157 uds_handle_event = nullptr; 382 application_data.clear();
383 bind_node_events.clear();
384 connection_status_event = nullptr;
385 recv_buffer_memory = nullptr;
386
387 connection_status = {};
388 connection_status.status = static_cast<u32>(NetworkStatus::NotConnected);
158} 389}
159 390
160} // namespace NWM 391} // namespace NWM
diff --git a/src/core/hle/service/nwm/nwm_uds.h b/src/core/hle/service/nwm/nwm_uds.h
index 55db748f6..3dfc32de8 100644
--- a/src/core/hle/service/nwm/nwm_uds.h
+++ b/src/core/hle/service/nwm/nwm_uds.h
@@ -4,6 +4,10 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <cstddef>
9#include "common/common_types.h"
10#include "common/swap.h"
7#include "core/hle/service/service.h" 11#include "core/hle/service/service.h"
8 12
9// Local-WLAN service 13// Local-WLAN service
@@ -11,6 +15,63 @@
11namespace Service { 15namespace Service {
12namespace NWM { 16namespace NWM {
13 17
18const size_t ApplicationDataSize = 0xC8;
19const u8 DefaultNetworkChannel = 11;
20
21struct NodeInfo {
22 u64_le friend_code_seed;
23 std::array<u16_le, 10> username;
24 INSERT_PADDING_BYTES(4);
25 u16_le network_node_id;
26 INSERT_PADDING_BYTES(6);
27};
28
29static_assert(sizeof(NodeInfo) == 40, "NodeInfo has incorrect size.");
30
31enum class NetworkStatus {
32 NotConnected = 3,
33 ConnectedAsHost = 6,
34 ConnectedAsClient = 9,
35 ConnectedAsSpectator = 10,
36};
37
38struct ConnectionStatus {
39 u32_le status;
40 INSERT_PADDING_WORDS(1);
41 u16_le network_node_id;
42 INSERT_PADDING_BYTES(2);
43 INSERT_PADDING_BYTES(32);
44 u8 total_nodes;
45 u8 max_nodes;
46 u16_le node_bitmask;
47};
48
49static_assert(sizeof(ConnectionStatus) == 0x30, "ConnectionStatus has incorrect size.");
50
51struct NetworkInfo {
52 std::array<u8, 6> host_mac_address;
53 u8 channel;
54 INSERT_PADDING_BYTES(1);
55 u8 initialized;
56 INSERT_PADDING_BYTES(3);
57 std::array<u8, 3> oui_value;
58 u8 oui_type;
59 // This field is received as BigEndian from the game.
60 u32_be wlan_comm_id;
61 u8 id;
62 INSERT_PADDING_BYTES(1);
63 u16_be attributes;
64 u32_be network_id;
65 u8 total_nodes;
66 u8 max_nodes;
67 INSERT_PADDING_BYTES(2);
68 INSERT_PADDING_BYTES(0x1F);
69 u8 application_data_size;
70 std::array<u8, ApplicationDataSize> application_data;
71};
72
73static_assert(sizeof(NetworkInfo) == 0x108, "NetworkInfo has incorrect size.");
74
14class NWM_UDS final : public Interface { 75class NWM_UDS final : public Interface {
15public: 76public:
16 NWM_UDS(); 77 NWM_UDS();