summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/hle/service/nwm/nwm_uds.cpp275
-rw-r--r--src/core/hle/service/nwm/uds_connection.cpp9
-rw-r--r--src/core/hle/service/nwm/uds_connection.h5
-rw-r--r--src/core/hle/service/nwm/uds_data.cpp21
-rw-r--r--src/core/hle/service/nwm/uds_data.h28
5 files changed, 250 insertions, 88 deletions
diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp
index 893bbb1e7..4e2af9ae6 100644
--- a/src/core/hle/service/nwm/nwm_uds.cpp
+++ b/src/core/hle/service/nwm/nwm_uds.cpp
@@ -2,8 +2,10 @@
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 <algorithm>
5#include <array> 6#include <array>
6#include <cstring> 7#include <cstring>
8#include <list>
7#include <mutex> 9#include <mutex>
8#include <unordered_map> 10#include <unordered_map>
9#include <vector> 11#include <vector>
@@ -37,9 +39,12 @@ static ConnectionStatus connection_status{};
37/* Node information about the current network. 39/* Node information about the current network.
38 * The amount of elements in this vector is always the maximum number 40 * The amount of elements in this vector is always the maximum number
39 * of nodes specified in the network configuration. 41 * of nodes specified in the network configuration.
40 * The first node is always the host, so this always contains at least 1 entry. 42 * The first node is always the host.
41 */ 43 */
42static NodeList node_info(1); 44static NodeList node_info;
45
46// Node information about our own system.
47static NodeInfo current_node;
43 48
44// Mapping of bind node ids to their respective events. 49// Mapping of bind node ids to their respective events.
45static std::unordered_map<u32, Kernel::SharedPtr<Kernel::Event>> bind_node_events; 50static std::unordered_map<u32, Kernel::SharedPtr<Kernel::Event>> bind_node_events;
@@ -54,6 +59,10 @@ static NetworkInfo network_info;
54// Event that will generate and send the 802.11 beacon frames. 59// Event that will generate and send the 802.11 beacon frames.
55static int beacon_broadcast_event; 60static int beacon_broadcast_event;
56 61
62// Mutex to synchronize access to the connection status between the emulation thread and the
63// network thread.
64static std::mutex connection_status_mutex;
65
57// Mutex to synchronize access to the list of received beacons between the emulation thread and the 66// Mutex to synchronize access to the list of received beacons between the emulation thread and the
58// network thread. 67// network thread.
59static std::mutex beacon_mutex; 68static std::mutex beacon_mutex;
@@ -63,14 +72,26 @@ static std::mutex beacon_mutex;
63constexpr size_t MaxBeaconFrames = 15; 72constexpr size_t MaxBeaconFrames = 15;
64 73
65// List of the last <MaxBeaconFrames> beacons received from the network. 74// List of the last <MaxBeaconFrames> beacons received from the network.
66static std::deque<Network::WifiPacket> received_beacons; 75static std::list<Network::WifiPacket> received_beacons;
67 76
68/** 77/**
69 * Returns a list of received 802.11 beacon frames from the specified sender since the last call. 78 * Returns a list of received 802.11 beacon frames from the specified sender since the last call.
70 */ 79 */
71std::deque<Network::WifiPacket> GetReceivedBeacons(const MacAddress& sender) { 80std::list<Network::WifiPacket> GetReceivedBeacons(const MacAddress& sender) {
72 std::lock_guard<std::mutex> lock(beacon_mutex); 81 std::lock_guard<std::mutex> lock(beacon_mutex);
73 // TODO(Subv): Filter by sender. 82 if (sender != Network::BroadcastMac) {
83 std::list<Network::WifiPacket> filtered_list;
84 const auto beacon = std::find_if(received_beacons.begin(), received_beacons.end(),
85 [&sender](const Network::WifiPacket& packet) {
86 return packet.transmitter_address == sender;
87 });
88 if (beacon != received_beacons.end()) {
89 filtered_list.push_back(*beacon);
90 // TODO(B3N30): Check if the complete deque is cleared or just the fetched entries
91 received_beacons.erase(beacon);
92 }
93 return filtered_list;
94 }
74 return std::move(received_beacons); 95 return std::move(received_beacons);
75} 96}
76 97
@@ -83,6 +104,15 @@ void SendPacket(Network::WifiPacket& packet) {
83// limit is exceeded. 104// limit is exceeded.
84void HandleBeaconFrame(const Network::WifiPacket& packet) { 105void HandleBeaconFrame(const Network::WifiPacket& packet) {
85 std::lock_guard<std::mutex> lock(beacon_mutex); 106 std::lock_guard<std::mutex> lock(beacon_mutex);
107 const auto unique_beacon =
108 std::find_if(received_beacons.begin(), received_beacons.end(),
109 [&packet](const Network::WifiPacket& new_packet) {
110 return new_packet.transmitter_address == packet.transmitter_address;
111 });
112 if (unique_beacon != received_beacons.end()) {
113 // We already have a beacon from the same mac in the deque, remove the old one;
114 received_beacons.erase(unique_beacon);
115 }
86 116
87 received_beacons.emplace_back(packet); 117 received_beacons.emplace_back(packet);
88 118
@@ -91,14 +121,33 @@ void HandleBeaconFrame(const Network::WifiPacket& packet) {
91 received_beacons.pop_front(); 121 received_beacons.pop_front();
92} 122}
93 123
124void HandleAssociationResponseFrame(const Network::WifiPacket& packet) {
125 auto assoc_result = GetAssociationResult(packet.data);
126
127 ASSERT_MSG(std::get<AssocStatus>(assoc_result) == AssocStatus::Successful,
128 "Could not join network");
129 {
130 std::lock_guard<std::mutex> lock(connection_status_mutex);
131 ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::Connecting));
132 }
133
134 // Send the EAPoL-Start packet to the server.
135 using Network::WifiPacket;
136 WifiPacket eapol_start;
137 eapol_start.channel = network_channel;
138 eapol_start.data = GenerateEAPoLStartFrame(std::get<u16>(assoc_result), current_node);
139 // TODO(B3N30): Encrypt the packet.
140 eapol_start.destination_address = packet.transmitter_address;
141 eapol_start.type = WifiPacket::PacketType::Data;
142
143 SendPacket(eapol_start);
144}
145
94/* 146/*
95 * Returns an available index in the nodes array for the 147 * Returns an available index in the nodes array for the
96 * currently-hosted UDS network. 148 * currently-hosted UDS network.
97 */ 149 */
98static u16 GetNextAvailableNodeId() { 150static u16 GetNextAvailableNodeId() {
99 ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost),
100 "Can not accept clients if we're not hosting a network");
101
102 for (u16 index = 0; index < connection_status.max_nodes; ++index) { 151 for (u16 index = 0; index < connection_status.max_nodes; ++index) {
103 if ((connection_status.node_bitmask & (1 << index)) == 0) 152 if ((connection_status.node_bitmask & (1 << index)) == 0)
104 return index; 153 return index;
@@ -113,35 +162,46 @@ static u16 GetNextAvailableNodeId() {
113 * authentication frame with SEQ1. 162 * authentication frame with SEQ1.
114 */ 163 */
115void StartConnectionSequence(const MacAddress& server) { 164void StartConnectionSequence(const MacAddress& server) {
116 ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::NotConnected));
117
118 // TODO(Subv): Handle timeout.
119
120 // Send an authentication frame with SEQ1
121 using Network::WifiPacket; 165 using Network::WifiPacket;
122 WifiPacket auth_request; 166 WifiPacket auth_request;
123 auth_request.channel = network_channel; 167 {
124 auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ1); 168 std::lock_guard<std::mutex> lock(connection_status_mutex);
125 auth_request.destination_address = server; 169 ASSERT(connection_status.status == static_cast<u32>(NetworkStatus::NotConnected));
126 auth_request.type = WifiPacket::PacketType::Authentication; 170
171 // TODO(Subv): Handle timeout.
172
173 // Send an authentication frame with SEQ1
174 auth_request.channel = network_channel;
175 auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ1);
176 auth_request.destination_address = server;
177 auth_request.type = WifiPacket::PacketType::Authentication;
178 }
127 179
128 SendPacket(auth_request); 180 SendPacket(auth_request);
129} 181}
130 182
131/// Sends an Association Response frame to the specified mac address 183/// Sends an Association Response frame to the specified mac address
132void SendAssociationResponseFrame(const MacAddress& address) { 184void SendAssociationResponseFrame(const MacAddress& address) {
133 ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost));
134
135 using Network::WifiPacket; 185 using Network::WifiPacket;
136 WifiPacket assoc_response; 186 WifiPacket assoc_response;
137 assoc_response.channel = network_channel; 187
138 // TODO(Subv): This will cause multiple clients to end up with the same association id, but 188 {
139 // we're not using that for anything. 189 std::lock_guard<std::mutex> lock(connection_status_mutex);
140 u16 association_id = 1; 190 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
141 assoc_response.data = GenerateAssocResponseFrame(AssocStatus::Successful, association_id, 191 LOG_ERROR(Service_NWM, "Connection sequence aborted, because connection status is %u",
142 network_info.network_id); 192 connection_status.status);
143 assoc_response.destination_address = address; 193 return;
144 assoc_response.type = WifiPacket::PacketType::AssociationResponse; 194 }
195
196 assoc_response.channel = network_channel;
197 // TODO(Subv): This will cause multiple clients to end up with the same association id, but
198 // we're not using that for anything.
199 u16 association_id = 1;
200 assoc_response.data = GenerateAssocResponseFrame(AssocStatus::Successful, association_id,
201 network_info.network_id);
202 assoc_response.destination_address = address;
203 assoc_response.type = WifiPacket::PacketType::AssociationResponse;
204 }
145 205
146 SendPacket(assoc_response); 206 SendPacket(assoc_response);
147} 207}
@@ -155,16 +215,23 @@ void SendAssociationResponseFrame(const MacAddress& address) {
155void HandleAuthenticationFrame(const Network::WifiPacket& packet) { 215void HandleAuthenticationFrame(const Network::WifiPacket& packet) {
156 // Only the SEQ1 auth frame is handled here, the SEQ2 frame doesn't need any special behavior 216 // Only the SEQ1 auth frame is handled here, the SEQ2 frame doesn't need any special behavior
157 if (GetAuthenticationSeqNumber(packet.data) == AuthenticationSeq::SEQ1) { 217 if (GetAuthenticationSeqNumber(packet.data) == AuthenticationSeq::SEQ1) {
158 ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost));
159
160 // Respond with an authentication response frame with SEQ2
161 using Network::WifiPacket; 218 using Network::WifiPacket;
162 WifiPacket auth_request; 219 WifiPacket auth_request;
163 auth_request.channel = network_channel; 220 {
164 auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2); 221 std::lock_guard<std::mutex> lock(connection_status_mutex);
165 auth_request.destination_address = packet.transmitter_address; 222 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
166 auth_request.type = WifiPacket::PacketType::Authentication; 223 LOG_ERROR(Service_NWM,
167 224 "Connection sequence aborted, because connection status is %u",
225 connection_status.status);
226 return;
227 }
228
229 // Respond with an authentication response frame with SEQ2
230 auth_request.channel = network_channel;
231 auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2);
232 auth_request.destination_address = packet.transmitter_address;
233 auth_request.type = WifiPacket::PacketType::Authentication;
234 }
168 SendPacket(auth_request); 235 SendPacket(auth_request);
169 236
170 SendAssociationResponseFrame(packet.transmitter_address); 237 SendAssociationResponseFrame(packet.transmitter_address);
@@ -180,6 +247,9 @@ void OnWifiPacketReceived(const Network::WifiPacket& packet) {
180 case Network::WifiPacket::PacketType::Authentication: 247 case Network::WifiPacket::PacketType::Authentication:
181 HandleAuthenticationFrame(packet); 248 HandleAuthenticationFrame(packet);
182 break; 249 break;
250 case Network::WifiPacket::PacketType::AssociationResponse:
251 HandleAssociationResponseFrame(packet);
252 break;
183 } 253 }
184} 254}
185 255
@@ -305,7 +375,7 @@ static void InitializeWithVersion(Interface* self) {
305 u32 sharedmem_size = rp.Pop<u32>(); 375 u32 sharedmem_size = rp.Pop<u32>();
306 376
307 // Update the node information with the data the game gave us. 377 // Update the node information with the data the game gave us.
308 rp.PopRaw(node_info[0]); 378 rp.PopRaw(current_node);
309 379
310 u16 version = rp.Pop<u16>(); 380 u16 version = rp.Pop<u16>();
311 381
@@ -315,10 +385,14 @@ static void InitializeWithVersion(Interface* self) {
315 385
316 ASSERT_MSG(recv_buffer_memory->size == sharedmem_size, "Invalid shared memory size."); 386 ASSERT_MSG(recv_buffer_memory->size == sharedmem_size, "Invalid shared memory size.");
317 387
318 // Reset the connection status, it contains all zeros after initialization, 388 {
319 // except for the actual status value. 389 std::lock_guard<std::mutex> lock(connection_status_mutex);
320 connection_status = {}; 390
321 connection_status.status = static_cast<u32>(NetworkStatus::NotConnected); 391 // Reset the connection status, it contains all zeros after initialization,
392 // except for the actual status value.
393 connection_status = {};
394 connection_status.status = static_cast<u32>(NetworkStatus::NotConnected);
395 }
322 396
323 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); 397 IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
324 rb.Push(RESULT_SUCCESS); 398 rb.Push(RESULT_SUCCESS);
@@ -348,12 +422,16 @@ static void GetConnectionStatus(Interface* self) {
348 IPC::RequestBuilder rb = rp.MakeBuilder(13, 0); 422 IPC::RequestBuilder rb = rp.MakeBuilder(13, 0);
349 423
350 rb.Push(RESULT_SUCCESS); 424 rb.Push(RESULT_SUCCESS);
351 rb.PushRaw(connection_status); 425 {
352 426 std::lock_guard<std::mutex> lock(connection_status_mutex);
353 // Reset the bitmask of changed nodes after each call to this 427 rb.PushRaw(connection_status);
354 // function to prevent falsely informing games of outstanding 428
355 // changes in subsequent calls. 429 // Reset the bitmask of changed nodes after each call to this
356 connection_status.changed_nodes = 0; 430 // function to prevent falsely informing games of outstanding
431 // changes in subsequent calls.
432 // TODO(Subv): Find exactly where the NWM module resets this value.
433 connection_status.changed_nodes = 0;
434 }
357 435
358 LOG_DEBUG(Service_NWM, "called"); 436 LOG_DEBUG(Service_NWM, "called");
359} 437}
@@ -434,31 +512,36 @@ static void BeginHostingNetwork(Interface* self) {
434 // The real UDS module throws a fatal error if this assert fails. 512 // The real UDS module throws a fatal error if this assert fails.
435 ASSERT_MSG(network_info.max_nodes > 1, "Trying to host a network of only one member."); 513 ASSERT_MSG(network_info.max_nodes > 1, "Trying to host a network of only one member.");
436 514
437 connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsHost); 515 {
438 516 std::lock_guard<std::mutex> lock(connection_status_mutex);
439 // Ensure the application data size is less than the maximum value. 517 connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsHost);
440 ASSERT_MSG(network_info.application_data_size <= ApplicationDataSize, "Data size is too big."); 518
441 519 // Ensure the application data size is less than the maximum value.
442 // Set up basic information for this network. 520 ASSERT_MSG(network_info.application_data_size <= ApplicationDataSize,
443 network_info.oui_value = NintendoOUI; 521 "Data size is too big.");
444 network_info.oui_type = static_cast<u8>(NintendoTagId::NetworkInfo); 522
445 523 // Set up basic information for this network.
446 connection_status.max_nodes = network_info.max_nodes; 524 network_info.oui_value = NintendoOUI;
447 525 network_info.oui_type = static_cast<u8>(NintendoTagId::NetworkInfo);
448 // Resize the nodes list to hold max_nodes. 526
449 node_info.resize(network_info.max_nodes); 527 connection_status.max_nodes = network_info.max_nodes;
450 528
451 // There's currently only one node in the network (the host). 529 // Resize the nodes list to hold max_nodes.
452 connection_status.total_nodes = 1; 530 node_info.resize(network_info.max_nodes);
453 network_info.total_nodes = 1; 531
454 // The host is always the first node 532 // There's currently only one node in the network (the host).
455 connection_status.network_node_id = 1; 533 connection_status.total_nodes = 1;
456 node_info[0].network_node_id = 1; 534 network_info.total_nodes = 1;
457 connection_status.nodes[0] = connection_status.network_node_id; 535 // The host is always the first node
458 // Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken. 536 connection_status.network_node_id = 1;
459 connection_status.node_bitmask |= 1; 537 current_node.network_node_id = 1;
460 // Notify the application that the first node was set. 538 connection_status.nodes[0] = connection_status.network_node_id;
461 connection_status.changed_nodes |= 1; 539 // Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken.
540 connection_status.node_bitmask |= 1;
541 // Notify the application that the first node was set.
542 connection_status.changed_nodes |= 1;
543 node_info[0] = current_node;
544 }
462 545
463 // If the game has a preferred channel, use that instead. 546 // If the game has a preferred channel, use that instead.
464 if (network_info.channel != 0) 547 if (network_info.channel != 0)
@@ -495,9 +578,13 @@ static void DestroyNetwork(Interface* self) {
495 // Unschedule the beacon broadcast event. 578 // Unschedule the beacon broadcast event.
496 CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); 579 CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0);
497 580
498 // TODO(Subv): Check if connection_status is indeed reset after this call. 581 {
499 connection_status = {}; 582 std::lock_guard<std::mutex> lock(connection_status_mutex);
500 connection_status.status = static_cast<u8>(NetworkStatus::NotConnected); 583
584 // TODO(Subv): Check if connection_status is indeed reset after this call.
585 connection_status = {};
586 connection_status.status = static_cast<u8>(NetworkStatus::NotConnected);
587 }
501 connection_status_event->Signal(); 588 connection_status_event->Signal();
502 589
503 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); 590 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
@@ -540,17 +627,24 @@ static void SendTo(Interface* self) {
540 627
541 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); 628 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
542 629
543 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsClient) && 630 u16 network_node_id;
544 connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
545 rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS,
546 ErrorSummary::InvalidState, ErrorLevel::Status));
547 return;
548 }
549 631
550 if (dest_node_id == connection_status.network_node_id) { 632 {
551 rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS, 633 std::lock_guard<std::mutex> lock(connection_status_mutex);
552 ErrorSummary::WrongArgument, ErrorLevel::Status)); 634 if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsClient) &&
553 return; 635 connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) {
636 rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS,
637 ErrorSummary::InvalidState, ErrorLevel::Status));
638 return;
639 }
640
641 if (dest_node_id == connection_status.network_node_id) {
642 rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS,
643 ErrorSummary::WrongArgument, ErrorLevel::Status));
644 return;
645 }
646
647 network_node_id = connection_status.network_node_id;
554 } 648 }
555 649
556 // TODO(Subv): Do something with the flags. 650 // TODO(Subv): Do something with the flags.
@@ -567,8 +661,8 @@ static void SendTo(Interface* self) {
567 661
568 // TODO(Subv): Increment the sequence number after each sent packet. 662 // TODO(Subv): Increment the sequence number after each sent packet.
569 u16 sequence_number = 0; 663 u16 sequence_number = 0;
570 std::vector<u8> data_payload = GenerateDataPayload( 664 std::vector<u8> data_payload =
571 data, data_channel, dest_node_id, connection_status.network_node_id, sequence_number); 665 GenerateDataPayload(data, data_channel, dest_node_id, network_node_id, sequence_number);
572 666
573 // TODO(Subv): Retrieve the MAC address of the dest_node_id and our own to encrypt 667 // TODO(Subv): Retrieve the MAC address of the dest_node_id and our own to encrypt
574 // and encapsulate the payload. 668 // and encapsulate the payload.
@@ -595,6 +689,7 @@ static void GetChannel(Interface* self) {
595 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0); 689 IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0);
596 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); 690 IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
597 691
692 std::lock_guard<std::mutex> lock(connection_status_mutex);
598 bool is_connected = connection_status.status != static_cast<u32>(NetworkStatus::NotConnected); 693 bool is_connected = connection_status.status != static_cast<u32>(NetworkStatus::NotConnected);
599 694
600 u8 channel = is_connected ? network_channel : 0; 695 u8 channel = is_connected ? network_channel : 0;
@@ -766,6 +861,7 @@ static void BeaconBroadcastCallback(u64 userdata, int cycles_late) {
766 * @param network_node_id Network Node Id of the connecting client. 861 * @param network_node_id Network Node Id of the connecting client.
767 */ 862 */
768void OnClientConnected(u16 network_node_id) { 863void OnClientConnected(u16 network_node_id) {
864 std::lock_guard<std::mutex> lock(connection_status_mutex);
769 ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost), 865 ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost),
770 "Can not accept clients if we're not hosting a network"); 866 "Can not accept clients if we're not hosting a network");
771 ASSERT_MSG(connection_status.total_nodes < connection_status.max_nodes, 867 ASSERT_MSG(connection_status.total_nodes < connection_status.max_nodes,
@@ -827,8 +923,11 @@ NWM_UDS::~NWM_UDS() {
827 connection_status_event = nullptr; 923 connection_status_event = nullptr;
828 recv_buffer_memory = nullptr; 924 recv_buffer_memory = nullptr;
829 925
830 connection_status = {}; 926 {
831 connection_status.status = static_cast<u32>(NetworkStatus::NotConnected); 927 std::lock_guard<std::mutex> lock(connection_status_mutex);
928 connection_status = {};
929 connection_status.status = static_cast<u32>(NetworkStatus::NotConnected);
930 }
832 931
833 CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); 932 CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0);
834} 933}
diff --git a/src/core/hle/service/nwm/uds_connection.cpp b/src/core/hle/service/nwm/uds_connection.cpp
index c8a76ec2a..c74f51253 100644
--- a/src/core/hle/service/nwm/uds_connection.cpp
+++ b/src/core/hle/service/nwm/uds_connection.cpp
@@ -75,5 +75,14 @@ std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_i
75 return data; 75 return data;
76} 76}
77 77
78std::tuple<AssocStatus, u16> GetAssociationResult(const std::vector<u8>& body) {
79 AssociationResponseFrame frame;
80 memcpy(&frame, body.data(), sizeof(frame));
81
82 constexpr u16 AssociationIdMask = 0x3FFF;
83 return std::make_tuple(static_cast<AssocStatus>(frame.status_code),
84 frame.assoc_id & AssociationIdMask);
85}
86
78} // namespace NWM 87} // namespace NWM
79} // namespace Service 88} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_connection.h b/src/core/hle/service/nwm/uds_connection.h
index 73f55a4fd..a664f8471 100644
--- a/src/core/hle/service/nwm/uds_connection.h
+++ b/src/core/hle/service/nwm/uds_connection.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <tuple>
7#include <vector> 8#include <vector>
8#include "common/common_types.h" 9#include "common/common_types.h"
9#include "common/swap.h" 10#include "common/swap.h"
@@ -47,5 +48,9 @@ AuthenticationSeq GetAuthenticationSeqNumber(const std::vector<u8>& body);
47/// network id, starting at the frame body. 48/// network id, starting at the frame body.
48std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id); 49std::vector<u8> GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id);
49 50
51/// Returns a tuple of (association status, association id) from the body of an AssociationResponse
52/// frame.
53std::tuple<AssocStatus, u16> GetAssociationResult(const std::vector<u8>& body);
54
50} // namespace NWM 55} // namespace NWM
51} // namespace Service 56} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_data.cpp b/src/core/hle/service/nwm/uds_data.cpp
index 8c6742dba..0fd9b8b8c 100644
--- a/src/core/hle/service/nwm/uds_data.cpp
+++ b/src/core/hle/service/nwm/uds_data.cpp
@@ -274,5 +274,26 @@ std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16
274 return buffer; 274 return buffer;
275} 275}
276 276
277std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info) {
278 EAPoLStartPacket eapol_start{};
279 eapol_start.association_id = association_id;
280 eapol_start.friend_code_seed = node_info.friend_code_seed;
281
282 for (int i = 0; i < node_info.username.size(); ++i)
283 eapol_start.username[i] = node_info.username[i];
284
285 // Note: The network_node_id and unknown bytes seem to be uninitialized in the NWM module.
286 // TODO(B3N30): The last 8 bytes seem to have a fixed value of 07 88 15 00 04 e9 13 00 in
287 // EAPoL-Start packets from different 3DSs to the same host during a Super Smash Bros. 4 game.
288 // Find out what that means.
289
290 std::vector<u8> eapol_buffer(sizeof(EAPoLStartPacket));
291 std::memcpy(eapol_buffer.data(), &eapol_start, sizeof(eapol_start));
292
293 std::vector<u8> buffer = GenerateLLCHeader(EtherType::EAPoL);
294 buffer.insert(buffer.end(), eapol_buffer.begin(), eapol_buffer.end());
295 return buffer;
296}
297
277} // namespace NWM 298} // namespace NWM
278} // namespace Service 299} // namespace Service
diff --git a/src/core/hle/service/nwm/uds_data.h b/src/core/hle/service/nwm/uds_data.h
index a23520a41..76e8f546b 100644
--- a/src/core/hle/service/nwm/uds_data.h
+++ b/src/core/hle/service/nwm/uds_data.h
@@ -67,6 +67,27 @@ struct DataFrameCryptoCTR {
67 67
68static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); 68static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size");
69 69
70constexpr u16 EAPoLStartMagic = 0x201;
71
72/*
73 * Nintendo EAPoLStartPacket, is used to initaliaze a connection between client and host
74 */
75struct EAPoLStartPacket {
76 u16_be magic = EAPoLStartMagic;
77 u16_be association_id;
78 // This value is hardcoded to 1 in the NWM module.
79 u16_be unknown = 1;
80 INSERT_PADDING_BYTES(2);
81
82 u64_be friend_code_seed;
83 std::array<u16_be, 10> username;
84 INSERT_PADDING_BYTES(4);
85 u16_be network_node_id;
86 INSERT_PADDING_BYTES(6);
87};
88
89static_assert(sizeof(EAPoLStartPacket) == 0x30, "EAPoLStartPacket has the wrong size");
90
70/** 91/**
71 * Generates an unencrypted 802.11 data payload. 92 * Generates an unencrypted 802.11 data payload.
72 * @returns The generated frame payload. 93 * @returns The generated frame payload.
@@ -74,5 +95,12 @@ static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wron
74std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node, 95std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node,
75 u16 src_node, u16 sequence_number); 96 u16 src_node, u16 sequence_number);
76 97
98/*
99 * Generates an unencrypted 802.11 data frame body with the EAPoL-Start format for UDS
100 * communication.
101 * @returns The generated frame body.
102 */
103std::vector<u8> GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info);
104
77} // namespace NWM 105} // namespace NWM
78} // namespace Service 106} // namespace Service