diff --git a/rpcs3/Emu/Cell/lv2/sys_net.cpp b/rpcs3/Emu/Cell/lv2/sys_net.cpp index 8433d46bbe..0156cfc1e2 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net.cpp @@ -900,8 +900,79 @@ error_code sys_net_bnet_sendmsg(ppu_thread& ppu, s32 s, vm::cptr { ppu.state += cpu_flag::wait; - sys_net.todo("sys_net_bnet_sendmsg(s=%d, msg=*0x%x, flags=0x%x)", s, msg, flags); - return CELL_OK; + sys_net.warning("sys_net_bnet_sendmsg(s=%d, msg=*0x%x, flags=0x%x)", s, msg, flags); + + if (flags & ~(SYS_NET_MSG_DONTWAIT | SYS_NET_MSG_WAITALL | SYS_NET_MSG_USECRYPTO | SYS_NET_MSG_USESIGNATURE)) + { + fmt::throw_exception("sys_net_bnet_sendmsg(s=%d): unknown flags (0x%x)", flags); + } + + s32 result{}; + + const auto sock = idm::check(s, [&](lv2_socket& sock) + { + auto netmsg = msg.get_ptr(); + const auto success = sock.sendmsg(flags, *netmsg); + + if (success) + { + result = *success; + + return true; + } + + sock.poll_queue(idm::get_unlocked>(ppu.id), lv2_socket::poll_t::write, [&](bs_t events) -> bool + { + if (events & lv2_socket::poll_t::write) + { + const auto success = sock.sendmsg(flags, *netmsg, false); + + if (success) + { + result = *success; + lv2_obj::awake(&ppu); + return true; + } + } + + sock.set_poll_event(lv2_socket::poll_t::write); + return false; + }); + + lv2_obj::sleep(ppu); + return false; + }); + + if (!sock) + { + return -SYS_NET_EBADF; + } + + if (!sock.ret) + { + while (true) + { + const auto state = ppu.state.fetch_sub(cpu_flag::signal); + if (is_stopped(state) || state & cpu_flag::signal) + { + break; + } + thread_ctrl::wait_on(ppu.state, state); + } + + if (ppu.gpr[3] == static_cast(-SYS_NET_EINTR)) + { + return -SYS_NET_EINTR; + } + } + + if (result >= 0 || result == -SYS_NET_EWOULDBLOCK) + { + return not_an_error(result); + } + + + return sys_net_error{result}; } error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr buf, u32 len, s32 flags, vm::cptr addr, u32 addrlen) diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.h b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.h index 82f223ce7a..243d2367f3 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.h +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.h @@ -94,6 +94,7 @@ public: virtual std::optional, sys_net_sockaddr>> recvfrom(s32 flags, u32 len, bool is_lock = true) = 0; virtual std::optional sendto(s32 flags, const std::vector& buf, std::optional opt_sn_addr, bool is_lock = true) = 0; + virtual std::optional sendmsg(s32 flags, const sys_net_msghdr& msg, bool is_lock = true) = 0; virtual void close() = 0; virtual s32 shutdown(s32 how) = 0; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp index 9ea8bd56bb..d6fdd671bd 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp @@ -931,6 +931,50 @@ std::optional lv2_socket_native::sendto(s32 flags, const std::vector& b return std::nullopt; } +std::optional lv2_socket_native::sendmsg(s32 flags, const sys_net_msghdr& msg, bool is_lock) +{ + std::unique_lock lock(mutex, std::defer_lock); + + if (is_lock) + { + lock.lock(); + } + + int native_flags = 0; + int native_result = -1; + + sys_net_error result{}; + + if (flags & SYS_NET_MSG_WAITALL) + { + native_flags |= MSG_WAITALL; + } + + + for (int i = 0; i < msg.msg_iovlen; i++) + { + auto iov_base = msg.msg_iov[i].iov_base; + const u32 len = msg.msg_iov[i].iov_len; + const std::vector buf_copy(vm::_ptr(iov_base.addr()), vm::_ptr(iov_base.addr()) + len); + + native_result = ::send(socket, reinterpret_cast(buf_copy.data()), buf_copy.size(), native_flags); + + if (native_result >= 0) + { + return {native_result}; + } + } + + result = get_last_error(!so_nbio && (flags & SYS_NET_MSG_DONTWAIT) == 0); + + if (result) + { + return {-result}; + } + + return std::nullopt; +} + void lv2_socket_native::close() { std::lock_guard lock(mutex); diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.h b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.h index ecc5f59fbe..0fce833bea 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.h +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.h @@ -48,6 +48,7 @@ public: s32 setsockopt(s32 level, s32 optname, const std::vector& optval) override; std::optional, sys_net_sockaddr>> recvfrom(s32 flags, u32 len, bool is_lock = true) override; std::optional sendto(s32 flags, const std::vector& buf, std::optional opt_sn_addr, bool is_lock = true) override; + std::optional sendmsg(s32 flags, const sys_net_msghdr& msg, bool is_lock = true) override; s32 poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) override; std::tuple select(bs_t selected, pollfd& native_pfd) override; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp index da29d4fdd5..eaf531add9 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp @@ -284,6 +284,12 @@ std::optional lv2_socket_p2p::sendto(s32 flags, const std::vector& buf, return std::nullopt; } +std::optional lv2_socket_p2p::sendmsg([[maybe_unused]] s32 flags, [[maybe_unused]] const sys_net_msghdr& msg, [[maybe_unused]] bool is_lock) +{ + sys_net.todo("lv2_socket_p2p::sendmsg"); + return {}; +} + void lv2_socket_p2p::close() { if (!port || !vport) diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.h b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.h index 3b9c17e642..5601c28977 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.h +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.h @@ -25,6 +25,7 @@ public: std::optional, sys_net_sockaddr>> recvfrom(s32 flags, u32 len, bool is_lock = true) override; std::optional sendto(s32 flags, const std::vector& buf, std::optional opt_sn_addr, bool is_lock = true) override; + std::optional sendmsg(s32 flags, const sys_net_msghdr& msg, bool is_lock = true) override; void close() override; s32 shutdown(s32 how) override; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp index 49e6c57770..9dfd2ffba5 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp @@ -795,6 +795,12 @@ std::optional lv2_socket_p2ps::sendto([[maybe_unused]] s32 flags, const std return {buf.size()}; } +std::optional lv2_socket_p2ps::sendmsg([[maybe_unused]] s32 flags, [[maybe_unused]] const sys_net_msghdr& msg, [[maybe_unused]] bool is_lock) +{ + sys_net.todo("lv2_socket_p2ps::sendmsg"); + return {}; +} + void lv2_socket_p2ps::close() { if (!port || !vport) diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.h b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.h index 740e369252..ff573ccac2 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.h +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.h @@ -79,6 +79,7 @@ public: std::optional, sys_net_sockaddr>> recvfrom(s32 flags, u32 len, bool is_lock = true) override; std::optional sendto(s32 flags, const std::vector& buf, std::optional opt_sn_addr, bool is_lock = true) override; + std::optional sendmsg(s32 flags, const sys_net_msghdr& msg, bool is_lock = true) override; void close() override; s32 shutdown(s32 how) override; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_raw.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_raw.cpp index 2ee070eb19..d1d1d7acc8 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_raw.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_raw.cpp @@ -84,6 +84,12 @@ std::optional lv2_socket_raw::sendto([[maybe_unused]] s32 flags, [[maybe_un return buf.size(); } +std::optional lv2_socket_raw::sendmsg([[maybe_unused]] s32 flags, [[maybe_unused]] const sys_net_msghdr& msg, [[maybe_unused]] bool is_lock) +{ + sys_net.todo("lv2_socket_raw::sendmsg"); + return {}; +} + void lv2_socket_raw::close() { sys_net.todo("lv2_socket_raw::close"); diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_raw.h b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_raw.h index 6886f23e45..2071fe3155 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_raw.h +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_raw.h @@ -25,6 +25,7 @@ public: std::optional, sys_net_sockaddr>> recvfrom(s32 flags, u32 len, bool is_lock = true) override; std::optional sendto(s32 flags, const std::vector& buf, std::optional opt_sn_addr, bool is_lock = true) override; + std::optional sendmsg(s32 flags, const sys_net_msghdr& msg, bool is_lock = true) override; void close() override; s32 shutdown(s32 how) override;