From 595e1197b62284392d4536ebfa87e7c3d6d2f274 Mon Sep 17 00:00:00 2001 From: CrunchBite Date: Sun, 7 Jun 2020 23:17:28 -0400 Subject: [PATCH 1/2] Rename existing BBA to BBA (TAP) to prepare for new BBA Based on work by tommy1019 --- Source/Core/Core/CMakeLists.txt | 8 +- Source/Core/Core/ConfigManager.cpp | 4 +- Source/Core/Core/ConfigManager.h | 3 +- Source/Core/Core/Core.vcxproj | 6 +- Source/Core/Core/Core.vcxproj.filters | 8 +- .../HW/EXI/{BBA-TAP => BBA}/TAP_Apple.cpp | 26 +++--- .../Core/HW/EXI/{BBA-TAP => BBA}/TAP_Unix.cpp | 28 +++--- .../HW/EXI/{BBA-TAP => BBA}/TAP_Win32.cpp | 28 +++--- .../Core/HW/EXI/{BBA-TAP => BBA}/TAP_Win32.h | 0 Source/Core/Core/HW/EXI/EXI_Device.cpp | 2 +- .../Core/Core/HW/EXI/EXI_DeviceEthernet.cpp | 38 ++++---- Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h | 87 ++++++++++++++----- .../Core/DolphinQt/Settings/GameCubePane.cpp | 11 ++- 13 files changed, 154 insertions(+), 95 deletions(-) rename Source/Core/Core/HW/EXI/{BBA-TAP => BBA}/TAP_Apple.cpp (69%) rename Source/Core/Core/HW/EXI/{BBA-TAP => BBA}/TAP_Unix.cpp (80%) rename Source/Core/Core/HW/EXI/{BBA-TAP => BBA}/TAP_Win32.cpp (92%) rename Source/Core/Core/HW/EXI/{BBA-TAP => BBA}/TAP_Win32.h (100%) diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 2b7c141e68..01f5591258 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -591,8 +591,8 @@ endif() if(WIN32) target_sources(core PRIVATE - HW/EXI/BBA-TAP/TAP_Win32.cpp - HW/EXI/BBA-TAP/TAP_Win32.h + HW/EXI/BBA/TAP_Win32.cpp + HW/EXI/BBA/TAP_Win32.h HW/WiimoteReal/IOWin.cpp HW/WiimoteReal/IOWin.h ) @@ -605,7 +605,7 @@ if(WIN32) target_compile_definitions(core PRIVATE "-D_WINSOCK_DEPRECATED_NO_WARNINGS") elseif(APPLE) target_sources(core PRIVATE - HW/EXI/BBA-TAP/TAP_Apple.cpp + HW/EXI/BBA/TAP_Apple.cpp HW/WiimoteReal/IOdarwin.h HW/WiimoteReal/IOdarwin_private.h HW/WiimoteReal/IOdarwin.mm @@ -613,7 +613,7 @@ elseif(APPLE) target_link_libraries(core PUBLIC ${IOB_LIBRARY}) elseif(UNIX) target_sources(core PRIVATE - HW/EXI/BBA-TAP/TAP_Unix.cpp + HW/EXI/BBA/TAP_Unix.cpp ) if(ANDROID) target_sources(core PRIVATE diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index 092d45b1da..3f3f1c238f 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -223,7 +223,7 @@ void SConfig::SaveCoreSettings(IniFile& ini) core->Set("SlotA", m_EXIDevice[0]); core->Set("SlotB", m_EXIDevice[1]); core->Set("SerialPort1", m_EXIDevice[2]); - core->Set("BBA_MAC", m_bba_mac); + core->Set("BBA_TAP_MAC", m_bba_tap_mac); for (int i = 0; i < SerialInterface::MAX_SI_CHANNELS; ++i) { core->Set(fmt::format("SIDevice{}", i), m_SIDevice[i]); @@ -488,7 +488,7 @@ void SConfig::LoadCoreSettings(IniFile& ini) core->Get("SlotA", (int*)&m_EXIDevice[0], ExpansionInterface::EXIDEVICE_MEMORYCARDFOLDER); core->Get("SlotB", (int*)&m_EXIDevice[1], ExpansionInterface::EXIDEVICE_NONE); core->Get("SerialPort1", (int*)&m_EXIDevice[2], ExpansionInterface::EXIDEVICE_NONE); - core->Get("BBA_MAC", &m_bba_mac); + core->Get("BBA_TAP_MAC", &m_bba_tap_mac); for (size_t i = 0; i < std::size(m_SIDevice); ++i) { core->Get(fmt::format("SIDevice{}", i), &m_SIDevice[i], diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h index 4341f88196..9c60dd50a1 100644 --- a/Source/Core/Core/ConfigManager.h +++ b/Source/Core/Core/ConfigManager.h @@ -226,7 +226,8 @@ struct SConfig std::string m_strGbaCartB; ExpansionInterface::TEXIDevices m_EXIDevice[3]; SerialInterface::SIDevices m_SIDevice[4]; - std::string m_bba_mac; + + std::string m_bba_tap_mac; // interface language std::string m_InterfaceLanguage; diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index deeab45e20..4828f5d1dc 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -1,4 +1,4 @@ - + @@ -140,7 +140,7 @@ - + @@ -498,7 +498,7 @@ - + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 7fe19d606b..9f64208ece 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -993,6 +993,9 @@ PowerPC\Jit64 + + HW %28Flipper/Hollywood%29\EXI - Expansion Interface\BBA + @@ -1740,6 +1743,9 @@ PowerPC\JitArmCommon + + HW %28Flipper/Hollywood%29\EXI - Expansion Interface\BBA + diff --git a/Source/Core/Core/HW/EXI/BBA-TAP/TAP_Apple.cpp b/Source/Core/Core/HW/EXI/BBA/TAP_Apple.cpp similarity index 69% rename from Source/Core/Core/HW/EXI/BBA-TAP/TAP_Apple.cpp rename to Source/Core/Core/HW/EXI/BBA/TAP_Apple.cpp index 6dbed04f20..4024447893 100644 --- a/Source/Core/Core/HW/EXI/BBA-TAP/TAP_Apple.cpp +++ b/Source/Core/Core/HW/EXI/BBA/TAP_Apple.cpp @@ -12,7 +12,7 @@ namespace ExpansionInterface { -bool CEXIETHERNET::Activate() +bool CEXIETHERNET::TAPNetworkInterface::Activate() { if (IsActivated()) return true; @@ -30,7 +30,7 @@ bool CEXIETHERNET::Activate() return RecvInit(); } -void CEXIETHERNET::Deactivate() +void CEXIETHERNET::TAPNetworkInterface::Deactivate() { close(fd); fd = -1; @@ -41,12 +41,12 @@ void CEXIETHERNET::Deactivate() readThread.join(); } -bool CEXIETHERNET::IsActivated() +bool CEXIETHERNET::TAPNetworkInterface::IsActivated() { return fd != -1; } -bool CEXIETHERNET::SendFrame(const u8* frame, u32 size) +bool CEXIETHERNET::TAPNetworkInterface::SendFrame(const u8* frame, u32 size) { INFO_LOG(SP1, "SendFrame %x\n%s", size, ArrayToString(frame, size, 0x10).c_str()); @@ -58,12 +58,12 @@ bool CEXIETHERNET::SendFrame(const u8* frame, u32 size) } else { - SendComplete(); + m_eth_ref->SendComplete(); return true; } } -void CEXIETHERNET::ReadThreadHandler(CEXIETHERNET* self) +void CEXIETHERNET::TAPNetworkInterface::ReadThreadHandler(TAPNetworkInterface* self) { while (!self->readThreadShutdown.IsSet()) { @@ -77,7 +77,7 @@ void CEXIETHERNET::ReadThreadHandler(CEXIETHERNET* self) if (select(self->fd + 1, &rfds, nullptr, nullptr, &timeout) <= 0) continue; - int readBytes = read(self->fd, self->mRecvBuffer.get(), BBA_RECV_SIZE); + int readBytes = read(self->fd, self->m_eth_ref->mRecvBuffer.get(), BBA_RECV_SIZE); if (readBytes < 0) { ERROR_LOG(SP1, "Failed to read from BBA, err=%d", readBytes); @@ -85,25 +85,25 @@ void CEXIETHERNET::ReadThreadHandler(CEXIETHERNET* self) else if (self->readEnabled.IsSet()) { INFO_LOG(SP1, "Read data: %s", - ArrayToString(self->mRecvBuffer.get(), readBytes, 0x10).c_str()); - self->mRecvBufferLength = readBytes; - self->RecvHandlePacket(); + ArrayToString(self->m_eth_ref->mRecvBuffer.get(), readBytes, 0x10).c_str()); + self->m_eth_ref->mRecvBufferLength = readBytes; + self->m_eth_ref->RecvHandlePacket(); } } } -bool CEXIETHERNET::RecvInit() +bool CEXIETHERNET::TAPNetworkInterface::RecvInit() { readThread = std::thread(ReadThreadHandler, this); return true; } -void CEXIETHERNET::RecvStart() +void CEXIETHERNET::TAPNetworkInterface::RecvStart() { readEnabled.Set(); } -void CEXIETHERNET::RecvStop() +void CEXIETHERNET::TAPNetworkInterface::RecvStop() { readEnabled.Clear(); } diff --git a/Source/Core/Core/HW/EXI/BBA-TAP/TAP_Unix.cpp b/Source/Core/Core/HW/EXI/BBA/TAP_Unix.cpp similarity index 80% rename from Source/Core/Core/HW/EXI/BBA-TAP/TAP_Unix.cpp rename to Source/Core/Core/HW/EXI/BBA/TAP_Unix.cpp index f13db04b65..4911f370bb 100644 --- a/Source/Core/Core/HW/EXI/BBA-TAP/TAP_Unix.cpp +++ b/Source/Core/Core/HW/EXI/BBA/TAP_Unix.cpp @@ -28,7 +28,7 @@ namespace ExpansionInterface #define NOTIMPLEMENTED(Name) \ NOTICE_LOG(SP1, "CEXIETHERNET::%s not implemented for your UNIX", Name); -bool CEXIETHERNET::Activate() +bool CEXIETHERNET::TAPNetworkInterface::Activate() { #ifdef __linux__ if (IsActivated()) @@ -50,7 +50,7 @@ bool CEXIETHERNET::Activate() const int MAX_INTERFACES = 32; for (int i = 0; i < MAX_INTERFACES; ++i) { - strncpy(ifr.ifr_name, StringFromFormat("Dolphin%d", i).c_str(), IFNAMSIZ - 1); + strncpy(ifr.ifr_name, StringFromFormat("Dolphin%d", i).c_str(), IFNAMSIZ); int err; if ((err = ioctl(fd, TUNSETIFF, (void*)&ifr)) < 0) @@ -78,7 +78,7 @@ bool CEXIETHERNET::Activate() #endif } -void CEXIETHERNET::Deactivate() +void CEXIETHERNET::TAPNetworkInterface::Deactivate() { #ifdef __linux__ close(fd); @@ -93,7 +93,7 @@ void CEXIETHERNET::Deactivate() #endif } -bool CEXIETHERNET::IsActivated() +bool CEXIETHERNET::TAPNetworkInterface::IsActivated() { #ifdef __linux__ return fd != -1 ? true : false; @@ -102,7 +102,7 @@ bool CEXIETHERNET::IsActivated() #endif } -bool CEXIETHERNET::SendFrame(const u8* frame, u32 size) +bool CEXIETHERNET::TAPNetworkInterface::SendFrame(const u8* frame, u32 size) { #ifdef __linux__ DEBUG_LOG(SP1, "SendFrame %x\n%s", size, ArrayToString(frame, size, 0x10).c_str()); @@ -115,7 +115,7 @@ bool CEXIETHERNET::SendFrame(const u8* frame, u32 size) } else { - SendComplete(); + m_eth_ref->SendComplete(); return true; } #else @@ -125,7 +125,7 @@ bool CEXIETHERNET::SendFrame(const u8* frame, u32 size) } #ifdef __linux__ -void CEXIETHERNET::ReadThreadHandler(CEXIETHERNET* self) +void CEXIETHERNET::TAPNetworkInterface::ReadThreadHandler(TAPNetworkInterface* self) { while (!self->readThreadShutdown.IsSet()) { @@ -139,7 +139,7 @@ void CEXIETHERNET::ReadThreadHandler(CEXIETHERNET* self) if (select(self->fd + 1, &rfds, nullptr, nullptr, &timeout) <= 0) continue; - int readBytes = read(self->fd, self->mRecvBuffer.get(), BBA_RECV_SIZE); + int readBytes = read(self->fd, self->m_eth_ref->mRecvBuffer.get(), BBA_RECV_SIZE); if (readBytes < 0) { ERROR_LOG(SP1, "Failed to read from BBA, err=%d", readBytes); @@ -147,15 +147,15 @@ void CEXIETHERNET::ReadThreadHandler(CEXIETHERNET* self) else if (self->readEnabled.IsSet()) { DEBUG_LOG(SP1, "Read data: %s", - ArrayToString(self->mRecvBuffer.get(), readBytes, 0x10).c_str()); - self->mRecvBufferLength = readBytes; - self->RecvHandlePacket(); + ArrayToString(self->m_eth_ref->mRecvBuffer.get(), readBytes, 0x10).c_str()); + self->m_eth_ref->mRecvBufferLength = readBytes; + self->m_eth_ref->RecvHandlePacket(); } } } #endif -bool CEXIETHERNET::RecvInit() +bool CEXIETHERNET::TAPNetworkInterface::RecvInit() { #ifdef __linux__ readThread = std::thread(ReadThreadHandler, this); @@ -166,7 +166,7 @@ bool CEXIETHERNET::RecvInit() #endif } -void CEXIETHERNET::RecvStart() +void CEXIETHERNET::TAPNetworkInterface::RecvStart() { #ifdef __linux__ readEnabled.Set(); @@ -175,7 +175,7 @@ void CEXIETHERNET::RecvStart() #endif } -void CEXIETHERNET::RecvStop() +void CEXIETHERNET::TAPNetworkInterface::RecvStop() { #ifdef __linux__ readEnabled.Clear(); diff --git a/Source/Core/Core/HW/EXI/BBA-TAP/TAP_Win32.cpp b/Source/Core/Core/HW/EXI/BBA/TAP_Win32.cpp similarity index 92% rename from Source/Core/Core/HW/EXI/BBA-TAP/TAP_Win32.cpp rename to Source/Core/Core/HW/EXI/BBA/TAP_Win32.cpp index 3e8b4d5442..a421484005 100644 --- a/Source/Core/Core/HW/EXI/BBA-TAP/TAP_Win32.cpp +++ b/Source/Core/Core/HW/EXI/BBA/TAP_Win32.cpp @@ -2,7 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -#include "Core/HW/EXI/BBA-TAP/TAP_Win32.h" +#include "Core/HW/EXI/BBA/TAP_Win32.h" #include "Common/Assert.h" #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" @@ -167,7 +167,7 @@ bool OpenTAP(HANDLE& adapter, const std::basic_string& device_guid) namespace ExpansionInterface { -bool CEXIETHERNET::Activate() +bool CEXIETHERNET::TAPNetworkInterface::Activate() { if (IsActivated()) return true; @@ -233,7 +233,7 @@ bool CEXIETHERNET::Activate() return RecvInit(); } -void CEXIETHERNET::Deactivate() +void CEXIETHERNET::TAPNetworkInterface::Deactivate() { if (!IsActivated()) return; @@ -258,19 +258,19 @@ void CEXIETHERNET::Deactivate() memset(&mWriteOverlapped, 0, sizeof(mWriteOverlapped)); } -bool CEXIETHERNET::IsActivated() +bool CEXIETHERNET::TAPNetworkInterface::IsActivated() { return mHAdapter != INVALID_HANDLE_VALUE; } -void CEXIETHERNET::ReadThreadHandler(CEXIETHERNET* self) +void CEXIETHERNET::TAPNetworkInterface::ReadThreadHandler(TAPNetworkInterface* self) { while (!self->readThreadShutdown.IsSet()) { DWORD transferred; // Read from TAP into internal buffer. - if (ReadFile(self->mHAdapter, self->mRecvBuffer.get(), BBA_RECV_SIZE, &transferred, + if (ReadFile(self->mHAdapter, self->m_eth_ref->mRecvBuffer.get(), BBA_RECV_SIZE, &transferred, &self->mReadOverlapped)) { // Returning immediately is not likely to happen, but if so, reset the event state manually. @@ -300,16 +300,16 @@ void CEXIETHERNET::ReadThreadHandler(CEXIETHERNET* self) // Copy to BBA buffer, and fire interrupt if enabled. DEBUG_LOG(SP1, "Received %u bytes:\n %s", transferred, - ArrayToString(self->mRecvBuffer.get(), transferred, 0x10).c_str()); + ArrayToString(self->m_eth_ref->mRecvBuffer.get(), transferred, 0x10).c_str()); if (self->readEnabled.IsSet()) { - self->mRecvBufferLength = transferred; - self->RecvHandlePacket(); + self->m_eth_ref->mRecvBufferLength = transferred; + self->m_eth_ref->RecvHandlePacket(); } } } -bool CEXIETHERNET::SendFrame(const u8* frame, u32 size) +bool CEXIETHERNET::TAPNetworkInterface::SendFrame(const u8* frame, u32 size) { DEBUG_LOG(SP1, "SendFrame %u bytes:\n%s", size, ArrayToString(frame, size, 0x10).c_str()); @@ -345,22 +345,22 @@ bool CEXIETHERNET::SendFrame(const u8* frame, u32 size) } // Always report the packet as being sent successfully, even though it might be a lie - SendComplete(); + m_eth_ref->SendComplete(); return true; } -bool CEXIETHERNET::RecvInit() +bool CEXIETHERNET::TAPNetworkInterface::RecvInit() { readThread = std::thread(ReadThreadHandler, this); return true; } -void CEXIETHERNET::RecvStart() +void CEXIETHERNET::TAPNetworkInterface::RecvStart() { readEnabled.Set(); } -void CEXIETHERNET::RecvStop() +void CEXIETHERNET::TAPNetworkInterface::RecvStop() { readEnabled.Clear(); } diff --git a/Source/Core/Core/HW/EXI/BBA-TAP/TAP_Win32.h b/Source/Core/Core/HW/EXI/BBA/TAP_Win32.h similarity index 100% rename from Source/Core/Core/HW/EXI/BBA-TAP/TAP_Win32.h rename to Source/Core/Core/HW/EXI/BBA/TAP_Win32.h diff --git a/Source/Core/Core/HW/EXI/EXI_Device.cpp b/Source/Core/Core/HW/EXI/EXI_Device.cpp index be4a7029c8..9995d6c156 100644 --- a/Source/Core/Core/HW/EXI/EXI_Device.cpp +++ b/Source/Core/Core/HW/EXI/EXI_Device.cpp @@ -132,7 +132,7 @@ std::unique_ptr EXIDevice_Create(const TEXIDevices device_type, cons break; case EXIDEVICE_ETH: - result = std::make_unique(); + result = std::make_unique(BBADeviceType::BBA_TAP); break; case EXIDEVICE_GECKO: diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.cpp index 1972e3e3ea..d303dbcdfb 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.cpp @@ -23,17 +23,11 @@ namespace ExpansionInterface // Multiple parts of this implementation depend on Dolphin // being compiled for a little endian host. -CEXIETHERNET::CEXIETHERNET() +CEXIETHERNET::CEXIETHERNET(BBADeviceType type) { - tx_fifo = std::make_unique(BBA_TXFIFO_SIZE); - mBbaMem = std::make_unique(BBA_MEM_SIZE); - mRecvBuffer = std::make_unique(BBA_RECV_SIZE); - - MXHardReset(); - - // Parse MAC address from config, and generate a new one if it doesn't + // Parse BBA (TAP) MAC address from config, and generate a new one if it doesn't // exist or can't be parsed. - std::string& mac_addr_setting = SConfig::GetInstance().m_bba_mac; + std::string& mac_addr_setting = SConfig::GetInstance().m_bba_tap_mac; std::optional mac_addr = Common::StringToMacAddress(mac_addr_setting); if (!mac_addr) @@ -43,6 +37,20 @@ CEXIETHERNET::CEXIETHERNET() SConfig::GetInstance().SaveSettings(); } + switch (type) + { + case BBADeviceType::BBA_TAP: + network_interface = std::make_unique(this); + INFO_LOG(SP1, "Created TAP physical network interface."); + break; + } + + tx_fifo = std::make_unique(BBA_TXFIFO_SIZE); + mBbaMem = std::make_unique(BBA_MEM_SIZE); + mRecvBuffer = std::make_unique(BBA_RECV_SIZE); + + MXHardReset(); + const auto& mac = mac_addr.value(); memcpy(&mBbaMem[BBA_NAFR_PAR0], mac.data(), mac.size()); @@ -52,7 +60,7 @@ CEXIETHERNET::CEXIETHERNET() CEXIETHERNET::~CEXIETHERNET() { - Deactivate(); + network_interface->Deactivate(); } void CEXIETHERNET::SetCS(int cs) @@ -303,7 +311,7 @@ void CEXIETHERNET::MXCommandHandler(u32 data, u32 size) { INFO_LOG(SP1, "Software reset"); // MXSoftReset(); - Activate(); + network_interface->Activate(); } if ((mBbaMem[BBA_NCRA] & NCRA_SR) ^ (data & NCRA_SR)) @@ -311,9 +319,9 @@ void CEXIETHERNET::MXCommandHandler(u32 data, u32 size) DEBUG_LOG(SP1, "%s rx", (data & NCRA_SR) ? "start" : "stop"); if (data & NCRA_SR) - RecvStart(); + network_interface->RecvStart(); else - RecvStop(); + network_interface->RecvStop(); } // Only start transfer if there isn't one currently running @@ -386,7 +394,7 @@ void CEXIETHERNET::DirectFIFOWrite(const u8* data, u32 size) void CEXIETHERNET::SendFromDirectFIFO() { - SendFrame(tx_fifo.get(), *(u16*)&mBbaMem[BBA_TXFIFOCNT]); + network_interface->SendFrame(tx_fifo.get(), *(u16*)&mBbaMem[BBA_TXFIFOCNT]); } void CEXIETHERNET::SendFromPacketBuffer() @@ -579,7 +587,7 @@ bool CEXIETHERNET::RecvHandlePacket() wait_for_next: if (mBbaMem[BBA_NCRA] & NCRA_SR) - RecvStart(); + network_interface->RecvStart(); return true; } diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h b/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h index 38a27bd7e0..a4d7efa789 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h +++ b/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h @@ -196,10 +196,15 @@ enum RecvStatus #define BBA_RECV_SIZE 0x800 +enum class BBADeviceType +{ + BBA_TAP, +}; + class CEXIETHERNET : public IEXIDevice { public: - CEXIETHERNET(); + explicit CEXIETHERNET(BBADeviceType type); virtual ~CEXIETHERNET(); void SetCS(int cs) override; bool IsPresent() const override; @@ -297,34 +302,70 @@ private: std::unique_ptr mBbaMem; std::unique_ptr tx_fifo; - // TAP interface - static void ReadThreadHandler(CEXIETHERNET* self); - bool Activate(); - void Deactivate(); - bool IsActivated(); - bool SendFrame(const u8* frame, u32 size); - bool RecvInit(); - void RecvStart(); - void RecvStop(); + class NetworkInterface + { + protected: + CEXIETHERNET* m_eth_ref = nullptr; + explicit NetworkInterface(CEXIETHERNET* eth_ref) : m_eth_ref{eth_ref} {} - std::unique_ptr mRecvBuffer; - u32 mRecvBufferLength = 0; + public: + virtual bool Activate() { return false; } + virtual void Deactivate() {} + virtual bool IsActivated() { return false; } + virtual bool SendFrame(const u8* frame, u32 size) { return false; } + virtual bool RecvInit() { return false; } + virtual void RecvStart() {} + virtual void RecvStop() {} -#if defined(_WIN32) - HANDLE mHAdapter = INVALID_HANDLE_VALUE; - OVERLAPPED mReadOverlapped = {}; - OVERLAPPED mWriteOverlapped = {}; - std::vector mWriteBuffer; - bool mWritePending = false; -#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) - int fd = -1; + virtual ~NetworkInterface() = default; + }; + + class TAPNetworkInterface : public NetworkInterface + { + public: + explicit TAPNetworkInterface(CEXIETHERNET* eth_ref) : NetworkInterface(eth_ref) + { + } + + public: + bool Activate() override; + void Deactivate() override; + bool IsActivated() override; + bool SendFrame(const u8* frame, u32 size) override; + bool RecvInit() override; + void RecvStart() override; + void RecvStop() override; + + private: +#if defined(WIN32) || defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ + defined(__OpenBSD__) + std::thread readThread; + Common::Flag readEnabled; + Common::Flag readThreadShutdown; + static void ReadThreadHandler(TAPNetworkInterface* self); #endif +#if defined(_WIN32) + HANDLE mHAdapter = INVALID_HANDLE_VALUE; + OVERLAPPED mReadOverlapped = {}; + OVERLAPPED mWriteOverlapped = {}; + std::vector mWriteBuffer; + bool mWritePending = false; +#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) + int fd = -1; +#endif + }; #if defined(WIN32) || defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ defined(__OpenBSD__) - std::thread readThread; - Common::Flag readEnabled; - Common::Flag readThreadShutdown; + std::thread readThread; + Common::Flag readEnabled; + Common::Flag readThreadShutdown; #endif +}; + + std::unique_ptr network_interface; + + std::unique_ptr mRecvBuffer; + u32 mRecvBufferLength = 0; }; } // namespace ExpansionInterface diff --git a/Source/Core/DolphinQt/Settings/GameCubePane.cpp b/Source/Core/DolphinQt/Settings/GameCubePane.cpp index 6700984ac7..d17009290f 100644 --- a/Source/Core/DolphinQt/Settings/GameCubePane.cpp +++ b/Source/Core/DolphinQt/Settings/GameCubePane.cpp @@ -103,7 +103,7 @@ void GameCubePane::CreateWidgets() 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"), ExpansionInterface::EXIDEVICE_ETH)}) + std::make_pair(tr("Broadband Adapter (TAP)"), ExpansionInterface::EXIDEVICE_ETH), { m_slot_combos[2]->addItem(entry.first, entry.second); } @@ -186,10 +186,13 @@ void GameCubePane::OnConfigPressed(int slot) { bool ok; const auto new_mac = QInputDialog::getText( - this, tr("Broadband Adapter MAC address"), tr("Enter new Broadband Adapter MAC address:"), - QLineEdit::Normal, QString::fromStdString(SConfig::GetInstance().m_bba_mac), &ok); + this, tr("Broadband Adapter (TAP) MAC address"), + tr("Enter new Broadband Adapter (TAP) MAC address:"), QLineEdit::Normal, + QString::fromStdString(SConfig::GetInstance().m_bba_tap_mac), &ok); if (ok) - SConfig::GetInstance().m_bba_mac = new_mac.toStdString(); + SConfig::GetInstance().m_bba_tap_mac = new_mac.toStdString(); + return; + } return; } default: From 067667fda5fd17780c22d9472d3a24d183372028 Mon Sep 17 00:00:00 2001 From: CrunchBite Date: Sun, 7 Jun 2020 23:25:45 -0400 Subject: [PATCH 2/2] Add XLink Kai UDP BBA --- Source/Core/Core/CMakeLists.txt | 3 + Source/Core/Core/ConfigManager.cpp | 8 +- Source/Core/Core/ConfigManager.h | 4 +- Source/Core/Core/Core.vcxproj | 1 + Source/Core/Core/Core.vcxproj.filters | 3 + Source/Core/Core/HW/EXI/BBA/XLINK_KAI_BBA.cpp | 306 ++++++++++++++++++ Source/Core/Core/HW/EXI/EXI_Device.cpp | 6 +- Source/Core/Core/HW/EXI/EXI_Device.h | 1 + .../Core/Core/HW/EXI/EXI_DeviceEthernet.cpp | 47 ++- Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h | 50 ++- .../Core/DolphinQt/Settings/GameCubePane.cpp | 24 +- 11 files changed, 425 insertions(+), 28 deletions(-) create mode 100644 Source/Core/Core/HW/EXI/BBA/XLINK_KAI_BBA.cpp diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 01f5591258..a7c06b71bb 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -593,6 +593,7 @@ if(WIN32) target_sources(core PRIVATE HW/EXI/BBA/TAP_Win32.cpp HW/EXI/BBA/TAP_Win32.h + HW/EXI/BBA/XLINK_KAI_BBA.cpp HW/WiimoteReal/IOWin.cpp HW/WiimoteReal/IOWin.h ) @@ -606,6 +607,7 @@ if(WIN32) elseif(APPLE) target_sources(core PRIVATE HW/EXI/BBA/TAP_Apple.cpp + HW/EXI/BBA/XLINK_KAI_BBA.cpp HW/WiimoteReal/IOdarwin.h HW/WiimoteReal/IOdarwin_private.h HW/WiimoteReal/IOdarwin.mm @@ -614,6 +616,7 @@ elseif(APPLE) elseif(UNIX) target_sources(core PRIVATE HW/EXI/BBA/TAP_Unix.cpp + HW/EXI/BBA/XLINK_KAI_BBA.cpp ) if(ANDROID) target_sources(core PRIVATE diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index 3f3f1c238f..fa5e181efe 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -223,7 +223,9 @@ void SConfig::SaveCoreSettings(IniFile& ini) core->Set("SlotA", m_EXIDevice[0]); core->Set("SlotB", m_EXIDevice[1]); core->Set("SerialPort1", m_EXIDevice[2]); - core->Set("BBA_TAP_MAC", m_bba_tap_mac); + core->Set("BBA_MAC", m_bba_mac); + core->Set("BBA_XLINK_IP", m_bba_xlink_ip); + core->Set("BBA_XLINK_CHAT_OSD", m_bba_xlink_chat_osd); for (int i = 0; i < SerialInterface::MAX_SI_CHANNELS; ++i) { core->Set(fmt::format("SIDevice{}", i), m_SIDevice[i]); @@ -488,7 +490,9 @@ void SConfig::LoadCoreSettings(IniFile& ini) core->Get("SlotA", (int*)&m_EXIDevice[0], ExpansionInterface::EXIDEVICE_MEMORYCARDFOLDER); core->Get("SlotB", (int*)&m_EXIDevice[1], ExpansionInterface::EXIDEVICE_NONE); core->Get("SerialPort1", (int*)&m_EXIDevice[2], ExpansionInterface::EXIDEVICE_NONE); - core->Get("BBA_TAP_MAC", &m_bba_tap_mac); + core->Get("BBA_MAC", &m_bba_mac); + core->Get("BBA_XLINK_IP", &m_bba_xlink_ip, "127.0.0.1"); + core->Get("BBA_XLINK_CHAT_OSD", &m_bba_xlink_chat_osd, true); for (size_t i = 0; i < std::size(m_SIDevice); ++i) { core->Get(fmt::format("SIDevice{}", i), &m_SIDevice[i], diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h index 9c60dd50a1..58073b8d42 100644 --- a/Source/Core/Core/ConfigManager.h +++ b/Source/Core/Core/ConfigManager.h @@ -227,7 +227,9 @@ struct SConfig ExpansionInterface::TEXIDevices m_EXIDevice[3]; SerialInterface::SIDevices m_SIDevice[4]; - std::string m_bba_tap_mac; + std::string m_bba_mac; + std::string m_bba_xlink_ip; + bool m_bba_xlink_chat_osd = true; // interface language std::string m_InterfaceLanguage; diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 4828f5d1dc..3b192dd8ff 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -141,6 +141,7 @@ + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 9f64208ece..d13d032ac1 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -993,6 +993,9 @@ PowerPC\Jit64 + + HW %28Flipper/Hollywood%29\EXI - Expansion Interface\BBA + HW %28Flipper/Hollywood%29\EXI - Expansion Interface\BBA diff --git a/Source/Core/Core/HW/EXI/BBA/XLINK_KAI_BBA.cpp b/Source/Core/Core/HW/EXI/BBA/XLINK_KAI_BBA.cpp new file mode 100644 index 0000000000..d02c139f31 --- /dev/null +++ b/Source/Core/Core/HW/EXI/BBA/XLINK_KAI_BBA.cpp @@ -0,0 +1,306 @@ +// Copyright 2008 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Common/Assert.h" +#include "Common/Logging/Log.h" +#include "Common/MsgHandler.h" +#include "Common/StringUtil.h" +#include "Core/HW/EXI/EXI_Device.h" +#include "Core/HW/EXI/EXI_DeviceEthernet.h" + +#include "VideoCommon/OnScreenDisplay.h" + +#include + +#include + +// BBA implementation with UDP interface to XLink Kai PC/MAC/RaspberryPi client +// For more information please see: https://www.teamxlink.co.uk/wiki/Emulator_Integration_Protocol +// Still have questions? Please email crunchbite@teamxlink.co.uk +// When editing this file please maintain proper capitalization of "XLink Kai" + +namespace ExpansionInterface +{ +bool CEXIETHERNET::XLinkNetworkInterface::Activate() +{ + if (IsActivated()) + return true; + + if (m_sf_socket.bind(sf::Socket::AnyPort) != sf::Socket::Done) + { + ERROR_LOG(SP1, "Couldn't open XLink Kai UDP socket, unable to initialize BBA"); + return false; + } + + m_sf_recipient_ip = m_dest_ip.c_str(); + + // Send connect command with unique local name + // connect;locally_unique_name;emulator_name;optional_padding + u8 buffer[255] = {}; + std::string cmd = + "connect;" + m_client_identifier + ";dolphin;000000000000000000000000000000000000000000"; + + const auto size = u32(cmd.length()); + memmove(buffer, cmd.c_str(), size); + + DEBUG_LOG(SP1, "SendCommandPayload %x\n%s", size, ArrayToString(buffer, size, 0x10).c_str()); + + if (m_sf_socket.send(buffer, size, m_sf_recipient_ip, m_dest_port) != sf::Socket::Done) + { + ERROR_LOG(SP1, "Activate(): failed to send connect message to XLink Kai client"); + } + + INFO_LOG(SP1, "BBA initialized."); + + return RecvInit(); +} + +void CEXIETHERNET::XLinkNetworkInterface::Deactivate() +{ + // Send d; to tell XLink we want to disconnect cleanly + // disconnect;optional_locally_unique_name;optional_padding + std::string cmd = + "disconnect;" + m_client_identifier + ";0000000000000000000000000000000000000000000"; + const auto size = u32(cmd.length()); + u8 buffer[255] = {}; + memmove(buffer, cmd.c_str(), size); + + DEBUG_LOG(SP1, "SendCommandPayload %x\n%s", size, ArrayToString(buffer, size, 0x10).c_str()); + + if (m_sf_socket.send(buffer, size, m_sf_recipient_ip, m_dest_port) != sf::Socket::Done) + { + ERROR_LOG(SP1, "Deactivate(): failed to send disconnect message to XLink Kai client"); + } + + NOTICE_LOG(SP1, "XLink Kai BBA deactivated"); + + m_bba_link_up = false; + + // Disable socket blocking + m_sf_socket.setBlocking(false); + + // Set flags for clean shutdown of the read thread + m_read_enabled.Clear(); + m_read_thread_shutdown.Set(); + + // Detach the thread and let it die on its own (leaks ~9kb of memory) + m_read_thread.detach(); + + // Close the socket + m_sf_socket.unbind(); +} + +bool CEXIETHERNET::XLinkNetworkInterface::IsActivated() +{ + return m_sf_socket.getLocalPort() > 0; +} + +bool CEXIETHERNET::XLinkNetworkInterface::SendFrame(const u8* frame, u32 size) +{ + // Is the BBA actually connected? + if (!m_bba_link_up) + { + // Has the user been told the connection failed? + if (!m_bba_failure_notified) + { + OSD::AddMessage("XLink Kai BBA not connected", 30000); + m_bba_failure_notified = true; + } + + // Return true because this isnt an error dolphin needs to handle + return true; + } + + // Overwrite buffer and add header + memmove(m_out_frame, "e;e;", 4); + memmove(m_out_frame + 4, frame, size); + size += 4; + + // Only uncomment for debugging, the performance hit is too big otherwise + // INFO_LOG(SP1, "SendFrame %x\n%s", size, ArrayToString(m_out_frame, size, 0x10).c_str()); + + if (m_sf_socket.send(m_out_frame, size, m_sf_recipient_ip, m_dest_port) != sf::Socket::Done) + { + ERROR_LOG(SP1, "SendFrame(): expected to write %u bytes, but failed, errno %d", size, errno); + return false; + } + else + { + m_eth_ref->SendComplete(); + return true; + } +} + +void CEXIETHERNET::XLinkNetworkInterface::ReadThreadHandler( + CEXIETHERNET::XLinkNetworkInterface* self) +{ + sf::IpAddress sender; + u16 port; + + while (!self->m_read_thread_shutdown.IsSet()) + { + if (!self->IsActivated()) + break; + + // XLink supports jumboframes but Gamecube BBA does not. We need to support jumbo frames + // *here* because XLink *could* send one + std::size_t bytes_read = 0; + if (self->m_sf_socket.receive(self->m_in_frame, std::size(self->m_in_frame), bytes_read, sender, + port) != sf::Socket::Done && + self->m_bba_link_up) + { + ERROR_LOG(SP1, "Failed to read from BBA, err=%zu", bytes_read); + } + + // Make sure *anything* was recieved before going any further + if (bytes_read < 1) + continue; + + // Did we get an ethernet frame? Copy the first 4 bytes to check the header + char temp_check[4]; + memmove(temp_check, self->m_in_frame, std::size(temp_check)); + + // Check for e;e; header; this indicates the received data is an ethernet frame + // e;e;raw_ethernet_frame_data + if (temp_check[2] == 'e' && temp_check[3] == ';') + { + // Is the frame larger than BBA_RECV_SIZE? + if ((bytes_read - 4) < BBA_RECV_SIZE) + { + // Copy payload into BBA buffer as an ethernet frame + memmove(self->m_eth_ref->mRecvBuffer.get(), self->m_in_frame + 4, bytes_read - 4); + + // Check the frame size again after the header is removed + if (bytes_read < 1) + { + ERROR_LOG(SP1, "Failed to read from BBA, err=%zu", bytes_read - 4); + } + else if (self->m_read_enabled.IsSet()) + { + // Only uncomment for debugging, the performance hit is too big otherwise + // DEBUG_LOG(SP1, "Read data: %s", ArrayToString(self->m_eth_ref->mRecvBuffer.get(), + // u32(bytes_read - 4), 0x10).c_str()); + self->m_eth_ref->mRecvBufferLength = u32(bytes_read - 4); + self->m_eth_ref->RecvHandlePacket(); + } + } + } + // Otherwise we recieved control data or junk + else + { + std::string control_msg(self->m_in_frame, self->m_in_frame + bytes_read); + INFO_LOG(SP1, "Received XLink Kai control data: %s", control_msg.c_str()); + + // connected;identifier; + if (StringBeginsWith(control_msg, "connected")) + { + NOTICE_LOG(SP1, "XLink Kai BBA connected"); + OSD::AddMessage("XLink Kai BBA connected", 4500); + + self->m_bba_link_up = true; + // TODO (in EXI_DeviceEthernet.cpp) bring the BBA link up here + + // Send any extra settings now + // Enable XLink chat messages in OSD if set + if (self->m_chat_osd_enabled) + { + constexpr std::string_view cmd = "setting;chat;true;"; + const auto size = u32(cmd.length()); + u8 buffer[255] = {}; + memmove(buffer, cmd.data(), size); + + DEBUG_LOG(SP1, "SendCommandPayload %x\n%s", size, + ArrayToString(buffer, size, 0x10).c_str()); + + if (self->m_sf_socket.send(buffer, size, self->m_sf_recipient_ip, self->m_dest_port) != + sf::Socket::Done) + { + ERROR_LOG(SP1, + "ReadThreadHandler(): failed to send setting message to XLink Kai client"); + } + } + } + // disconnected;optional_identifier;optional_message; + else if (StringBeginsWith(control_msg, "disconnected")) + { + NOTICE_LOG(SP1, "XLink Kai BBA disconnected"); + // Show OSD message for 15 seconds to make sure the user sees it + OSD::AddMessage("XLink Kai BBA disconnected", 15000); + + // TODO (TODO in EXI_DeviceEthernet.cpp) bring the BBA link down here + self->m_bba_link_up = false; + + // Disable socket blocking + self->m_sf_socket.setBlocking(false); + + // Shut down the read thread + self->m_read_enabled.Clear(); + self->m_read_thread_shutdown.Set(); + + // Close the socket + self->m_sf_socket.unbind(); + break; + } + // keepalive; + else if (StringBeginsWith(control_msg, "keepalive")) + { + DEBUG_LOG(SP1, "XLink Kai BBA keepalive"); + + // Only uncomment for debugging, just clogs the log otherwise + // INFO_LOG(SP1, "SendCommandPayload %x\n%s", 2, ArrayToString(m_in_frame, 2, + // 0x10).c_str()); + + // Reply (using the message that came in!) + if (self->m_sf_socket.send(self->m_in_frame, 10, self->m_sf_recipient_ip, + self->m_dest_port) != sf::Socket::Done) + { + ERROR_LOG(SP1, "ReadThreadHandler(): failed to reply to XLink Kai client keepalive"); + } + } + // message;message_text; + else if (StringBeginsWith(control_msg, "message")) + { + std::string msg = control_msg.substr(8, control_msg.length() - 1); + + NOTICE_LOG(SP1, "XLink Kai message: %s", msg.c_str()); + // Show OSD message for 15 seconds to make sure the user sees it + OSD::AddMessage(std::move(msg), 15000); + } + // chat;message_text; + else if (StringBeginsWith(control_msg, "chat")) + { + std::string msg = control_msg.substr(5, control_msg.length() - 1); + + NOTICE_LOG(SP1, "XLink Kai chat: %s", msg.c_str()); + OSD::AddMessage(std::move(msg), 5000); + } + // directmessage;message_text; + else if (StringBeginsWith(control_msg, "directmessage")) + { + std::string msg = control_msg.substr(14, control_msg.length() - 1); + + NOTICE_LOG(SP1, "XLink Kai direct message: %s", msg.c_str()); + OSD::AddMessage(std::move(msg), 5000); + } + // else junk/unsupported control message + } + } +} + +bool CEXIETHERNET::XLinkNetworkInterface::RecvInit() +{ + m_read_thread = std::thread(ReadThreadHandler, this); + return true; +} + +void CEXIETHERNET::XLinkNetworkInterface::RecvStart() +{ + m_read_enabled.Set(); +} + +void CEXIETHERNET::XLinkNetworkInterface::RecvStop() +{ + m_read_enabled.Clear(); +} +} // namespace ExpansionInterface diff --git a/Source/Core/Core/HW/EXI/EXI_Device.cpp b/Source/Core/Core/HW/EXI/EXI_Device.cpp index 9995d6c156..5fe23b7fdd 100644 --- a/Source/Core/Core/HW/EXI/EXI_Device.cpp +++ b/Source/Core/Core/HW/EXI/EXI_Device.cpp @@ -132,7 +132,11 @@ std::unique_ptr EXIDevice_Create(const TEXIDevices device_type, cons break; case EXIDEVICE_ETH: - result = std::make_unique(BBADeviceType::BBA_TAP); + result = std::make_unique(BBADeviceType::TAP); + break; + + case EXIDEVICE_ETHXLINK: + result = std::make_unique(BBADeviceType::XLINK); break; case EXIDEVICE_GECKO: diff --git a/Source/Core/Core/HW/EXI/EXI_Device.h b/Source/Core/Core/HW/EXI/EXI_Device.h index 0eb30904ca..bef1c53601 100644 --- a/Source/Core/Core/HW/EXI/EXI_Device.h +++ b/Source/Core/Core/HW/EXI/EXI_Device.h @@ -27,6 +27,7 @@ enum TEXIDevices : int // Converted to EXIDEVICE_MEMORYCARD internally. EXIDEVICE_MEMORYCARDFOLDER, EXIDEVICE_AGP, + EXIDEVICE_ETHXLINK, EXIDEVICE_NONE = 0xFF }; diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.cpp index d303dbcdfb..bba4f38791 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.cpp @@ -4,6 +4,7 @@ #include "Core/HW/EXI/EXI_DeviceEthernet.h" +#include #include #include #include @@ -12,6 +13,7 @@ #include "Common/CommonTypes.h" #include "Common/Logging/Log.h" #include "Common/Network.h" +#include "Common/StringUtil.h" #include "Core/ConfigManager.h" #include "Core/CoreTiming.h" #include "Core/HW/EXI/EXI.h" @@ -25,11 +27,14 @@ namespace ExpansionInterface CEXIETHERNET::CEXIETHERNET(BBADeviceType type) { - // Parse BBA (TAP) MAC address from config, and generate a new one if it doesn't + // Parse MAC address from config, and generate a new one if it doesn't // exist or can't be parsed. - std::string& mac_addr_setting = SConfig::GetInstance().m_bba_tap_mac; + std::string& mac_addr_setting = SConfig::GetInstance().m_bba_mac; std::optional mac_addr = Common::StringToMacAddress(mac_addr_setting); + std::transform(mac_addr_setting.begin(), mac_addr_setting.end(), mac_addr_setting.begin(), + [](unsigned char c) { return std::tolower(c); }); + if (!mac_addr) { mac_addr = Common::GenerateMacAddress(Common::MACConsumer::BBA); @@ -39,10 +44,32 @@ CEXIETHERNET::CEXIETHERNET(BBADeviceType type) switch (type) { - case BBADeviceType::BBA_TAP: - network_interface = std::make_unique(this); + case BBADeviceType::TAP: + m_network_interface = std::make_unique(this); INFO_LOG(SP1, "Created TAP physical network interface."); break; + case BBADeviceType::XLINK: + // TODO start BBA with network link down, bring it up after "connected" response from XLink + + // Perform sanity check on BBA MAC address, XLink requires the vendor OUI to be Nintendo's and + // to be one of the two used for the GameCube. + // Don't actually stop the BBA from initializing though + if (!StringBeginsWith(mac_addr_setting, "00:09:bf") && + !StringBeginsWith(mac_addr_setting, "00:17:ab")) + { + PanicAlertT("BBA MAC address %s invalid for XLink Kai. A valid Nintendo GameCube MAC address " + "must be used. Generate a new MAC address starting with 00:09:bf or 00:17:ab.", + mac_addr_setting.c_str()); + } + + // m_client_mdentifier should be unique per connected emulator from the XLink kai client's + // perspective so lets use "dolphin" + m_network_interface = std::make_unique( + this, SConfig::GetInstance().m_bba_xlink_ip, 34523, + "dolphin" + SConfig::GetInstance().m_bba_mac, SConfig::GetInstance().m_bba_xlink_chat_osd); + INFO_LOG(SP1, "Created XLink Kai BBA network interface connection to %s:34523", + SConfig::GetInstance().m_bba_xlink_ip.c_str()); + break; } tx_fifo = std::make_unique(BBA_TXFIFO_SIZE); @@ -60,7 +87,7 @@ CEXIETHERNET::CEXIETHERNET(BBADeviceType type) CEXIETHERNET::~CEXIETHERNET() { - network_interface->Deactivate(); + m_network_interface->Deactivate(); } void CEXIETHERNET::SetCS(int cs) @@ -311,7 +338,7 @@ void CEXIETHERNET::MXCommandHandler(u32 data, u32 size) { INFO_LOG(SP1, "Software reset"); // MXSoftReset(); - network_interface->Activate(); + m_network_interface->Activate(); } if ((mBbaMem[BBA_NCRA] & NCRA_SR) ^ (data & NCRA_SR)) @@ -319,9 +346,9 @@ void CEXIETHERNET::MXCommandHandler(u32 data, u32 size) DEBUG_LOG(SP1, "%s rx", (data & NCRA_SR) ? "start" : "stop"); if (data & NCRA_SR) - network_interface->RecvStart(); + m_network_interface->RecvStart(); else - network_interface->RecvStop(); + m_network_interface->RecvStop(); } // Only start transfer if there isn't one currently running @@ -394,7 +421,7 @@ void CEXIETHERNET::DirectFIFOWrite(const u8* data, u32 size) void CEXIETHERNET::SendFromDirectFIFO() { - network_interface->SendFrame(tx_fifo.get(), *(u16*)&mBbaMem[BBA_TXFIFOCNT]); + m_network_interface->SendFrame(tx_fifo.get(), *(u16*)&mBbaMem[BBA_TXFIFOCNT]); } void CEXIETHERNET::SendFromPacketBuffer() @@ -587,7 +614,7 @@ bool CEXIETHERNET::RecvHandlePacket() wait_for_next: if (mBbaMem[BBA_NCRA] & NCRA_SR) - network_interface->RecvStart(); + m_network_interface->RecvStart(); return true; } diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h b/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h index a4d7efa789..ed79b9a9b1 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h +++ b/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h @@ -12,6 +12,8 @@ #include #endif +#include + #include "Common/Flag.h" #include "Core/HW/EXI/EXI_Device.h" @@ -198,7 +200,8 @@ enum RecvStatus enum class BBADeviceType { - BBA_TAP, + TAP, + XLINK, }; class CEXIETHERNET : public IEXIDevice @@ -323,9 +326,7 @@ private: class TAPNetworkInterface : public NetworkInterface { public: - explicit TAPNetworkInterface(CEXIETHERNET* eth_ref) : NetworkInterface(eth_ref) - { - } + explicit TAPNetworkInterface(CEXIETHERNET* eth_ref) : NetworkInterface(eth_ref) {} public: bool Activate() override; @@ -355,15 +356,46 @@ private: #endif }; + class XLinkNetworkInterface : public NetworkInterface + { + public: + XLinkNetworkInterface(CEXIETHERNET* eth_ref, std::string dest_ip, int dest_port, + std::string identifier, bool chat_osd_enabled) + : NetworkInterface(eth_ref), m_dest_ip(std::move(dest_ip)), m_dest_port(dest_port), + m_client_identifier(identifier), m_chat_osd_enabled(chat_osd_enabled) + { + } + + public: + bool Activate() override; + void Deactivate() override; + bool IsActivated() override; + bool SendFrame(const u8* frame, u32 size) override; + bool RecvInit() override; + void RecvStart() override; + void RecvStop() override; + + private: + std::string m_dest_ip; + int m_dest_port; + std::string m_client_identifier; + bool m_chat_osd_enabled; + bool m_bba_link_up = false; + bool m_bba_failure_notified = false; #if defined(WIN32) || defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ defined(__OpenBSD__) - std::thread readThread; - Common::Flag readEnabled; - Common::Flag readThreadShutdown; + sf::UdpSocket m_sf_socket; + sf::IpAddress m_sf_recipient_ip; + char m_in_frame[9004]; + char m_out_frame[9004]; + std::thread m_read_thread; + Common::Flag m_read_enabled; + Common::Flag m_read_thread_shutdown; + static void ReadThreadHandler(XLinkNetworkInterface* self); #endif -}; + }; - std::unique_ptr network_interface; + std::unique_ptr m_network_interface; std::unique_ptr mRecvBuffer; u32 mRecvBufferLength = 0; diff --git a/Source/Core/DolphinQt/Settings/GameCubePane.cpp b/Source/Core/DolphinQt/Settings/GameCubePane.cpp index d17009290f..4dcc5c1bbf 100644 --- a/Source/Core/DolphinQt/Settings/GameCubePane.cpp +++ b/Source/Core/DolphinQt/Settings/GameCubePane.cpp @@ -104,6 +104,8 @@ void GameCubePane::CreateWidgets() {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)}) { m_slot_combos[2]->addItem(entry.first, entry.second); } @@ -158,7 +160,8 @@ void GameCubePane::UpdateButton(int slot) value == ExpansionInterface::EXIDEVICE_AGP || value == ExpansionInterface::EXIDEVICE_MIC); break; case SLOT_SP1_INDEX: - has_config = (value == ExpansionInterface::EXIDEVICE_ETH); + has_config = (value == ExpansionInterface::EXIDEVICE_ETH || + value == ExpansionInterface::EXIDEVICE_ETHXLINK); break; } @@ -186,13 +189,24 @@ void GameCubePane::OnConfigPressed(int slot) { bool ok; const auto new_mac = QInputDialog::getText( - this, tr("Broadband Adapter (TAP) MAC address"), - tr("Enter new Broadband Adapter (TAP) MAC address:"), QLineEdit::Normal, - QString::fromStdString(SConfig::GetInstance().m_bba_tap_mac), &ok); + // i18n: MAC stands for Media Access Control. A MAC address uniquely identifies a network + // interface (physical) like a serial number. "MAC" should be kept in translations. + this, tr("Broadband Adapter MAC address"), tr("Enter new Broadband Adapter MAC address:"), + QLineEdit::Normal, QString::fromStdString(SConfig::GetInstance().m_bba_mac), &ok); if (ok) - SConfig::GetInstance().m_bba_tap_mac = new_mac.toStdString(); + SConfig::GetInstance().m_bba_mac = new_mac.toStdString(); return; } + case ExpansionInterface::EXIDEVICE_ETHXLINK: + { + bool ok; + const auto new_dest = QInputDialog::getText( + this, tr("Broadband Adapter (XLink Kai) Destination Address"), + tr("Enter IP address of device running the XLink Kai Client.\nFor more information see" + " https://www.teamxlink.co.uk/wiki/Dolphin"), + QLineEdit::Normal, QString::fromStdString(SConfig::GetInstance().m_bba_xlink_ip), &ok); + if (ok) + SConfig::GetInstance().m_bba_xlink_ip = new_dest.toStdString(); return; } default: