mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	SOC_U: Preliminary implementation of sockets.
Stubbed CreateMemoryBlock Using Berkeley sockets, and Winsock2.2 on Windows. So far ftpony creates the socket and accepts incoming connections SOC_U: Renamed functions to maintain consistency Also prevents possible scope errors / conflicts with the actual Berkeley socket functions SOCU: Close all the opened sockets when cleaning up SOCU
This commit is contained in:
		
							parent
							
								
									59bba04628
								
							
						
					
					
						commit
						97a7381d29
					
				
					 8 changed files with 726 additions and 25 deletions
				
			
		|  | @ -24,7 +24,7 @@ endif() | |||
| if (APPLE) | ||||
|     target_link_libraries(citra iconv ${COREFOUNDATION_LIBRARY}) | ||||
| elseif (WIN32) | ||||
|     target_link_libraries(citra winmm) | ||||
|     target_link_libraries(citra winmm wsock32 ws2_32) | ||||
|     if (MINGW) # GCC does not support codecvt, so use iconv instead | ||||
|         target_link_libraries(citra iconv) | ||||
|     endif() | ||||
|  |  | |||
|  | @ -68,7 +68,7 @@ endif() | |||
| if (APPLE) | ||||
|     target_link_libraries(citra-qt iconv ${COREFOUNDATION_LIBRARY}) | ||||
| elseif (WIN32) | ||||
|     target_link_libraries(citra-qt winmm) | ||||
|     target_link_libraries(citra-qt winmm wsock32 ws2_32) | ||||
| else() # Unix | ||||
|     target_link_libraries(citra-qt rt) | ||||
| endif() | ||||
|  |  | |||
|  | @ -39,6 +39,7 @@ static std::shared_ptr<Logger> global_logger; | |||
|         SUB(Service, CFG) \ | ||||
|         SUB(Service, DSP) \ | ||||
|         SUB(Service, HID) \ | ||||
|         SUB(Service, SOC) \ | ||||
|         CLS(HW) \ | ||||
|         SUB(HW, Memory) \ | ||||
|         SUB(HW, GPU) \ | ||||
|  |  | |||
|  | @ -59,6 +59,7 @@ enum class Class : ClassType { | |||
|     Service_CFG,                ///< The CFG (Configuration) service
 | ||||
|     Service_DSP,                ///< The DSP (DSP control) service
 | ||||
|     Service_HID,                ///< The HID (User input) service
 | ||||
|     Service_SOC,                ///< The SOC (Socket) service
 | ||||
|     HW,                         ///< Low-level hardware emulation
 | ||||
|     HW_Memory,                  ///< Memory-map and address translation
 | ||||
|     HW_GPU,                     ///< GPU control emulation
 | ||||
|  |  | |||
|  | @ -128,6 +128,13 @@ template<s32 func(s32*, u32, s32)> void Wrap() { | |||
|     FuncReturn(retval); | ||||
| } | ||||
| 
 | ||||
| template<s32 func(u32*, u32, u32, u32, u32)> void Wrap() { | ||||
|     u32 param_1 = 0; | ||||
|     u32 retval = func(¶m_1, PARAM(1), PARAM(2), PARAM(3), PARAM(4)); | ||||
|     Core::g_app_core->SetReg(1, param_1); | ||||
|     FuncReturn(retval); | ||||
| } | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Function wrappers that return type u32
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,40 +2,712 @@ | |||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "common/platform.h" | ||||
| 
 | ||||
| #if EMU_PLATFORM == PLATFORM_WINDOWS | ||||
| #include <winsock2.h> | ||||
| #include <ws2tcpip.h> | ||||
| #else | ||||
| #include <sys/socket.h> | ||||
| #include <netinet/in.h> | ||||
| #include <netdb.h> | ||||
| #include <arpa/inet.h> | ||||
| #include <fcntl.h> | ||||
| #include <poll.h> | ||||
| #endif | ||||
| 
 | ||||
| #include "common/log.h" | ||||
| #include "common/scope_exit.h" | ||||
| #include "core/hle/hle.h" | ||||
| #include "core/hle/service/soc_u.h" | ||||
| #include <unordered_map> | ||||
| 
 | ||||
| #if EMU_PLATFORM == PLATFORM_WINDOWS | ||||
| #    define WSAEAGAIN      WSAEWOULDBLOCK | ||||
| #    define WSAEMULTIHOP   -1 // Invalid dummy value
 | ||||
| #    define ERRNO(x)       WSA##x | ||||
| #    define GET_ERRNO      WSAGetLastError() | ||||
| #    define poll(x, y, z)  WSAPoll(x, y, z); | ||||
| #else | ||||
| #    define ERRNO(x)       x | ||||
| #    define GET_ERRNO      errno | ||||
| #    define closesocket(x) close(x) | ||||
| #endif | ||||
| 
 | ||||
| static const s32 SOCKET_ERROR_VALUE = -1; | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Namespace SOC_U
 | ||||
| 
 | ||||
| namespace SOC_U { | ||||
| 
 | ||||
| /// Holds the translation from system network errors to 3DS network errors
 | ||||
| static const std::unordered_map<int, int> error_map = { { | ||||
|     { E2BIG, 1 }, | ||||
|     { ERRNO(EACCES), 2 }, | ||||
|     { ERRNO(EADDRINUSE), 3 }, | ||||
|     { ERRNO(EADDRNOTAVAIL), 4 }, | ||||
|     { ERRNO(EAFNOSUPPORT), 5 }, | ||||
|     { ERRNO(EAGAIN), 6 }, | ||||
|     { ERRNO(EALREADY), 7 }, | ||||
|     { ERRNO(EBADF), 8 }, | ||||
|     { EBADMSG, 9 }, | ||||
|     { EBUSY, 10 }, | ||||
|     { ECANCELED, 11 }, | ||||
|     { ECHILD, 12 }, | ||||
|     { ERRNO(ECONNABORTED), 13 }, | ||||
|     { ERRNO(ECONNREFUSED), 14 }, | ||||
|     { ERRNO(ECONNRESET), 15 }, | ||||
|     { EDEADLK, 16 }, | ||||
|     { ERRNO(EDESTADDRREQ), 17 }, | ||||
|     { EDOM, 18 }, | ||||
|     { ERRNO(EDQUOT), 19 }, | ||||
|     { EEXIST, 20 }, | ||||
|     { ERRNO(EFAULT), 21 }, | ||||
|     { EFBIG, 22 }, | ||||
|     { ERRNO(EHOSTUNREACH), 23 }, | ||||
|     { EIDRM, 24 }, | ||||
|     { EILSEQ, 25 }, | ||||
|     { ERRNO(EINPROGRESS), 26 }, | ||||
|     { ERRNO(EINTR), 27 }, | ||||
|     { ERRNO(EINVAL), 28 }, | ||||
|     { EIO, 29 }, | ||||
|     { ERRNO(EISCONN), 30 }, | ||||
|     { EISDIR, 31 }, | ||||
|     { ERRNO(ELOOP), 32 }, | ||||
|     { ERRNO(EMFILE), 33 }, | ||||
|     { EMLINK, 34 }, | ||||
|     { ERRNO(EMSGSIZE), 35 }, | ||||
|     { ERRNO(EMULTIHOP), 36 }, | ||||
|     { ERRNO(ENAMETOOLONG), 37 }, | ||||
|     { ERRNO(ENETDOWN), 38 }, | ||||
|     { ERRNO(ENETRESET), 39 }, | ||||
|     { ERRNO(ENETUNREACH), 40 }, | ||||
|     { ENFILE, 41 }, | ||||
|     { ERRNO(ENOBUFS), 42 }, | ||||
|     { ENODATA, 43 }, | ||||
|     { ENODEV, 44 }, | ||||
|     { ENOENT, 45 }, | ||||
|     { ENOEXEC, 46 }, | ||||
|     { ENOLCK, 47 }, | ||||
|     { ENOLINK, 48 }, | ||||
|     { ENOMEM, 49 }, | ||||
|     { ENOMSG, 50 }, | ||||
|     { ERRNO(ENOPROTOOPT), 51 }, | ||||
|     { ENOSPC, 52 }, | ||||
|     { ENOSR, 53 }, | ||||
|     { ENOSTR, 54 }, | ||||
|     { ENOSYS, 55 }, | ||||
|     { ERRNO(ENOTCONN), 56 }, | ||||
|     { ENOTDIR, 57 }, | ||||
|     { ERRNO(ENOTEMPTY), 58 }, | ||||
|     { ERRNO(ENOTSOCK), 59 }, | ||||
|     { ENOTSUP, 60 }, | ||||
|     { ENOTTY, 61 }, | ||||
|     { ENXIO, 62 }, | ||||
|     { ERRNO(EOPNOTSUPP), 63 }, | ||||
|     { EOVERFLOW, 64 }, | ||||
|     { EPERM, 65 }, | ||||
|     { EPIPE, 66 }, | ||||
|     { EPROTO, 67 }, | ||||
|     { ERRNO(EPROTONOSUPPORT), 68 }, | ||||
|     { ERRNO(EPROTOTYPE), 69 }, | ||||
|     { ERANGE, 70 }, | ||||
|     { EROFS, 71 }, | ||||
|     { ESPIPE, 72 }, | ||||
|     { ESRCH, 73 }, | ||||
|     { ERRNO(ESTALE), 74 }, | ||||
|     { ETIME, 75 }, | ||||
|     { ERRNO(ETIMEDOUT), 76 } | ||||
| }}; | ||||
| 
 | ||||
| /// Converts a network error from platform-specific to 3ds-specific
 | ||||
| static int TranslateError(int error) { | ||||
|     auto found = error_map.find(error); | ||||
|     if (found != error_map.end()) | ||||
|         return -found->second; | ||||
|      | ||||
|     return error; | ||||
| } | ||||
| 
 | ||||
| /// Holds information about a particular socket
 | ||||
| struct SocketHolder { | ||||
|     u32 socket_fd; ///< The socket descriptor
 | ||||
|     bool blocking; ///< Whether the socket is blocking or not, it is only read on Windows.
 | ||||
| }; | ||||
| 
 | ||||
| /// Structure to represent the 3ds' pollfd structure, which is different than most implementations
 | ||||
| struct CTRPollFD { | ||||
|     u32 fd; ///< Socket handle
 | ||||
| 
 | ||||
|     union Events { | ||||
|         u32 hex; ///< The complete value formed by the flags
 | ||||
|         BitField<0, 1, u32> pollin; | ||||
|         BitField<1, 1, u32> pollpri; | ||||
|         BitField<2, 1, u32> pollhup; | ||||
|         BitField<3, 1, u32> pollerr; | ||||
|         BitField<4, 1, u32> pollout; | ||||
|         BitField<5, 1, u32> pollnval; | ||||
| 
 | ||||
|         Events& operator=(const Events& other) { | ||||
|             hex = other.hex; | ||||
|             return *this; | ||||
|         } | ||||
| 
 | ||||
|         /// Translates the resulting events of a Poll operation from platform-specific to 3ds specific
 | ||||
|         static Events TranslateTo3DS(u32 input_event) { | ||||
|             Events ev = {}; | ||||
|             if (input_event & POLLIN) | ||||
|                 ev.pollin = 1; | ||||
|             if (input_event & POLLPRI) | ||||
|                 ev.pollpri = 1; | ||||
|             if (input_event & POLLHUP) | ||||
|                 ev.pollhup = 1; | ||||
|             if (input_event & POLLERR) | ||||
|                 ev.pollerr = 1; | ||||
|             if (input_event & POLLOUT) | ||||
|                 ev.pollout = 1; | ||||
|             if (input_event & POLLNVAL) | ||||
|                 ev.pollnval = 1; | ||||
|             return ev; | ||||
|         } | ||||
| 
 | ||||
|         /// Translates the resulting events of a Poll operation from 3ds specific to platform specific
 | ||||
|         static u32 TranslateToPlatform(Events input_event) { | ||||
|             u32 ret = 0; | ||||
|             if (input_event.pollin) | ||||
|                 ret |= POLLIN; | ||||
|             if (input_event.pollpri) | ||||
|                 ret |= POLLPRI; | ||||
|             if (input_event.pollhup) | ||||
|                 ret |= POLLHUP; | ||||
|             if (input_event.pollerr) | ||||
|                 ret |= POLLERR; | ||||
|             if (input_event.pollout) | ||||
|                 ret |= POLLOUT; | ||||
|             if (input_event.pollnval) | ||||
|                 ret |= POLLNVAL; | ||||
|             return ret; | ||||
|         } | ||||
|     }; | ||||
|     Events events; ///< Events to poll for (input)
 | ||||
|     Events revents; ///< Events received (output)
 | ||||
| 
 | ||||
|     /// Converts a platform-specific pollfd to a 3ds specific structure
 | ||||
|     static CTRPollFD FromPlatform(pollfd const& fd) { | ||||
|         CTRPollFD result; | ||||
|         result.events.hex = Events::TranslateTo3DS(fd.events).hex; | ||||
|         result.revents.hex = Events::TranslateTo3DS(fd.revents).hex; | ||||
|         result.fd = static_cast<u32>(fd.fd); | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     /// Converts a 3ds specific pollfd to a platform-specific structure
 | ||||
|     static pollfd ToPlatform(CTRPollFD const& fd) { | ||||
|         pollfd result; | ||||
|         result.events = Events::TranslateToPlatform(fd.events); | ||||
|         result.revents = Events::TranslateToPlatform(fd.revents); | ||||
|         result.fd = fd.fd; | ||||
|         return result; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| /// Union to represent the 3ds' sockaddr structure
 | ||||
| union CTRSockAddr { | ||||
|     /// Structure to represent a raw sockaddr
 | ||||
|     struct { | ||||
|         u8 len; ///< The length of the entire structure, only the set fields count
 | ||||
|         u8 sa_family; ///< The address family of the sockaddr
 | ||||
|         u8 sa_data[0x1A]; ///< The extra data, this varies, depending on the address family
 | ||||
|     } raw; | ||||
| 
 | ||||
|     /// Structure to represent the 3ds' sockaddr_in structure
 | ||||
|     struct CTRSockAddrIn { | ||||
|         u8 len; ///< The length of the entire structure
 | ||||
|         u8 sin_family; ///< The address family of the sockaddr_in
 | ||||
|         u16 sin_port; ///< The port associated with this sockaddr_in
 | ||||
|         u32 sin_addr; ///< The actual address of the sockaddr_in
 | ||||
|     } in; | ||||
| 
 | ||||
|     /// Convert a 3DS CTRSockAddr to a platform-specific sockaddr
 | ||||
|     static sockaddr ToPlatform(CTRSockAddr const& ctr_addr) { | ||||
|         sockaddr result; | ||||
|         result.sa_family = ctr_addr.raw.sa_family; | ||||
|         memset(result.sa_data, 0, sizeof(result.sa_data)); | ||||
| 
 | ||||
|         // We can not guarantee ABI compatibility between platforms so we copy the fields manually
 | ||||
|         switch (result.sa_family) { | ||||
|         case AF_INET: | ||||
|         { | ||||
|             sockaddr_in* result_in = reinterpret_cast<sockaddr_in*>(&result); | ||||
|             result_in->sin_port = ctr_addr.in.sin_port; | ||||
|             result_in->sin_addr.s_addr = ctr_addr.in.sin_addr; | ||||
|             memset(result_in->sin_zero, 0, sizeof(result_in->sin_zero)); | ||||
|             break; | ||||
|         } | ||||
|         default: | ||||
|             _dbg_assert_msg_(Service_SOC, false, "Unhandled address family (sa_family) in CTRSockAddr::ToPlatform"); | ||||
|             break; | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     /// Convert a platform-specific sockaddr to a 3DS CTRSockAddr
 | ||||
|     static CTRSockAddr FromPlatform(sockaddr const& addr) { | ||||
|         CTRSockAddr result; | ||||
|         result.raw.sa_family = static_cast<u8>(addr.sa_family); | ||||
|         // We can not guarantee ABI compatibility between platforms so we copy the fields manually
 | ||||
|         switch (result.raw.sa_family) { | ||||
|         case AF_INET: | ||||
|         { | ||||
|             sockaddr_in const* addr_in = reinterpret_cast<sockaddr_in const*>(&addr); | ||||
|             result.raw.len = sizeof(CTRSockAddrIn); | ||||
|             result.in.sin_port = addr_in->sin_port; | ||||
|             result.in.sin_addr = addr_in->sin_addr.s_addr; | ||||
|             break; | ||||
|         } | ||||
|         default: | ||||
|             _dbg_assert_msg_(Service_SOC, false, "Unhandled address family (sa_family) in CTRSockAddr::ToPlatform"); | ||||
|             break; | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| /// Holds info about the currently open sockets
 | ||||
| static std::unordered_map<u32, SocketHolder> open_sockets; | ||||
| 
 | ||||
| /// Close all open sockets
 | ||||
| static void CleanupSockets() { | ||||
|     for (auto sock : open_sockets) | ||||
|         closesocket(sock.second.socket_fd); | ||||
|     open_sockets.clear(); | ||||
| } | ||||
| 
 | ||||
| static void Socket(Service::Interface* self) { | ||||
|     u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||||
|     u32 domain = cmd_buffer[1]; // Address family
 | ||||
|     u32 type = cmd_buffer[2]; | ||||
|     u32 protocol = cmd_buffer[3]; | ||||
| 
 | ||||
|     // Only 0 is allowed according to 3dbrew, using 0 will let the OS decide which protocol to use
 | ||||
|     if (protocol != 0) { | ||||
|         cmd_buffer[1] = UnimplementedFunction(ErrorModule::SOC).raw; // TODO(Subv): Correct error code
 | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (domain != AF_INET) { | ||||
|         cmd_buffer[1] = UnimplementedFunction(ErrorModule::SOC).raw; // TODO(Subv): Correct error code
 | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (type != SOCK_DGRAM && type != SOCK_STREAM) { | ||||
|         cmd_buffer[1] = UnimplementedFunction(ErrorModule::SOC).raw; // TODO(Subv): Correct error code
 | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     u32 socket_handle = static_cast<u32>(::socket(domain, type, protocol)); | ||||
| 
 | ||||
|     if (socket_handle != SOCKET_ERROR_VALUE) | ||||
|         open_sockets[socket_handle] = { socket_handle, true }; | ||||
| 
 | ||||
|     int result = 0; | ||||
|     if (socket_handle == SOCKET_ERROR_VALUE) | ||||
|         result = TranslateError(GET_ERRNO); | ||||
| 
 | ||||
|     cmd_buffer[1] = result; | ||||
|     cmd_buffer[2] = socket_handle; | ||||
| } | ||||
| 
 | ||||
| static void Bind(Service::Interface* self) { | ||||
|     u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||||
|     u32 socket_handle = cmd_buffer[1]; | ||||
|     u32 len = cmd_buffer[2]; | ||||
|     CTRSockAddr* ctr_sock_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[6])); | ||||
| 
 | ||||
|     if (ctr_sock_addr == nullptr) { | ||||
|         cmd_buffer[1] = -1; // TODO(Subv): Correct code
 | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     sockaddr sock_addr = CTRSockAddr::ToPlatform(*ctr_sock_addr); | ||||
| 
 | ||||
|     int res = ::bind(socket_handle, &sock_addr, std::max<u32>(sizeof(sock_addr), len)); | ||||
|      | ||||
|     int result = 0; | ||||
|     if (res != 0) | ||||
|         result = TranslateError(GET_ERRNO); | ||||
| 
 | ||||
|     cmd_buffer[2] = res; | ||||
|     cmd_buffer[1] = result; | ||||
| } | ||||
| 
 | ||||
| static void Fcntl(Service::Interface* self) { | ||||
|     u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||||
|     u32 socket_handle = cmd_buffer[1]; | ||||
|     u32 ctr_cmd = cmd_buffer[2]; | ||||
|     u32 ctr_arg = cmd_buffer[3]; | ||||
|   | ||||
|     int result = 0; | ||||
|     u32 posix_ret = 0; // TODO: Check what hardware returns for F_SETFL (unspecified by POSIX)
 | ||||
|     SCOPE_EXIT({ | ||||
|             cmd_buffer[1] = result; | ||||
|             cmd_buffer[2] = posix_ret; | ||||
|     }); | ||||
|   | ||||
|     if (ctr_cmd == 3) { // F_GETFL
 | ||||
| #if EMU_PLATFORM == PLATFORM_WINDOWS | ||||
|         posix_ret = 0; | ||||
|         auto iter = open_sockets.find(socket_handle); | ||||
|         if (iter != open_sockets.end() && iter->second.blocking == false) | ||||
|             posix_ret |= 4; // O_NONBLOCK
 | ||||
| #else | ||||
|         int ret = ::fcntl(socket_handle, F_GETFL, 0); | ||||
|         if (ret == SOCKET_ERROR_VALUE) { | ||||
|             result = TranslateError(GET_ERRNO); | ||||
|             posix_ret = -1; | ||||
|             return; | ||||
|         } | ||||
|         posix_ret = 0; | ||||
|         if (ret & O_NONBLOCK) | ||||
|             posix_ret |= 4; // O_NONBLOCK
 | ||||
| #endif | ||||
|     } else if (ctr_cmd == 4) { // F_SETFL
 | ||||
| #if EMU_PLATFORM == PLATFORM_WINDOWS | ||||
|         unsigned long tmp = (ctr_arg & 4 /* O_NONBLOCK */) ? 1 : 0; | ||||
|         int ret = ioctlsocket(socket_handle, FIONBIO, &tmp); | ||||
|         if (ret == SOCKET_ERROR_VALUE) { | ||||
|             result = TranslateError(GET_ERRNO); | ||||
|             posix_ret = -1; | ||||
|             return; | ||||
|         } | ||||
|         auto iter = open_sockets.find(socket_handle); | ||||
|         if (iter != open_sockets.end()) | ||||
|             iter->second.blocking = (tmp == 0); | ||||
| #else | ||||
|         int flags = ::fcntl(socket_handle, F_GETFL, 0); | ||||
|         if (flags == SOCKET_ERROR_VALUE) { | ||||
|             result = TranslateError(GET_ERRNO); | ||||
|             posix_ret = -1; | ||||
|             return; | ||||
|         } | ||||
|   | ||||
|         flags &= ~O_NONBLOCK; | ||||
|         if (ctr_arg & 4) // O_NONBLOCK
 | ||||
|             flags |= O_NONBLOCK; | ||||
|   | ||||
|         int ret = ::fcntl(socket_handle, F_SETFL, flags); | ||||
|         if (ret == SOCKET_ERROR_VALUE) { | ||||
|             result = TranslateError(GET_ERRNO); | ||||
|             posix_ret = -1; | ||||
|             return; | ||||
|         } | ||||
| #endif | ||||
|     } else { | ||||
|         LOG_ERROR(Service_SOC, "Unsupported command (%d) in fcntl call"); | ||||
|         result = TranslateError(EINVAL); // TODO: Find the correct error
 | ||||
|         posix_ret = -1; | ||||
|         return; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void Listen(Service::Interface* self) { | ||||
|     u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||||
|     u32 socket_handle = cmd_buffer[1]; | ||||
|     u32 backlog = cmd_buffer[2]; | ||||
| 
 | ||||
|     int ret = ::listen(socket_handle, backlog); | ||||
|     int result = 0; | ||||
|     if (ret != 0) | ||||
|         result = TranslateError(GET_ERRNO); | ||||
| 
 | ||||
|     cmd_buffer[2] = ret; | ||||
|     cmd_buffer[1] = result; | ||||
| } | ||||
| 
 | ||||
| static void Accept(Service::Interface* self) { | ||||
|     // TODO(Subv): Calling this function on a blocking socket will block the emu thread, 
 | ||||
|     // preventing graceful shutdown when closing the emulator, this can be fixed by always 
 | ||||
|     // performing nonblocking operations and spinlock until the data is available
 | ||||
|     u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||||
|     u32 socket_handle = cmd_buffer[1]; | ||||
|     socklen_t max_addr_len = static_cast<socklen_t>(cmd_buffer[2]); | ||||
|     sockaddr addr; | ||||
|     socklen_t addr_len = sizeof(addr); | ||||
|     u32 ret = static_cast<u32>(::accept(socket_handle, &addr, &addr_len)); | ||||
|      | ||||
|     if (ret != SOCKET_ERROR_VALUE) | ||||
|         open_sockets[ret] = { ret, true }; | ||||
| 
 | ||||
|     int result = 0; | ||||
|     if (ret == SOCKET_ERROR_VALUE) { | ||||
|         result = TranslateError(GET_ERRNO); | ||||
|     } else { | ||||
|         CTRSockAddr ctr_addr = CTRSockAddr::FromPlatform(addr); | ||||
|         Memory::WriteBlock(cmd_buffer[0x104 >> 2], (const u8*)&ctr_addr, max_addr_len); | ||||
|     } | ||||
| 
 | ||||
|     cmd_buffer[2] = ret; | ||||
|     cmd_buffer[1] = result; | ||||
| } | ||||
| 
 | ||||
| static void GetHostId(Service::Interface* self) { | ||||
|     u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||||
| 
 | ||||
|     char name[128]; | ||||
|     gethostname(name, sizeof(name)); | ||||
|     hostent* host = gethostbyname(name); | ||||
|     in_addr* addr = reinterpret_cast<in_addr*>(host->h_addr); | ||||
| 
 | ||||
|     cmd_buffer[2] = addr->s_addr; | ||||
|     cmd_buffer[1] = 0; | ||||
| } | ||||
| 
 | ||||
| static void Close(Service::Interface* self) { | ||||
|     u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||||
|     u32 socket_handle = cmd_buffer[1]; | ||||
| 
 | ||||
|     int ret = 0; | ||||
|     open_sockets.erase(socket_handle); | ||||
| 
 | ||||
|     ret = closesocket(socket_handle); | ||||
| 
 | ||||
|     int result = 0; | ||||
|     if (ret != 0) | ||||
|         result = TranslateError(GET_ERRNO); | ||||
| 
 | ||||
|     cmd_buffer[2] = ret; | ||||
|     cmd_buffer[1] = result; | ||||
| } | ||||
| 
 | ||||
| static void SendTo(Service::Interface* self) { | ||||
|     u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||||
|     u32 socket_handle = cmd_buffer[1]; | ||||
|     u32 len = cmd_buffer[2]; | ||||
|     u32 flags = cmd_buffer[3]; | ||||
|     u32 addr_len = cmd_buffer[4]; | ||||
| 
 | ||||
|     u8* input_buff = Memory::GetPointer(cmd_buffer[8]); | ||||
|     CTRSockAddr* ctr_dest_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[10])); | ||||
| 
 | ||||
|     if (ctr_dest_addr == nullptr) { | ||||
|         cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
 | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     int ret = -1; | ||||
|     if (addr_len > 0) { | ||||
|         sockaddr dest_addr = CTRSockAddr::ToPlatform(*ctr_dest_addr); | ||||
|         ret = ::sendto(socket_handle, (const char*)input_buff, len, flags, &dest_addr, sizeof(dest_addr)); | ||||
|     } else { | ||||
|         ret = ::sendto(socket_handle, (const char*)input_buff, len, flags, nullptr, 0); | ||||
|     } | ||||
| 
 | ||||
|     int result = 0; | ||||
|     if (ret == SOCKET_ERROR_VALUE) | ||||
|         result = TranslateError(GET_ERRNO); | ||||
| 
 | ||||
|     cmd_buffer[2] = ret; | ||||
|     cmd_buffer[1] = result; | ||||
| } | ||||
| 
 | ||||
| static void RecvFrom(Service::Interface* self) { | ||||
|     // TODO(Subv): Calling this function on a blocking socket will block the emu thread, 
 | ||||
|     // preventing graceful shutdown when closing the emulator, this can be fixed by always 
 | ||||
|     // performing nonblocking operations and spinlock until the data is available
 | ||||
|     u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||||
|     u32 socket_handle = cmd_buffer[1]; | ||||
|     u32 len = cmd_buffer[2]; | ||||
|     u32 flags = cmd_buffer[3]; | ||||
|     socklen_t addr_len = static_cast<socklen_t>(cmd_buffer[4]); | ||||
| 
 | ||||
|     u8* output_buff = Memory::GetPointer(cmd_buffer[0x104 >> 2]); | ||||
|     sockaddr src_addr; | ||||
|     socklen_t src_addr_len = sizeof(src_addr); | ||||
|     int ret = ::recvfrom(socket_handle, (char*)output_buff, len, flags, &src_addr, &src_addr_len); | ||||
| 
 | ||||
|     if (cmd_buffer[0x1A0 >> 2] != 0) { | ||||
|         CTRSockAddr* ctr_src_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x1A0 >> 2])); | ||||
|         *ctr_src_addr = CTRSockAddr::FromPlatform(src_addr); | ||||
|     } | ||||
| 
 | ||||
|     int result = 0; | ||||
|     int total_received = ret; | ||||
|     if (ret == SOCKET_ERROR_VALUE) { | ||||
|         result = TranslateError(GET_ERRNO); | ||||
|         total_received = 0; | ||||
|     } | ||||
| 
 | ||||
|     cmd_buffer[1] = result; | ||||
|     cmd_buffer[2] = ret; | ||||
|     cmd_buffer[3] = total_received; | ||||
| } | ||||
| 
 | ||||
| static void Poll(Service::Interface* self) { | ||||
|     u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||||
|     u32 nfds = cmd_buffer[1]; | ||||
|     int timeout = cmd_buffer[2]; | ||||
|     CTRPollFD* input_fds = reinterpret_cast<CTRPollFD*>(Memory::GetPointer(cmd_buffer[6])); | ||||
|     CTRPollFD* output_fds = reinterpret_cast<CTRPollFD*>(Memory::GetPointer(cmd_buffer[0x104 >> 2])); | ||||
| 
 | ||||
|     // The 3ds_pollfd and the pollfd structures may be different (Windows/Linux have different sizes)
 | ||||
|     // so we have to copy the data
 | ||||
|     pollfd* platform_pollfd = new pollfd[nfds]; | ||||
|     for (unsigned current_fds = 0; current_fds < nfds; ++current_fds) | ||||
|         platform_pollfd[current_fds] = CTRPollFD::ToPlatform(input_fds[current_fds]); | ||||
|      | ||||
|     int ret = ::poll(platform_pollfd, nfds, timeout); | ||||
| 
 | ||||
|     // Now update the output pollfd structure
 | ||||
|     for (unsigned current_fds = 0; current_fds < nfds; ++current_fds) | ||||
|         output_fds[current_fds] = CTRPollFD::FromPlatform(platform_pollfd[current_fds]); | ||||
| 
 | ||||
|     delete[] platform_pollfd; | ||||
| 
 | ||||
|     int result = 0; | ||||
|     if (ret == SOCKET_ERROR_VALUE) | ||||
|         result = TranslateError(GET_ERRNO); | ||||
| 
 | ||||
|     cmd_buffer[1] = result; | ||||
|     cmd_buffer[2] = ret; | ||||
| } | ||||
| 
 | ||||
| static void GetSockName(Service::Interface* self) { | ||||
|     u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||||
|     u32 socket_handle = cmd_buffer[1]; | ||||
|     socklen_t ctr_len = cmd_buffer[2]; | ||||
| 
 | ||||
|     CTRSockAddr* ctr_dest_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x104 >> 2])); | ||||
| 
 | ||||
|     sockaddr dest_addr; | ||||
|     socklen_t dest_addr_len = sizeof(dest_addr); | ||||
|     int ret = ::getsockname(socket_handle, &dest_addr, &dest_addr_len); | ||||
| 
 | ||||
|     if (ctr_dest_addr != nullptr) { | ||||
|         *ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr); | ||||
|     } else { | ||||
|         cmd_buffer[1] = -1; // TODO(Subv): Verify error
 | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     int result = 0; | ||||
|     if (ret != 0) | ||||
|         result = TranslateError(GET_ERRNO); | ||||
| 
 | ||||
|     cmd_buffer[2] = ret; | ||||
|     cmd_buffer[1] = result; | ||||
| } | ||||
| 
 | ||||
| static void Shutdown(Service::Interface* self) { | ||||
|     u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||||
|     u32 socket_handle = cmd_buffer[1]; | ||||
|     int how = cmd_buffer[2]; | ||||
| 
 | ||||
|     int ret = ::shutdown(socket_handle, how); | ||||
|     int result = 0; | ||||
|     if (ret != 0) | ||||
|         result = TranslateError(GET_ERRNO); | ||||
|     cmd_buffer[2] = ret; | ||||
|     cmd_buffer[1] = result; | ||||
| } | ||||
| 
 | ||||
| static void GetPeerName(Service::Interface* self) { | ||||
|     u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||||
|     u32 socket_handle = cmd_buffer[1]; | ||||
|     socklen_t len = cmd_buffer[2]; | ||||
| 
 | ||||
|     CTRSockAddr* ctr_dest_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x104 >> 2])); | ||||
|      | ||||
|     sockaddr dest_addr; | ||||
|     socklen_t dest_addr_len = sizeof(dest_addr); | ||||
|     int ret = ::getpeername(socket_handle, &dest_addr, &dest_addr_len); | ||||
| 
 | ||||
|     if (ctr_dest_addr != nullptr) { | ||||
|         *ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr); | ||||
|     } else { | ||||
|         cmd_buffer[1] = -1; | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     int result = 0; | ||||
|     if (ret != 0) | ||||
|         result = TranslateError(GET_ERRNO); | ||||
| 
 | ||||
|     cmd_buffer[2] = ret; | ||||
|     cmd_buffer[1] = result; | ||||
| } | ||||
| 
 | ||||
| static void Connect(Service::Interface* self) { | ||||
|     // TODO(Subv): Calling this function on a blocking socket will block the emu thread, 
 | ||||
|     // preventing graceful shutdown when closing the emulator, this can be fixed by always 
 | ||||
|     // performing nonblocking operations and spinlock until the data is available
 | ||||
|     u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||||
|     u32 socket_handle = cmd_buffer[1]; | ||||
|     socklen_t len = cmd_buffer[2]; | ||||
| 
 | ||||
|     CTRSockAddr* ctr_input_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[6])); | ||||
|     if (ctr_input_addr == nullptr) { | ||||
|         cmd_buffer[1] = -1; // TODO(Subv): Verify error
 | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     sockaddr input_addr = CTRSockAddr::ToPlatform(*ctr_input_addr); | ||||
|     int ret = ::connect(socket_handle, &input_addr, sizeof(input_addr)); | ||||
|     int result = 0; | ||||
|     if (ret != 0) | ||||
|         result = TranslateError(GET_ERRNO); | ||||
|     cmd_buffer[2] = ret; | ||||
|     cmd_buffer[1] = result; | ||||
| } | ||||
| 
 | ||||
| static void InitializeSockets(Service::Interface* self) { | ||||
|     // TODO(Subv): Implement
 | ||||
| #if EMU_PLATFORM == PLATFORM_WINDOWS | ||||
|     WSADATA data; | ||||
|     WSAStartup(MAKEWORD(2, 2), &data); | ||||
| #endif | ||||
| 
 | ||||
|     u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||||
|     cmd_buffer[1] = 0; | ||||
| } | ||||
| 
 | ||||
| static void ShutdownSockets(Service::Interface* self) { | ||||
|     // TODO(Subv): Implement
 | ||||
|     CleanupSockets(); | ||||
| 
 | ||||
| #if EMU_PLATFORM == PLATFORM_WINDOWS | ||||
|     WSACleanup(); | ||||
| #endif | ||||
| 
 | ||||
|     u32* cmd_buffer = Kernel::GetCommandBuffer(); | ||||
|     cmd_buffer[1] = 0; | ||||
| } | ||||
| 
 | ||||
| const Interface::FunctionInfo FunctionTable[] = { | ||||
|     {0x00010044, nullptr,                       "InitializeSockets"}, | ||||
|     {0x000200C2, nullptr,                       "socket"}, | ||||
|     {0x00030082, nullptr,                       "listen"}, | ||||
|     {0x00040082, nullptr,                       "accept"}, | ||||
|     {0x00050084, nullptr,                       "bind"}, | ||||
|     {0x00060084, nullptr,                       "connect"}, | ||||
|     {0x00010044, InitializeSockets,             "InitializeSockets"}, | ||||
|     {0x000200C2, Socket,                        "Socket"}, | ||||
|     {0x00030082, Listen,                        "Listen"}, | ||||
|     {0x00040082, Accept,                        "Accept"}, | ||||
|     {0x00050084, Bind,                          "Bind"}, | ||||
|     {0x00060084, Connect,                       "Connect"}, | ||||
|     {0x00070104, nullptr,                       "recvfrom_other"}, | ||||
|     {0x00080102, nullptr,                       "recvfrom"}, | ||||
|     {0x00080102, RecvFrom,                      "RecvFrom"}, | ||||
|     {0x00090106, nullptr,                       "sendto_other"}, | ||||
|     {0x000A0106, nullptr,                       "sendto"}, | ||||
|     {0x000B0042, nullptr,                       "close"}, | ||||
|     {0x000C0082, nullptr,                       "shutdown"}, | ||||
|     {0x000D0082, nullptr,                       "gethostbyname"}, | ||||
|     {0x000E00C2, nullptr,                       "gethostbyaddr"}, | ||||
|     {0x000A0106, SendTo,                        "SendTo"}, | ||||
|     {0x000B0042, Close,                         "Close"}, | ||||
|     {0x000C0082, Shutdown,                      "Shutdown"}, | ||||
|     {0x000D0082, nullptr,                       "GetHostByName"}, | ||||
|     {0x000E00C2, nullptr,                       "GetHostByAddr"}, | ||||
|     {0x000F0106, nullptr,                       "unknown_resolve_ip"}, | ||||
|     {0x00110102, nullptr,                       "getsockopt"}, | ||||
|     {0x00120104, nullptr,                       "setsockopt"}, | ||||
|     {0x001300C2, nullptr,                       "fcntl"}, | ||||
|     {0x00140084, nullptr,                       "poll"}, | ||||
|     {0x00150042, nullptr,                       "sockatmark"}, | ||||
|     {0x00160000, nullptr,                       "gethostid"}, | ||||
|     {0x00170082, nullptr,                       "getsockname"}, | ||||
|     {0x00180082, nullptr,                       "getpeername"}, | ||||
|     {0x00190000, nullptr,                       "ShutdownSockets"}, | ||||
|     {0x00110102, nullptr,                       "GetSockOpt"}, | ||||
|     {0x00120104, nullptr,                       "SetSockOpt"}, | ||||
|     {0x001300C2, Fcntl,                         "Fcntl"}, | ||||
|     {0x00140084, Poll,                          "Poll"}, | ||||
|     {0x00150042, nullptr,                       "SockAtMark"}, | ||||
|     {0x00160000, GetHostId,                     "GetHostId"}, | ||||
|     {0x00170082, GetSockName,                   "GetSockName"}, | ||||
|     {0x00180082, GetPeerName,                   "GetPeerName"}, | ||||
|     {0x00190000, ShutdownSockets,               "ShutdownSockets"}, | ||||
|     {0x001A00C0, nullptr,                       "GetNetworkOpt"}, | ||||
|     {0x001B0040, nullptr,                       "ICMPSocket"}, | ||||
|     {0x001C0104, nullptr,                       "ICMPPing"}, | ||||
|  | @ -52,4 +724,11 @@ Interface::Interface() { | |||
|     Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||||
| } | ||||
| 
 | ||||
| Interface::~Interface() { | ||||
|     CleanupSockets(); | ||||
| #if EMU_PLATFORM == PLATFORM_WINDOWS | ||||
|     WSACleanup(); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| } // namespace
 | ||||
|  |  | |||
|  | @ -14,6 +14,7 @@ namespace SOC_U { | |||
| class Interface : public Service::Interface { | ||||
| public: | ||||
|     Interface(); | ||||
|     ~Interface(); | ||||
| 
 | ||||
|     std::string GetPortName() const override { | ||||
|         return "soc:U"; | ||||
|  |  | |||
|  | @ -352,6 +352,18 @@ static s64 GetSystemTick() { | |||
|     return (s64)Core::g_app_core->GetTicks(); | ||||
| } | ||||
| 
 | ||||
| /// Creates a memory block at the specified address with the specified permissions and size
 | ||||
| static Result CreateMemoryBlock(Handle* memblock, u32 addr, u32 size, u32 my_permission, | ||||
|     u32 other_permission) { | ||||
| 
 | ||||
|     // TODO(Subv): Implement this function
 | ||||
| 
 | ||||
|     Handle shared_memory = Kernel::CreateSharedMemory(); | ||||
|     *memblock = shared_memory; | ||||
|     LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%08X", addr); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| const HLE::FunctionDef SVC_Table[] = { | ||||
|     {0x00, nullptr,                         "Unknown"}, | ||||
|     {0x01, HLE::Wrap<ControlMemory>,        "ControlMemory"}, | ||||
|  | @ -383,7 +395,7 @@ const HLE::FunctionDef SVC_Table[] = { | |||
|     {0x1B, nullptr,                         "SetTimer"}, | ||||
|     {0x1C, nullptr,                         "CancelTimer"}, | ||||
|     {0x1D, nullptr,                         "ClearTimer"}, | ||||
|     {0x1E, nullptr,                         "CreateMemoryBlock"}, | ||||
|     {0x1E, HLE::Wrap<CreateMemoryBlock>,    "CreateMemoryBlock"}, | ||||
|     {0x1F, HLE::Wrap<MapMemoryBlock>,       "MapMemoryBlock"}, | ||||
|     {0x20, nullptr,                         "UnmapMemoryBlock"}, | ||||
|     {0x21, HLE::Wrap<CreateAddressArbiter>, "CreateAddressArbiter"}, | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue