mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	Merge pull request #265 from Subv/socu
SOC_U: Implemented some socket functions
This commit is contained in:
		
						commit
						08b6cf778d
					
				
					 8 changed files with 726 additions and 25 deletions
				
			
		|  | @ -24,10 +24,10 @@ endif() | ||||||
| if (APPLE) | if (APPLE) | ||||||
|     target_link_libraries(citra iconv ${COREFOUNDATION_LIBRARY}) |     target_link_libraries(citra iconv ${COREFOUNDATION_LIBRARY}) | ||||||
| elseif (WIN32) | 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 |     if (MINGW) # GCC does not support codecvt, so use iconv instead | ||||||
|         target_link_libraries(citra iconv) |         target_link_libraries(citra iconv) | ||||||
|     endif()     |     endif() | ||||||
| else() # Unix | else() # Unix | ||||||
|     target_link_libraries(citra rt) |     target_link_libraries(citra rt) | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
|  | @ -68,7 +68,7 @@ endif() | ||||||
| if (APPLE) | if (APPLE) | ||||||
|     target_link_libraries(citra-qt iconv ${COREFOUNDATION_LIBRARY}) |     target_link_libraries(citra-qt iconv ${COREFOUNDATION_LIBRARY}) | ||||||
| elseif (WIN32) | elseif (WIN32) | ||||||
|     target_link_libraries(citra-qt winmm) |     target_link_libraries(citra-qt winmm wsock32 ws2_32) | ||||||
| else() # Unix | else() # Unix | ||||||
|     target_link_libraries(citra-qt rt) |     target_link_libraries(citra-qt rt) | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
|  | @ -39,6 +39,7 @@ static std::shared_ptr<Logger> global_logger; | ||||||
|         SUB(Service, CFG) \ |         SUB(Service, CFG) \ | ||||||
|         SUB(Service, DSP) \ |         SUB(Service, DSP) \ | ||||||
|         SUB(Service, HID) \ |         SUB(Service, HID) \ | ||||||
|  |         SUB(Service, SOC) \ | ||||||
|         CLS(HW) \ |         CLS(HW) \ | ||||||
|         SUB(HW, Memory) \ |         SUB(HW, Memory) \ | ||||||
|         SUB(HW, GPU) \ |         SUB(HW, GPU) \ | ||||||
|  |  | ||||||
|  | @ -59,6 +59,7 @@ enum class Class : ClassType { | ||||||
|     Service_CFG,                ///< The CFG (Configuration) service
 |     Service_CFG,                ///< The CFG (Configuration) service
 | ||||||
|     Service_DSP,                ///< The DSP (DSP control) service
 |     Service_DSP,                ///< The DSP (DSP control) service
 | ||||||
|     Service_HID,                ///< The HID (User input) service
 |     Service_HID,                ///< The HID (User input) service
 | ||||||
|  |     Service_SOC,                ///< The SOC (Socket) service
 | ||||||
|     HW,                         ///< Low-level hardware emulation
 |     HW,                         ///< Low-level hardware emulation
 | ||||||
|     HW_Memory,                  ///< Memory-map and address translation
 |     HW_Memory,                  ///< Memory-map and address translation
 | ||||||
|     HW_GPU,                     ///< GPU control emulation
 |     HW_GPU,                     ///< GPU control emulation
 | ||||||
|  |  | ||||||
|  | @ -128,6 +128,13 @@ template<s32 func(s32*, u32, s32)> void Wrap() { | ||||||
|     FuncReturn(retval); |     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
 | // Function wrappers that return type u32
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2,40 +2,712 @@ | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // 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/log.h" | ||||||
|  | #include "common/scope_exit.h" | ||||||
| #include "core/hle/hle.h" | #include "core/hle/hle.h" | ||||||
| #include "core/hle/service/soc_u.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
 | ||||||
| 
 | 
 | ||||||
| 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[] = { | const Interface::FunctionInfo FunctionTable[] = { | ||||||
|     {0x00010044, nullptr,                       "InitializeSockets"}, |     {0x00010044, InitializeSockets,             "InitializeSockets"}, | ||||||
|     {0x000200C2, nullptr,                       "socket"}, |     {0x000200C2, Socket,                        "Socket"}, | ||||||
|     {0x00030082, nullptr,                       "listen"}, |     {0x00030082, Listen,                        "Listen"}, | ||||||
|     {0x00040082, nullptr,                       "accept"}, |     {0x00040082, Accept,                        "Accept"}, | ||||||
|     {0x00050084, nullptr,                       "bind"}, |     {0x00050084, Bind,                          "Bind"}, | ||||||
|     {0x00060084, nullptr,                       "connect"}, |     {0x00060084, Connect,                       "Connect"}, | ||||||
|     {0x00070104, nullptr,                       "recvfrom_other"}, |     {0x00070104, nullptr,                       "recvfrom_other"}, | ||||||
|     {0x00080102, nullptr,                       "recvfrom"}, |     {0x00080102, RecvFrom,                      "RecvFrom"}, | ||||||
|     {0x00090106, nullptr,                       "sendto_other"}, |     {0x00090106, nullptr,                       "sendto_other"}, | ||||||
|     {0x000A0106, nullptr,                       "sendto"}, |     {0x000A0106, SendTo,                        "SendTo"}, | ||||||
|     {0x000B0042, nullptr,                       "close"}, |     {0x000B0042, Close,                         "Close"}, | ||||||
|     {0x000C0082, nullptr,                       "shutdown"}, |     {0x000C0082, Shutdown,                      "Shutdown"}, | ||||||
|     {0x000D0082, nullptr,                       "gethostbyname"}, |     {0x000D0082, nullptr,                       "GetHostByName"}, | ||||||
|     {0x000E00C2, nullptr,                       "gethostbyaddr"}, |     {0x000E00C2, nullptr,                       "GetHostByAddr"}, | ||||||
|     {0x000F0106, nullptr,                       "unknown_resolve_ip"}, |     {0x000F0106, nullptr,                       "unknown_resolve_ip"}, | ||||||
|     {0x00110102, nullptr,                       "getsockopt"}, |     {0x00110102, nullptr,                       "GetSockOpt"}, | ||||||
|     {0x00120104, nullptr,                       "setsockopt"}, |     {0x00120104, nullptr,                       "SetSockOpt"}, | ||||||
|     {0x001300C2, nullptr,                       "fcntl"}, |     {0x001300C2, Fcntl,                         "Fcntl"}, | ||||||
|     {0x00140084, nullptr,                       "poll"}, |     {0x00140084, Poll,                          "Poll"}, | ||||||
|     {0x00150042, nullptr,                       "sockatmark"}, |     {0x00150042, nullptr,                       "SockAtMark"}, | ||||||
|     {0x00160000, nullptr,                       "gethostid"}, |     {0x00160000, GetHostId,                     "GetHostId"}, | ||||||
|     {0x00170082, nullptr,                       "getsockname"}, |     {0x00170082, GetSockName,                   "GetSockName"}, | ||||||
|     {0x00180082, nullptr,                       "getpeername"}, |     {0x00180082, GetPeerName,                   "GetPeerName"}, | ||||||
|     {0x00190000, nullptr,                       "ShutdownSockets"}, |     {0x00190000, ShutdownSockets,               "ShutdownSockets"}, | ||||||
|     {0x001A00C0, nullptr,                       "GetNetworkOpt"}, |     {0x001A00C0, nullptr,                       "GetNetworkOpt"}, | ||||||
|     {0x001B0040, nullptr,                       "ICMPSocket"}, |     {0x001B0040, nullptr,                       "ICMPSocket"}, | ||||||
|     {0x001C0104, nullptr,                       "ICMPPing"}, |     {0x001C0104, nullptr,                       "ICMPPing"}, | ||||||
|  | @ -52,4 +724,11 @@ Interface::Interface() { | ||||||
|     Register(FunctionTable, ARRAY_SIZE(FunctionTable)); |     Register(FunctionTable, ARRAY_SIZE(FunctionTable)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | Interface::~Interface() { | ||||||
|  |     CleanupSockets(); | ||||||
|  | #if EMU_PLATFORM == PLATFORM_WINDOWS | ||||||
|  |     WSACleanup(); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace
 | } // namespace
 | ||||||
|  |  | ||||||
|  | @ -14,6 +14,7 @@ namespace SOC_U { | ||||||
| class Interface : public Service::Interface { | class Interface : public Service::Interface { | ||||||
| public: | public: | ||||||
|     Interface(); |     Interface(); | ||||||
|  |     ~Interface(); | ||||||
| 
 | 
 | ||||||
|     std::string GetPortName() const override { |     std::string GetPortName() const override { | ||||||
|         return "soc:U"; |         return "soc:U"; | ||||||
|  |  | ||||||
|  | @ -352,6 +352,18 @@ static s64 GetSystemTick() { | ||||||
|     return (s64)Core::g_app_core->GetTicks(); |     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[] = { | const HLE::FunctionDef SVC_Table[] = { | ||||||
|     {0x00, nullptr,                         "Unknown"}, |     {0x00, nullptr,                         "Unknown"}, | ||||||
|     {0x01, HLE::Wrap<ControlMemory>,        "ControlMemory"}, |     {0x01, HLE::Wrap<ControlMemory>,        "ControlMemory"}, | ||||||
|  | @ -383,7 +395,7 @@ const HLE::FunctionDef SVC_Table[] = { | ||||||
|     {0x1B, nullptr,                         "SetTimer"}, |     {0x1B, nullptr,                         "SetTimer"}, | ||||||
|     {0x1C, nullptr,                         "CancelTimer"}, |     {0x1C, nullptr,                         "CancelTimer"}, | ||||||
|     {0x1D, nullptr,                         "ClearTimer"}, |     {0x1D, nullptr,                         "ClearTimer"}, | ||||||
|     {0x1E, nullptr,                         "CreateMemoryBlock"}, |     {0x1E, HLE::Wrap<CreateMemoryBlock>,    "CreateMemoryBlock"}, | ||||||
|     {0x1F, HLE::Wrap<MapMemoryBlock>,       "MapMemoryBlock"}, |     {0x1F, HLE::Wrap<MapMemoryBlock>,       "MapMemoryBlock"}, | ||||||
|     {0x20, nullptr,                         "UnmapMemoryBlock"}, |     {0x20, nullptr,                         "UnmapMemoryBlock"}, | ||||||
|     {0x21, HLE::Wrap<CreateAddressArbiter>, "CreateAddressArbiter"}, |     {0x21, HLE::Wrap<CreateAddressArbiter>, "CreateAddressArbiter"}, | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue