summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/citra/CMakeLists.txt4
-rw-r--r--src/citra_qt/CMakeLists.txt2
-rw-r--r--src/common/logging/backend.cpp1
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/core/hle/function_wrappers.h7
-rw-r--r--src/core/hle/service/soc_u.cpp721
-rw-r--r--src/core/hle/service/soc_u.h1
-rw-r--r--src/core/hle/svc.cpp14
8 files changed, 726 insertions, 25 deletions
diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt
index 94f8d13c7..bbb3374f2 100644
--- a/src/citra/CMakeLists.txt
+++ b/src/citra/CMakeLists.txt
@@ -24,10 +24,10 @@ endif()
24if (APPLE) 24if (APPLE)
25 target_link_libraries(citra iconv ${COREFOUNDATION_LIBRARY}) 25 target_link_libraries(citra iconv ${COREFOUNDATION_LIBRARY})
26elseif (WIN32) 26elseif (WIN32)
27 target_link_libraries(citra winmm) 27 target_link_libraries(citra winmm wsock32 ws2_32)
28 if (MINGW) # GCC does not support codecvt, so use iconv instead 28 if (MINGW) # GCC does not support codecvt, so use iconv instead
29 target_link_libraries(citra iconv) 29 target_link_libraries(citra iconv)
30 endif() 30 endif()
31else() # Unix 31else() # Unix
32 target_link_libraries(citra rt) 32 target_link_libraries(citra rt)
33endif() 33endif()
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index 356ec754f..8a511e02f 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -68,7 +68,7 @@ endif()
68if (APPLE) 68if (APPLE)
69 target_link_libraries(citra-qt iconv ${COREFOUNDATION_LIBRARY}) 69 target_link_libraries(citra-qt iconv ${COREFOUNDATION_LIBRARY})
70elseif (WIN32) 70elseif (WIN32)
71 target_link_libraries(citra-qt winmm) 71 target_link_libraries(citra-qt winmm wsock32 ws2_32)
72else() # Unix 72else() # Unix
73 target_link_libraries(citra-qt rt) 73 target_link_libraries(citra-qt rt)
74endif() 74endif()
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 816d1bb55..7ac30ad50 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -39,6 +39,7 @@ static std::shared_ptr<Logger> global_logger;
39 SUB(Service, CFG) \ 39 SUB(Service, CFG) \
40 SUB(Service, DSP) \ 40 SUB(Service, DSP) \
41 SUB(Service, HID) \ 41 SUB(Service, HID) \
42 SUB(Service, SOC) \
42 CLS(HW) \ 43 CLS(HW) \
43 SUB(HW, Memory) \ 44 SUB(HW, Memory) \
44 SUB(HW, GPU) \ 45 SUB(HW, GPU) \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index d1c391862..06b99b07a 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -59,6 +59,7 @@ enum class Class : ClassType {
59 Service_CFG, ///< The CFG (Configuration) service 59 Service_CFG, ///< The CFG (Configuration) service
60 Service_DSP, ///< The DSP (DSP control) service 60 Service_DSP, ///< The DSP (DSP control) service
61 Service_HID, ///< The HID (User input) service 61 Service_HID, ///< The HID (User input) service
62 Service_SOC, ///< The SOC (Socket) service
62 HW, ///< Low-level hardware emulation 63 HW, ///< Low-level hardware emulation
63 HW_Memory, ///< Memory-map and address translation 64 HW_Memory, ///< Memory-map and address translation
64 HW_GPU, ///< GPU control emulation 65 HW_GPU, ///< GPU control emulation
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index 3259ce9eb..0f822f84b 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -128,6 +128,13 @@ template<s32 func(s32*, u32, s32)> void Wrap() {
128 FuncReturn(retval); 128 FuncReturn(retval);
129} 129}
130 130
131template<s32 func(u32*, u32, u32, u32, u32)> void Wrap() {
132 u32 param_1 = 0;
133 u32 retval = func(&param_1, PARAM(1), PARAM(2), PARAM(3), PARAM(4));
134 Core::g_app_core->SetReg(1, param_1);
135 FuncReturn(retval);
136}
137
131//////////////////////////////////////////////////////////////////////////////////////////////////// 138////////////////////////////////////////////////////////////////////////////////////////////////////
132// Function wrappers that return type u32 139// Function wrappers that return type u32
133 140
diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index ef4f9829d..9fbf18b26 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -2,40 +2,712 @@
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 "common/platform.h"
6
7#if EMU_PLATFORM == PLATFORM_WINDOWS
8#include <winsock2.h>
9#include <ws2tcpip.h>
10#else
11#include <sys/socket.h>
12#include <netinet/in.h>
13#include <netdb.h>
14#include <arpa/inet.h>
15#include <fcntl.h>
16#include <poll.h>
17#endif
18
5#include "common/log.h" 19#include "common/log.h"
20#include "common/scope_exit.h"
6#include "core/hle/hle.h" 21#include "core/hle/hle.h"
7#include "core/hle/service/soc_u.h" 22#include "core/hle/service/soc_u.h"
23#include <unordered_map>
24
25#if EMU_PLATFORM == PLATFORM_WINDOWS
26# define WSAEAGAIN WSAEWOULDBLOCK
27# define WSAEMULTIHOP -1 // Invalid dummy value
28# define ERRNO(x) WSA##x
29# define GET_ERRNO WSAGetLastError()
30# define poll(x, y, z) WSAPoll(x, y, z);
31#else
32# define ERRNO(x) x
33# define GET_ERRNO errno
34# define closesocket(x) close(x)
35#endif
36
37static const s32 SOCKET_ERROR_VALUE = -1;
8 38
9//////////////////////////////////////////////////////////////////////////////////////////////////// 39////////////////////////////////////////////////////////////////////////////////////////////////////
10// Namespace SOC_U 40// Namespace SOC_U
11 41
12namespace SOC_U { 42namespace SOC_U {
13 43
44/// Holds the translation from system network errors to 3DS network errors
45static const std::unordered_map<int, int> error_map = { {
46 { E2BIG, 1 },
47 { ERRNO(EACCES), 2 },
48 { ERRNO(EADDRINUSE), 3 },
49 { ERRNO(EADDRNOTAVAIL), 4 },
50 { ERRNO(EAFNOSUPPORT), 5 },
51 { ERRNO(EAGAIN), 6 },
52 { ERRNO(EALREADY), 7 },
53 { ERRNO(EBADF), 8 },
54 { EBADMSG, 9 },
55 { EBUSY, 10 },
56 { ECANCELED, 11 },
57 { ECHILD, 12 },
58 { ERRNO(ECONNABORTED), 13 },
59 { ERRNO(ECONNREFUSED), 14 },
60 { ERRNO(ECONNRESET), 15 },
61 { EDEADLK, 16 },
62 { ERRNO(EDESTADDRREQ), 17 },
63 { EDOM, 18 },
64 { ERRNO(EDQUOT), 19 },
65 { EEXIST, 20 },
66 { ERRNO(EFAULT), 21 },
67 { EFBIG, 22 },
68 { ERRNO(EHOSTUNREACH), 23 },
69 { EIDRM, 24 },
70 { EILSEQ, 25 },
71 { ERRNO(EINPROGRESS), 26 },
72 { ERRNO(EINTR), 27 },
73 { ERRNO(EINVAL), 28 },
74 { EIO, 29 },
75 { ERRNO(EISCONN), 30 },
76 { EISDIR, 31 },
77 { ERRNO(ELOOP), 32 },
78 { ERRNO(EMFILE), 33 },
79 { EMLINK, 34 },
80 { ERRNO(EMSGSIZE), 35 },
81 { ERRNO(EMULTIHOP), 36 },
82 { ERRNO(ENAMETOOLONG), 37 },
83 { ERRNO(ENETDOWN), 38 },
84 { ERRNO(ENETRESET), 39 },
85 { ERRNO(ENETUNREACH), 40 },
86 { ENFILE, 41 },
87 { ERRNO(ENOBUFS), 42 },
88 { ENODATA, 43 },
89 { ENODEV, 44 },
90 { ENOENT, 45 },
91 { ENOEXEC, 46 },
92 { ENOLCK, 47 },
93 { ENOLINK, 48 },
94 { ENOMEM, 49 },
95 { ENOMSG, 50 },
96 { ERRNO(ENOPROTOOPT), 51 },
97 { ENOSPC, 52 },
98 { ENOSR, 53 },
99 { ENOSTR, 54 },
100 { ENOSYS, 55 },
101 { ERRNO(ENOTCONN), 56 },
102 { ENOTDIR, 57 },
103 { ERRNO(ENOTEMPTY), 58 },
104 { ERRNO(ENOTSOCK), 59 },
105 { ENOTSUP, 60 },
106 { ENOTTY, 61 },
107 { ENXIO, 62 },
108 { ERRNO(EOPNOTSUPP), 63 },
109 { EOVERFLOW, 64 },
110 { EPERM, 65 },
111 { EPIPE, 66 },
112 { EPROTO, 67 },
113 { ERRNO(EPROTONOSUPPORT), 68 },
114 { ERRNO(EPROTOTYPE), 69 },
115 { ERANGE, 70 },
116 { EROFS, 71 },
117 { ESPIPE, 72 },
118 { ESRCH, 73 },
119 { ERRNO(ESTALE), 74 },
120 { ETIME, 75 },
121 { ERRNO(ETIMEDOUT), 76 }
122}};
123
124/// Converts a network error from platform-specific to 3ds-specific
125static int TranslateError(int error) {
126 auto found = error_map.find(error);
127 if (found != error_map.end())
128 return -found->second;
129
130 return error;
131}
132
133/// Holds information about a particular socket
134struct SocketHolder {
135 u32 socket_fd; ///< The socket descriptor
136 bool blocking; ///< Whether the socket is blocking or not, it is only read on Windows.
137};
138
139/// Structure to represent the 3ds' pollfd structure, which is different than most implementations
140struct CTRPollFD {
141 u32 fd; ///< Socket handle
142
143 union Events {
144 u32 hex; ///< The complete value formed by the flags
145 BitField<0, 1, u32> pollin;
146 BitField<1, 1, u32> pollpri;
147 BitField<2, 1, u32> pollhup;
148 BitField<3, 1, u32> pollerr;
149 BitField<4, 1, u32> pollout;
150 BitField<5, 1, u32> pollnval;
151
152 Events& operator=(const Events& other) {
153 hex = other.hex;
154 return *this;
155 }
156
157 /// Translates the resulting events of a Poll operation from platform-specific to 3ds specific
158 static Events TranslateTo3DS(u32 input_event) {
159 Events ev = {};
160 if (input_event & POLLIN)
161 ev.pollin = 1;
162 if (input_event & POLLPRI)
163 ev.pollpri = 1;
164 if (input_event & POLLHUP)
165 ev.pollhup = 1;
166 if (input_event & POLLERR)
167 ev.pollerr = 1;
168 if (input_event & POLLOUT)
169 ev.pollout = 1;
170 if (input_event & POLLNVAL)
171 ev.pollnval = 1;
172 return ev;
173 }
174
175 /// Translates the resulting events of a Poll operation from 3ds specific to platform specific
176 static u32 TranslateToPlatform(Events input_event) {
177 u32 ret = 0;
178 if (input_event.pollin)
179 ret |= POLLIN;
180 if (input_event.pollpri)
181 ret |= POLLPRI;
182 if (input_event.pollhup)
183 ret |= POLLHUP;
184 if (input_event.pollerr)
185 ret |= POLLERR;
186 if (input_event.pollout)
187 ret |= POLLOUT;
188 if (input_event.pollnval)
189 ret |= POLLNVAL;
190 return ret;
191 }
192 };
193 Events events; ///< Events to poll for (input)
194 Events revents; ///< Events received (output)
195
196 /// Converts a platform-specific pollfd to a 3ds specific structure
197 static CTRPollFD FromPlatform(pollfd const& fd) {
198 CTRPollFD result;
199 result.events.hex = Events::TranslateTo3DS(fd.events).hex;
200 result.revents.hex = Events::TranslateTo3DS(fd.revents).hex;
201 result.fd = static_cast<u32>(fd.fd);
202 return result;
203 }
204
205 /// Converts a 3ds specific pollfd to a platform-specific structure
206 static pollfd ToPlatform(CTRPollFD const& fd) {
207 pollfd result;
208 result.events = Events::TranslateToPlatform(fd.events);
209 result.revents = Events::TranslateToPlatform(fd.revents);
210 result.fd = fd.fd;
211 return result;
212 }
213};
214
215/// Union to represent the 3ds' sockaddr structure
216union CTRSockAddr {
217 /// Structure to represent a raw sockaddr
218 struct {
219 u8 len; ///< The length of the entire structure, only the set fields count
220 u8 sa_family; ///< The address family of the sockaddr
221 u8 sa_data[0x1A]; ///< The extra data, this varies, depending on the address family
222 } raw;
223
224 /// Structure to represent the 3ds' sockaddr_in structure
225 struct CTRSockAddrIn {
226 u8 len; ///< The length of the entire structure
227 u8 sin_family; ///< The address family of the sockaddr_in
228 u16 sin_port; ///< The port associated with this sockaddr_in
229 u32 sin_addr; ///< The actual address of the sockaddr_in
230 } in;
231
232 /// Convert a 3DS CTRSockAddr to a platform-specific sockaddr
233 static sockaddr ToPlatform(CTRSockAddr const& ctr_addr) {
234 sockaddr result;
235 result.sa_family = ctr_addr.raw.sa_family;
236 memset(result.sa_data, 0, sizeof(result.sa_data));
237
238 // We can not guarantee ABI compatibility between platforms so we copy the fields manually
239 switch (result.sa_family) {
240 case AF_INET:
241 {
242 sockaddr_in* result_in = reinterpret_cast<sockaddr_in*>(&result);
243 result_in->sin_port = ctr_addr.in.sin_port;
244 result_in->sin_addr.s_addr = ctr_addr.in.sin_addr;
245 memset(result_in->sin_zero, 0, sizeof(result_in->sin_zero));
246 break;
247 }
248 default:
249 _dbg_assert_msg_(Service_SOC, false, "Unhandled address family (sa_family) in CTRSockAddr::ToPlatform");
250 break;
251 }
252 return result;
253 }
254
255 /// Convert a platform-specific sockaddr to a 3DS CTRSockAddr
256 static CTRSockAddr FromPlatform(sockaddr const& addr) {
257 CTRSockAddr result;
258 result.raw.sa_family = static_cast<u8>(addr.sa_family);
259 // We can not guarantee ABI compatibility between platforms so we copy the fields manually
260 switch (result.raw.sa_family) {
261 case AF_INET:
262 {
263 sockaddr_in const* addr_in = reinterpret_cast<sockaddr_in const*>(&addr);
264 result.raw.len = sizeof(CTRSockAddrIn);
265 result.in.sin_port = addr_in->sin_port;
266 result.in.sin_addr = addr_in->sin_addr.s_addr;
267 break;
268 }
269 default:
270 _dbg_assert_msg_(Service_SOC, false, "Unhandled address family (sa_family) in CTRSockAddr::ToPlatform");
271 break;
272 }
273 return result;
274 }
275};
276
277/// Holds info about the currently open sockets
278static std::unordered_map<u32, SocketHolder> open_sockets;
279
280/// Close all open sockets
281static void CleanupSockets() {
282 for (auto sock : open_sockets)
283 closesocket(sock.second.socket_fd);
284 open_sockets.clear();
285}
286
287static void Socket(Service::Interface* self) {
288 u32* cmd_buffer = Kernel::GetCommandBuffer();
289 u32 domain = cmd_buffer[1]; // Address family
290 u32 type = cmd_buffer[2];
291 u32 protocol = cmd_buffer[3];
292
293 // Only 0 is allowed according to 3dbrew, using 0 will let the OS decide which protocol to use
294 if (protocol != 0) {
295 cmd_buffer[1] = UnimplementedFunction(ErrorModule::SOC).raw; // TODO(Subv): Correct error code
296 return;
297 }
298
299 if (domain != AF_INET) {
300 cmd_buffer[1] = UnimplementedFunction(ErrorModule::SOC).raw; // TODO(Subv): Correct error code
301 return;
302 }
303
304 if (type != SOCK_DGRAM && type != SOCK_STREAM) {
305 cmd_buffer[1] = UnimplementedFunction(ErrorModule::SOC).raw; // TODO(Subv): Correct error code
306 return;
307 }
308
309 u32 socket_handle = static_cast<u32>(::socket(domain, type, protocol));
310
311 if (socket_handle != SOCKET_ERROR_VALUE)
312 open_sockets[socket_handle] = { socket_handle, true };
313
314 int result = 0;
315 if (socket_handle == SOCKET_ERROR_VALUE)
316 result = TranslateError(GET_ERRNO);
317
318 cmd_buffer[1] = result;
319 cmd_buffer[2] = socket_handle;
320}
321
322static void Bind(Service::Interface* self) {
323 u32* cmd_buffer = Kernel::GetCommandBuffer();
324 u32 socket_handle = cmd_buffer[1];
325 u32 len = cmd_buffer[2];
326 CTRSockAddr* ctr_sock_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[6]));
327
328 if (ctr_sock_addr == nullptr) {
329 cmd_buffer[1] = -1; // TODO(Subv): Correct code
330 return;
331 }
332
333 sockaddr sock_addr = CTRSockAddr::ToPlatform(*ctr_sock_addr);
334
335 int res = ::bind(socket_handle, &sock_addr, std::max<u32>(sizeof(sock_addr), len));
336
337 int result = 0;
338 if (res != 0)
339 result = TranslateError(GET_ERRNO);
340
341 cmd_buffer[2] = res;
342 cmd_buffer[1] = result;
343}
344
345static void Fcntl(Service::Interface* self) {
346 u32* cmd_buffer = Kernel::GetCommandBuffer();
347 u32 socket_handle = cmd_buffer[1];
348 u32 ctr_cmd = cmd_buffer[2];
349 u32 ctr_arg = cmd_buffer[3];
350
351 int result = 0;
352 u32 posix_ret = 0; // TODO: Check what hardware returns for F_SETFL (unspecified by POSIX)
353 SCOPE_EXIT({
354 cmd_buffer[1] = result;
355 cmd_buffer[2] = posix_ret;
356 });
357
358 if (ctr_cmd == 3) { // F_GETFL
359#if EMU_PLATFORM == PLATFORM_WINDOWS
360 posix_ret = 0;
361 auto iter = open_sockets.find(socket_handle);
362 if (iter != open_sockets.end() && iter->second.blocking == false)
363 posix_ret |= 4; // O_NONBLOCK
364#else
365 int ret = ::fcntl(socket_handle, F_GETFL, 0);
366 if (ret == SOCKET_ERROR_VALUE) {
367 result = TranslateError(GET_ERRNO);
368 posix_ret = -1;
369 return;
370 }
371 posix_ret = 0;
372 if (ret & O_NONBLOCK)
373 posix_ret |= 4; // O_NONBLOCK
374#endif
375 } else if (ctr_cmd == 4) { // F_SETFL
376#if EMU_PLATFORM == PLATFORM_WINDOWS
377 unsigned long tmp = (ctr_arg & 4 /* O_NONBLOCK */) ? 1 : 0;
378 int ret = ioctlsocket(socket_handle, FIONBIO, &tmp);
379 if (ret == SOCKET_ERROR_VALUE) {
380 result = TranslateError(GET_ERRNO);
381 posix_ret = -1;
382 return;
383 }
384 auto iter = open_sockets.find(socket_handle);
385 if (iter != open_sockets.end())
386 iter->second.blocking = (tmp == 0);
387#else
388 int flags = ::fcntl(socket_handle, F_GETFL, 0);
389 if (flags == SOCKET_ERROR_VALUE) {
390 result = TranslateError(GET_ERRNO);
391 posix_ret = -1;
392 return;
393 }
394
395 flags &= ~O_NONBLOCK;
396 if (ctr_arg & 4) // O_NONBLOCK
397 flags |= O_NONBLOCK;
398
399 int ret = ::fcntl(socket_handle, F_SETFL, flags);
400 if (ret == SOCKET_ERROR_VALUE) {
401 result = TranslateError(GET_ERRNO);
402 posix_ret = -1;
403 return;
404 }
405#endif
406 } else {
407 LOG_ERROR(Service_SOC, "Unsupported command (%d) in fcntl call");
408 result = TranslateError(EINVAL); // TODO: Find the correct error
409 posix_ret = -1;
410 return;
411 }
412}
413
414static void Listen(Service::Interface* self) {
415 u32* cmd_buffer = Kernel::GetCommandBuffer();
416 u32 socket_handle = cmd_buffer[1];
417 u32 backlog = cmd_buffer[2];
418
419 int ret = ::listen(socket_handle, backlog);
420 int result = 0;
421 if (ret != 0)
422 result = TranslateError(GET_ERRNO);
423
424 cmd_buffer[2] = ret;
425 cmd_buffer[1] = result;
426}
427
428static void Accept(Service::Interface* self) {
429 // TODO(Subv): Calling this function on a blocking socket will block the emu thread,
430 // preventing graceful shutdown when closing the emulator, this can be fixed by always
431 // performing nonblocking operations and spinlock until the data is available
432 u32* cmd_buffer = Kernel::GetCommandBuffer();
433 u32 socket_handle = cmd_buffer[1];
434 socklen_t max_addr_len = static_cast<socklen_t>(cmd_buffer[2]);
435 sockaddr addr;
436 socklen_t addr_len = sizeof(addr);
437 u32 ret = static_cast<u32>(::accept(socket_handle, &addr, &addr_len));
438
439 if (ret != SOCKET_ERROR_VALUE)
440 open_sockets[ret] = { ret, true };
441
442 int result = 0;
443 if (ret == SOCKET_ERROR_VALUE) {
444 result = TranslateError(GET_ERRNO);
445 } else {
446 CTRSockAddr ctr_addr = CTRSockAddr::FromPlatform(addr);
447 Memory::WriteBlock(cmd_buffer[0x104 >> 2], (const u8*)&ctr_addr, max_addr_len);
448 }
449
450 cmd_buffer[2] = ret;
451 cmd_buffer[1] = result;
452}
453
454static void GetHostId(Service::Interface* self) {
455 u32* cmd_buffer = Kernel::GetCommandBuffer();
456
457 char name[128];
458 gethostname(name, sizeof(name));
459 hostent* host = gethostbyname(name);
460 in_addr* addr = reinterpret_cast<in_addr*>(host->h_addr);
461
462 cmd_buffer[2] = addr->s_addr;
463 cmd_buffer[1] = 0;
464}
465
466static void Close(Service::Interface* self) {
467 u32* cmd_buffer = Kernel::GetCommandBuffer();
468 u32 socket_handle = cmd_buffer[1];
469
470 int ret = 0;
471 open_sockets.erase(socket_handle);
472
473 ret = closesocket(socket_handle);
474
475 int result = 0;
476 if (ret != 0)
477 result = TranslateError(GET_ERRNO);
478
479 cmd_buffer[2] = ret;
480 cmd_buffer[1] = result;
481}
482
483static void SendTo(Service::Interface* self) {
484 u32* cmd_buffer = Kernel::GetCommandBuffer();
485 u32 socket_handle = cmd_buffer[1];
486 u32 len = cmd_buffer[2];
487 u32 flags = cmd_buffer[3];
488 u32 addr_len = cmd_buffer[4];
489
490 u8* input_buff = Memory::GetPointer(cmd_buffer[8]);
491 CTRSockAddr* ctr_dest_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[10]));
492
493 if (ctr_dest_addr == nullptr) {
494 cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
495 return;
496 }
497
498 int ret = -1;
499 if (addr_len > 0) {
500 sockaddr dest_addr = CTRSockAddr::ToPlatform(*ctr_dest_addr);
501 ret = ::sendto(socket_handle, (const char*)input_buff, len, flags, &dest_addr, sizeof(dest_addr));
502 } else {
503 ret = ::sendto(socket_handle, (const char*)input_buff, len, flags, nullptr, 0);
504 }
505
506 int result = 0;
507 if (ret == SOCKET_ERROR_VALUE)
508 result = TranslateError(GET_ERRNO);
509
510 cmd_buffer[2] = ret;
511 cmd_buffer[1] = result;
512}
513
514static void RecvFrom(Service::Interface* self) {
515 // TODO(Subv): Calling this function on a blocking socket will block the emu thread,
516 // preventing graceful shutdown when closing the emulator, this can be fixed by always
517 // performing nonblocking operations and spinlock until the data is available
518 u32* cmd_buffer = Kernel::GetCommandBuffer();
519 u32 socket_handle = cmd_buffer[1];
520 u32 len = cmd_buffer[2];
521 u32 flags = cmd_buffer[3];
522 socklen_t addr_len = static_cast<socklen_t>(cmd_buffer[4]);
523
524 u8* output_buff = Memory::GetPointer(cmd_buffer[0x104 >> 2]);
525 sockaddr src_addr;
526 socklen_t src_addr_len = sizeof(src_addr);
527 int ret = ::recvfrom(socket_handle, (char*)output_buff, len, flags, &src_addr, &src_addr_len);
528
529 if (cmd_buffer[0x1A0 >> 2] != 0) {
530 CTRSockAddr* ctr_src_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x1A0 >> 2]));
531 *ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
532 }
533
534 int result = 0;
535 int total_received = ret;
536 if (ret == SOCKET_ERROR_VALUE) {
537 result = TranslateError(GET_ERRNO);
538 total_received = 0;
539 }
540
541 cmd_buffer[1] = result;
542 cmd_buffer[2] = ret;
543 cmd_buffer[3] = total_received;
544}
545
546static void Poll(Service::Interface* self) {
547 u32* cmd_buffer = Kernel::GetCommandBuffer();
548 u32 nfds = cmd_buffer[1];
549 int timeout = cmd_buffer[2];
550 CTRPollFD* input_fds = reinterpret_cast<CTRPollFD*>(Memory::GetPointer(cmd_buffer[6]));
551 CTRPollFD* output_fds = reinterpret_cast<CTRPollFD*>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
552
553 // The 3ds_pollfd and the pollfd structures may be different (Windows/Linux have different sizes)
554 // so we have to copy the data
555 pollfd* platform_pollfd = new pollfd[nfds];
556 for (unsigned current_fds = 0; current_fds < nfds; ++current_fds)
557 platform_pollfd[current_fds] = CTRPollFD::ToPlatform(input_fds[current_fds]);
558
559 int ret = ::poll(platform_pollfd, nfds, timeout);
560
561 // Now update the output pollfd structure
562 for (unsigned current_fds = 0; current_fds < nfds; ++current_fds)
563 output_fds[current_fds] = CTRPollFD::FromPlatform(platform_pollfd[current_fds]);
564
565 delete[] platform_pollfd;
566
567 int result = 0;
568 if (ret == SOCKET_ERROR_VALUE)
569 result = TranslateError(GET_ERRNO);
570
571 cmd_buffer[1] = result;
572 cmd_buffer[2] = ret;
573}
574
575static void GetSockName(Service::Interface* self) {
576 u32* cmd_buffer = Kernel::GetCommandBuffer();
577 u32 socket_handle = cmd_buffer[1];
578 socklen_t ctr_len = cmd_buffer[2];
579
580 CTRSockAddr* ctr_dest_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
581
582 sockaddr dest_addr;
583 socklen_t dest_addr_len = sizeof(dest_addr);
584 int ret = ::getsockname(socket_handle, &dest_addr, &dest_addr_len);
585
586 if (ctr_dest_addr != nullptr) {
587 *ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr);
588 } else {
589 cmd_buffer[1] = -1; // TODO(Subv): Verify error
590 return;
591 }
592
593 int result = 0;
594 if (ret != 0)
595 result = TranslateError(GET_ERRNO);
596
597 cmd_buffer[2] = ret;
598 cmd_buffer[1] = result;
599}
600
601static void Shutdown(Service::Interface* self) {
602 u32* cmd_buffer = Kernel::GetCommandBuffer();
603 u32 socket_handle = cmd_buffer[1];
604 int how = cmd_buffer[2];
605
606 int ret = ::shutdown(socket_handle, how);
607 int result = 0;
608 if (ret != 0)
609 result = TranslateError(GET_ERRNO);
610 cmd_buffer[2] = ret;
611 cmd_buffer[1] = result;
612}
613
614static void GetPeerName(Service::Interface* self) {
615 u32* cmd_buffer = Kernel::GetCommandBuffer();
616 u32 socket_handle = cmd_buffer[1];
617 socklen_t len = cmd_buffer[2];
618
619 CTRSockAddr* ctr_dest_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
620
621 sockaddr dest_addr;
622 socklen_t dest_addr_len = sizeof(dest_addr);
623 int ret = ::getpeername(socket_handle, &dest_addr, &dest_addr_len);
624
625 if (ctr_dest_addr != nullptr) {
626 *ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr);
627 } else {
628 cmd_buffer[1] = -1;
629 return;
630 }
631
632 int result = 0;
633 if (ret != 0)
634 result = TranslateError(GET_ERRNO);
635
636 cmd_buffer[2] = ret;
637 cmd_buffer[1] = result;
638}
639
640static void Connect(Service::Interface* self) {
641 // TODO(Subv): Calling this function on a blocking socket will block the emu thread,
642 // preventing graceful shutdown when closing the emulator, this can be fixed by always
643 // performing nonblocking operations and spinlock until the data is available
644 u32* cmd_buffer = Kernel::GetCommandBuffer();
645 u32 socket_handle = cmd_buffer[1];
646 socklen_t len = cmd_buffer[2];
647
648 CTRSockAddr* ctr_input_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[6]));
649 if (ctr_input_addr == nullptr) {
650 cmd_buffer[1] = -1; // TODO(Subv): Verify error
651 return;
652 }
653
654 sockaddr input_addr = CTRSockAddr::ToPlatform(*ctr_input_addr);
655 int ret = ::connect(socket_handle, &input_addr, sizeof(input_addr));
656 int result = 0;
657 if (ret != 0)
658 result = TranslateError(GET_ERRNO);
659 cmd_buffer[2] = ret;
660 cmd_buffer[1] = result;
661}
662
663static void InitializeSockets(Service::Interface* self) {
664 // TODO(Subv): Implement
665#if EMU_PLATFORM == PLATFORM_WINDOWS
666 WSADATA data;
667 WSAStartup(MAKEWORD(2, 2), &data);
668#endif
669
670 u32* cmd_buffer = Kernel::GetCommandBuffer();
671 cmd_buffer[1] = 0;
672}
673
674static void ShutdownSockets(Service::Interface* self) {
675 // TODO(Subv): Implement
676 CleanupSockets();
677
678#if EMU_PLATFORM == PLATFORM_WINDOWS
679 WSACleanup();
680#endif
681
682 u32* cmd_buffer = Kernel::GetCommandBuffer();
683 cmd_buffer[1] = 0;
684}
685
14const Interface::FunctionInfo FunctionTable[] = { 686const Interface::FunctionInfo FunctionTable[] = {
15 {0x00010044, nullptr, "InitializeSockets"}, 687 {0x00010044, InitializeSockets, "InitializeSockets"},
16 {0x000200C2, nullptr, "socket"}, 688 {0x000200C2, Socket, "Socket"},
17 {0x00030082, nullptr, "listen"}, 689 {0x00030082, Listen, "Listen"},
18 {0x00040082, nullptr, "accept"}, 690 {0x00040082, Accept, "Accept"},
19 {0x00050084, nullptr, "bind"}, 691 {0x00050084, Bind, "Bind"},
20 {0x00060084, nullptr, "connect"}, 692 {0x00060084, Connect, "Connect"},
21 {0x00070104, nullptr, "recvfrom_other"}, 693 {0x00070104, nullptr, "recvfrom_other"},
22 {0x00080102, nullptr, "recvfrom"}, 694 {0x00080102, RecvFrom, "RecvFrom"},
23 {0x00090106, nullptr, "sendto_other"}, 695 {0x00090106, nullptr, "sendto_other"},
24 {0x000A0106, nullptr, "sendto"}, 696 {0x000A0106, SendTo, "SendTo"},
25 {0x000B0042, nullptr, "close"}, 697 {0x000B0042, Close, "Close"},
26 {0x000C0082, nullptr, "shutdown"}, 698 {0x000C0082, Shutdown, "Shutdown"},
27 {0x000D0082, nullptr, "gethostbyname"}, 699 {0x000D0082, nullptr, "GetHostByName"},
28 {0x000E00C2, nullptr, "gethostbyaddr"}, 700 {0x000E00C2, nullptr, "GetHostByAddr"},
29 {0x000F0106, nullptr, "unknown_resolve_ip"}, 701 {0x000F0106, nullptr, "unknown_resolve_ip"},
30 {0x00110102, nullptr, "getsockopt"}, 702 {0x00110102, nullptr, "GetSockOpt"},
31 {0x00120104, nullptr, "setsockopt"}, 703 {0x00120104, nullptr, "SetSockOpt"},
32 {0x001300C2, nullptr, "fcntl"}, 704 {0x001300C2, Fcntl, "Fcntl"},
33 {0x00140084, nullptr, "poll"}, 705 {0x00140084, Poll, "Poll"},
34 {0x00150042, nullptr, "sockatmark"}, 706 {0x00150042, nullptr, "SockAtMark"},
35 {0x00160000, nullptr, "gethostid"}, 707 {0x00160000, GetHostId, "GetHostId"},
36 {0x00170082, nullptr, "getsockname"}, 708 {0x00170082, GetSockName, "GetSockName"},
37 {0x00180082, nullptr, "getpeername"}, 709 {0x00180082, GetPeerName, "GetPeerName"},
38 {0x00190000, nullptr, "ShutdownSockets"}, 710 {0x00190000, ShutdownSockets, "ShutdownSockets"},
39 {0x001A00C0, nullptr, "GetNetworkOpt"}, 711 {0x001A00C0, nullptr, "GetNetworkOpt"},
40 {0x001B0040, nullptr, "ICMPSocket"}, 712 {0x001B0040, nullptr, "ICMPSocket"},
41 {0x001C0104, nullptr, "ICMPPing"}, 713 {0x001C0104, nullptr, "ICMPPing"},
@@ -52,4 +724,11 @@ Interface::Interface() {
52 Register(FunctionTable, ARRAY_SIZE(FunctionTable)); 724 Register(FunctionTable, ARRAY_SIZE(FunctionTable));
53} 725}
54 726
727Interface::~Interface() {
728 CleanupSockets();
729#if EMU_PLATFORM == PLATFORM_WINDOWS
730 WSACleanup();
731#endif
732}
733
55} // namespace 734} // namespace
diff --git a/src/core/hle/service/soc_u.h b/src/core/hle/service/soc_u.h
index 2edf3b482..483b3111b 100644
--- a/src/core/hle/service/soc_u.h
+++ b/src/core/hle/service/soc_u.h
@@ -14,6 +14,7 @@ namespace SOC_U {
14class Interface : public Service::Interface { 14class Interface : public Service::Interface {
15public: 15public:
16 Interface(); 16 Interface();
17 ~Interface();
17 18
18 std::string GetPortName() const override { 19 std::string GetPortName() const override {
19 return "soc:U"; 20 return "soc:U";
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 25944fc68..c25409a9f 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -352,6 +352,18 @@ static s64 GetSystemTick() {
352 return (s64)Core::g_app_core->GetTicks(); 352 return (s64)Core::g_app_core->GetTicks();
353} 353}
354 354
355/// Creates a memory block at the specified address with the specified permissions and size
356static Result CreateMemoryBlock(Handle* memblock, u32 addr, u32 size, u32 my_permission,
357 u32 other_permission) {
358
359 // TODO(Subv): Implement this function
360
361 Handle shared_memory = Kernel::CreateSharedMemory();
362 *memblock = shared_memory;
363 LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%08X", addr);
364 return 0;
365}
366
355const HLE::FunctionDef SVC_Table[] = { 367const HLE::FunctionDef SVC_Table[] = {
356 {0x00, nullptr, "Unknown"}, 368 {0x00, nullptr, "Unknown"},
357 {0x01, HLE::Wrap<ControlMemory>, "ControlMemory"}, 369 {0x01, HLE::Wrap<ControlMemory>, "ControlMemory"},
@@ -383,7 +395,7 @@ const HLE::FunctionDef SVC_Table[] = {
383 {0x1B, nullptr, "SetTimer"}, 395 {0x1B, nullptr, "SetTimer"},
384 {0x1C, nullptr, "CancelTimer"}, 396 {0x1C, nullptr, "CancelTimer"},
385 {0x1D, nullptr, "ClearTimer"}, 397 {0x1D, nullptr, "ClearTimer"},
386 {0x1E, nullptr, "CreateMemoryBlock"}, 398 {0x1E, HLE::Wrap<CreateMemoryBlock>, "CreateMemoryBlock"},
387 {0x1F, HLE::Wrap<MapMemoryBlock>, "MapMemoryBlock"}, 399 {0x1F, HLE::Wrap<MapMemoryBlock>, "MapMemoryBlock"},
388 {0x20, nullptr, "UnmapMemoryBlock"}, 400 {0x20, nullptr, "UnmapMemoryBlock"},
389 {0x21, HLE::Wrap<CreateAddressArbiter>, "CreateAddressArbiter"}, 401 {0x21, HLE::Wrap<CreateAddressArbiter>, "CreateAddressArbiter"},