diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 72835324a7..de8fd8ac93 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -623,6 +623,7 @@ if(WIN32) elseif(APPLE) target_sources(core PRIVATE HW/EXI/BBA/TAP_Apple.cpp + HW/EXI/BBA/TAPServer_Apple.cpp HW/EXI/BBA/XLINK_KAI_BBA.cpp HW/WiimoteReal/IOdarwin.h HW/WiimoteReal/IOdarwin_private.h diff --git a/Source/Core/Core/HW/EXI/BBA/TAPServer_Apple.cpp b/Source/Core/Core/HW/EXI/BBA/TAPServer_Apple.cpp new file mode 100644 index 0000000000..d7a0156b5d --- /dev/null +++ b/Source/Core/Core/HW/EXI/BBA/TAPServer_Apple.cpp @@ -0,0 +1,127 @@ +// Copyright 2020 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include +#include + +#include "Common/Logging/Log.h" +#include "Common/StringUtil.h" +#include "Core/HW/EXI/EXI_Device.h" +#include "Core/HW/EXI/EXI_DeviceEthernet.h" + +namespace ExpansionInterface +{ +// This interface is only implemented on macOS, since macOS needs a replacement +// for TunTap when the kernel extension is no longer supported. This interface +// only appears in the menu on macOS, so on other platforms, it does nothing and +// refuses to activate. + +constexpr char socket_path[] = "/tmp/dolphin-tap"; + +bool CEXIETHERNET::TAPServerNetworkInterface::Activate() +{ + if (IsActivated()) + return true; + + sockaddr_un sun = {}; + if (sizeof(socket_path) > sizeof(sun.sun_path)) + { + ERROR_LOG(SP1, "Socket path is too long, unable to init BBA"); + return false; + } + sun.sun_family = AF_UNIX; + strcpy(sun.sun_path, socket_path); + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd == -1) + { + ERROR_LOG(SP1, "Couldn't create socket, unable to init BBA"); + return false; + } + + if (connect(fd, reinterpret_cast(&sun), sizeof(sun)) == -1) + { + ERROR_LOG(SP1, "Couldn't connect socket (%d), unable to init BBA", errno); + close(fd); + fd = -1; + return false; + } + + INFO_LOG(SP1, "BBA initialized.\n"); + return RecvInit(); +} + +bool CEXIETHERNET::TAPServerNetworkInterface::SendFrame(const u8* frame, u32 size) +{ + { + const std::string s = ArrayToString(frame, size, 0x10); + INFO_LOG(SP1, "SendFrame %x\n%s\n", size, s.c_str()); + } + + auto size16 = u16(size); + if (write(fd, &size16, 2) != 2) + { + ERROR_LOG(SP1, "SendFrame(): could not write size field\n"); + return false; + } + int written_bytes = write(fd, frame, size); + if (u32(written_bytes) != size) + { + ERROR_LOG(SP1, "SendFrame(): expected to write %d bytes, instead wrote %d", size, + written_bytes); + return false; + } + else + { + m_eth_ref->SendComplete(); + return true; + } +} + +void CEXIETHERNET::TAPServerNetworkInterface::ReadThreadHandler() +{ + while (!readThreadShutdown.IsSet()) + { + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + + timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 50000; + if (select(fd + 1, &rfds, nullptr, nullptr, &timeout) <= 0) + continue; + + u16 size; + if (read(fd, &size, 2) != 2) + { + ERROR_LOG(SP1, "Failed to read size field from BBA, err=%d", errno); + } + else + { + int read_bytes = read(fd, m_eth_ref->mRecvBuffer.get(), size); + if (read_bytes < 0) + { + ERROR_LOG(SP1, "Failed to read packet data from BBA, err=%d", errno); + } + else if (readEnabled.IsSet()) + { + std::string data_string = ArrayToString(m_eth_ref->mRecvBuffer.get(), read_bytes, 0x10); + INFO_LOG(SP1, "Read data: %s", data_string.c_str()); + m_eth_ref->mRecvBufferLength = read_bytes; + m_eth_ref->RecvHandlePacket(); + } + } + } +} + +bool CEXIETHERNET::TAPServerNetworkInterface::RecvInit() +{ + readThread = std::thread(&CEXIETHERNET::TAPServerNetworkInterface::ReadThreadHandler, this); + return true; +} + +} // namespace ExpansionInterface diff --git a/Source/Core/Core/HW/EXI/EXI_Device.cpp b/Source/Core/Core/HW/EXI/EXI_Device.cpp index c590b75c3b..926492b160 100644 --- a/Source/Core/Core/HW/EXI/EXI_Device.cpp +++ b/Source/Core/Core/HW/EXI/EXI_Device.cpp @@ -136,6 +136,12 @@ std::unique_ptr EXIDevice_Create(const TEXIDevices device_type, cons result = std::make_unique(BBADeviceType::TAP); break; +#if defined(__APPLE__) + case EXIDEVICE_ETHTAPSERVER: + result = std::make_unique(BBADeviceType::TAPSERVER); + break; +#endif + case EXIDEVICE_ETHXLINK: result = std::make_unique(BBADeviceType::XLINK); break; diff --git a/Source/Core/Core/HW/EXI/EXI_Device.h b/Source/Core/Core/HW/EXI/EXI_Device.h index cf3476c15b..8822e0d7be 100644 --- a/Source/Core/Core/HW/EXI/EXI_Device.h +++ b/Source/Core/Core/HW/EXI/EXI_Device.h @@ -33,6 +33,9 @@ enum TEXIDevices : int EXIDEVICE_MEMORYCARDFOLDER, EXIDEVICE_AGP, EXIDEVICE_ETHXLINK, +#if defined(__APPLE__) + EXIDEVICE_ETHTAPSERVER, +#endif EXIDEVICE_NONE = 0xFF }; diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.cpp index 75103900ff..7c21f6f3fa 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.cpp @@ -48,6 +48,12 @@ CEXIETHERNET::CEXIETHERNET(BBADeviceType type) m_network_interface = std::make_unique(this); INFO_LOG_FMT(SP1, "Created TAP physical network interface."); break; +#if defined(__APPLE__) + case BBADeviceType::TAPSERVER: + m_network_interface = std::make_unique(this); + INFO_LOG(SP1, "Created tapserver physical network interface."); + break; +#endif case BBADeviceType::XLINK: // TODO start BBA with network link down, bring it up after "connected" response from XLink diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h b/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h index 1637b47633..38970ff0fc 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h +++ b/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h @@ -202,6 +202,9 @@ enum class BBADeviceType { TAP, XLINK, +#if defined(__APPLE__) + TAPSERVER, +#endif }; class CEXIETHERNET : public IEXIDevice @@ -337,7 +340,7 @@ private: void RecvStart() override; void RecvStop() override; - private: + protected: #if defined(WIN32) || defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ defined(__OpenBSD__) std::thread readThread; @@ -356,6 +359,22 @@ private: #endif }; +#if defined(__APPLE__) + class TAPServerNetworkInterface : public TAPNetworkInterface + { + public: + explicit TAPServerNetworkInterface(CEXIETHERNET* eth_ref) : TAPNetworkInterface(eth_ref) {} + + public: + bool Activate() override; + bool SendFrame(const u8* frame, u32 size) override; + bool RecvInit() override; + + private: + void ReadThreadHandler(); + }; +#endif + class XLinkNetworkInterface : public NetworkInterface { public: diff --git a/Source/Core/DolphinQt/Settings/GameCubePane.cpp b/Source/Core/DolphinQt/Settings/GameCubePane.cpp index abcec0d0a8..c0af9c653c 100644 --- a/Source/Core/DolphinQt/Settings/GameCubePane.cpp +++ b/Source/Core/DolphinQt/Settings/GameCubePane.cpp @@ -100,12 +100,16 @@ void GameCubePane::CreateWidgets() // Add SP1 devices - for (const auto& entry : - {std::make_pair(tr(""), ExpansionInterface::EXIDEVICE_NONE), - std::make_pair(tr("Dummy"), ExpansionInterface::EXIDEVICE_DUMMY), - std::make_pair(tr("Broadband Adapter (TAP)"), ExpansionInterface::EXIDEVICE_ETH), - std::make_pair(tr("Broadband Adapter (XLink Kai)"), - ExpansionInterface::EXIDEVICE_ETHXLINK)}) + std::vector> sp1Entries{ + std::make_pair(tr(""), ExpansionInterface::EXIDEVICE_NONE), + std::make_pair(tr("Dummy"), ExpansionInterface::EXIDEVICE_DUMMY), + std::make_pair(tr("Broadband Adapter (TAP)"), ExpansionInterface::EXIDEVICE_ETH), + std::make_pair(tr("Broadband Adapter (XLink Kai)"), ExpansionInterface::EXIDEVICE_ETHXLINK)}; +#if defined(__APPLE__) + sp1Entries.emplace_back(std::make_pair(tr("Broadband Adapter (tapserver)"), + ExpansionInterface::EXIDEVICE_ETHTAPSERVER)); +#endif + for (const auto& entry : sp1Entries) { m_slot_combos[2]->addItem(entry.first, entry.second); }