diff --git a/Source/Core/Common/Network.cpp b/Source/Core/Common/Network.cpp index 23c570045f..c03ed0fc76 100644 --- a/Source/Core/Common/Network.cpp +++ b/Source/Core/Common/Network.cpp @@ -3,12 +3,13 @@ // Refer to the license.txt file included. #include -#include #include #include +#include #include "Common/Network.h" #include "Common/StringUtil.h" +#include "Common/Timer.h" void GenerateMacAddress(const MACConsumer type, u8* mac) { @@ -27,16 +28,12 @@ void GenerateMacAddress(const MACConsumer type, u8* mac) break; } - srand((unsigned int)time(nullptr)); - - u8 id[3] = - { - (u8)rand(), - (u8)rand(), - (u8)rand() - }; - - memcpy(&mac[3], id, 3); + // Generate the 24-bit NIC-specific portion of the MAC address. + std::default_random_engine generator(Common::Timer::GetTimeMs()); + std::uniform_int_distribution distribution(0x00, 0xFF); + mac[3] = static_cast(distribution(generator)); + mac[4] = static_cast(distribution(generator)); + mac[5] = static_cast(distribution(generator)); } std::string MacAddressToString(const u8* mac) diff --git a/Source/Core/Core/HW/BBA-TAP/TAP_Apple.cpp b/Source/Core/Core/HW/BBA-TAP/TAP_Apple.cpp index 2dad7169ad..bd0e3e9860 100644 --- a/Source/Core/Core/HW/BBA-TAP/TAP_Apple.cpp +++ b/Source/Core/Core/HW/BBA-TAP/TAP_Apple.cpp @@ -23,10 +23,8 @@ bool CEXIETHERNET::Activate() return false; } - readEnabled.store(false); - INFO_LOG(SP1, "BBA initialized."); - return true; + return RecvInit(); } void CEXIETHERNET::Deactivate() @@ -34,7 +32,8 @@ void CEXIETHERNET::Deactivate() close(fd); fd = -1; - readEnabled.store(false); + readEnabled.Clear(); + readThreadShutdown.Set(); if (readThread.joinable()) readThread.join(); } @@ -64,11 +63,8 @@ bool CEXIETHERNET::SendFrame(u8* frame, u32 size) static void ReadThreadHandler(CEXIETHERNET* self) { - while (true) + while (!self->readThreadShutdown.IsSet()) { - if (self->fd < 0) - return; - fd_set rfds; FD_ZERO(&rfds); FD_SET(self->fd, &rfds); @@ -79,14 +75,14 @@ static void ReadThreadHandler(CEXIETHERNET* self) if (select(self->fd + 1, &rfds, nullptr, nullptr, &timeout) <= 0) continue; - int readBytes = read(self->fd, self->mRecvBuffer, BBA_RECV_SIZE); + int readBytes = read(self->fd, self->mRecvBuffer.get(), BBA_RECV_SIZE); if (readBytes < 0) { ERROR_LOG(SP1, "Failed to read from BBA, err=%d", readBytes); } - else if (self->readEnabled.load()) + else if (self->readEnabled.IsSet()) { - INFO_LOG(SP1, "Read data: %s", ArrayToString(self->mRecvBuffer, readBytes, 0x10).c_str()); + INFO_LOG(SP1, "Read data: %s", ArrayToString(self->mRecvBuffer.get(), readBytes, 0x10).c_str()); self->mRecvBufferLength = readBytes; self->RecvHandlePacket(); } @@ -99,16 +95,12 @@ bool CEXIETHERNET::RecvInit() return true; } -bool CEXIETHERNET::RecvStart() +void CEXIETHERNET::RecvStart() { - if (!readThread.joinable()) - RecvInit(); - - readEnabled.store(true); - return true; + readEnabled.Set(); } void CEXIETHERNET::RecvStop() { - readEnabled.store(false); + readEnabled.Clear(); } diff --git a/Source/Core/Core/HW/BBA-TAP/TAP_Unix.cpp b/Source/Core/Core/HW/BBA-TAP/TAP_Unix.cpp index f13103196d..a44a8968f7 100644 --- a/Source/Core/Core/HW/BBA-TAP/TAP_Unix.cpp +++ b/Source/Core/Core/HW/BBA-TAP/TAP_Unix.cpp @@ -68,10 +68,8 @@ bool CEXIETHERNET::Activate() } ioctl(fd, TUNSETNOCSUM, 1); - readEnabled.store(false); - INFO_LOG(SP1, "BBA initialized with associated tap %s", ifr.ifr_name); - return true; + return RecvInit(); #else NOTIMPLEMENTED("Activate"); return false; @@ -84,7 +82,8 @@ void CEXIETHERNET::Deactivate() close(fd); fd = -1; - readEnabled.store(false); + readEnabled.Clear(); + readThreadShutdown.Set(); if (readThread.joinable()) readThread.join(); #else @@ -126,11 +125,8 @@ bool CEXIETHERNET::SendFrame(u8* frame, u32 size) static void ReadThreadHandler(CEXIETHERNET* self) { - while (true) + while (!self->readThreadShutdown.IsSet()) { - if (self->fd < 0) - return; - fd_set rfds; FD_ZERO(&rfds); FD_SET(self->fd, &rfds); @@ -141,14 +137,14 @@ static void ReadThreadHandler(CEXIETHERNET* self) if (select(self->fd + 1, &rfds, nullptr, nullptr, &timeout) <= 0) continue; - int readBytes = read(self->fd, self->mRecvBuffer, BBA_RECV_SIZE); + int readBytes = read(self->fd, self->mRecvBuffer.get(), BBA_RECV_SIZE); if (readBytes < 0) { ERROR_LOG(SP1, "Failed to read from BBA, err=%d", readBytes); } - else if (self->readEnabled.load()) + else if (self->readEnabled.IsSet()) { - INFO_LOG(SP1, "Read data: %s", ArrayToString(self->mRecvBuffer, readBytes, 0x10).c_str()); + INFO_LOG(SP1, "Read data: %s", ArrayToString(self->mRecvBuffer.get(), readBytes, 0x10).c_str()); self->mRecvBufferLength = readBytes; self->RecvHandlePacket(); } @@ -166,24 +162,19 @@ bool CEXIETHERNET::RecvInit() #endif } -bool CEXIETHERNET::RecvStart() +void CEXIETHERNET::RecvStart() { #ifdef __linux__ - if (!readThread.joinable()) - RecvInit(); - - readEnabled.store(true); - return true; + readEnabled.Set(); #else NOTIMPLEMENTED("RecvStart"); - return false; #endif } void CEXIETHERNET::RecvStop() { #ifdef __linux__ - readEnabled.store(false); + readEnabled.Clear(); #else NOTIMPLEMENTED("RecvStop"); #endif diff --git a/Source/Core/Core/HW/BBA-TAP/TAP_Win32.cpp b/Source/Core/Core/HW/BBA-TAP/TAP_Win32.cpp index 53825a4c7d..37b55c212a 100644 --- a/Source/Core/Core/HW/BBA-TAP/TAP_Win32.cpp +++ b/Source/Core/Core/HW/BBA-TAP/TAP_Win32.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include "Common/Assert.h" #include "Common/MsgHandler.h" #include "Common/StringUtil.h" #include "Common/Logging/Log.h" @@ -222,7 +223,14 @@ bool CEXIETHERNET::Activate() return false; } - return true; + /* initialize read/write events */ + mReadOverlapped.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); + mWriteOverlapped.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); + if (mReadOverlapped.hEvent == nullptr || mWriteOverlapped.hEvent == nullptr) + return false; + + mWriteBuffer.reserve(1518); + return RecvInit(); } void CEXIETHERNET::Deactivate() @@ -230,10 +238,24 @@ void CEXIETHERNET::Deactivate() if (!IsActivated()) return; - RecvStop(); + // Signal read thread to exit. + readEnabled.Clear(); + readThreadShutdown.Set(); + // Cancel any outstanding requests from both this thread (writes), and the read thread. + CancelIoEx(mHAdapter, nullptr); + + // Wait for read thread to exit. + if (readThread.joinable()) + readThread.join(); + + // Clean-up handles + CloseHandle(mReadOverlapped.hEvent); + CloseHandle(mWriteOverlapped.hEvent); CloseHandle(mHAdapter); mHAdapter = INVALID_HANDLE_VALUE; + memset(&mReadOverlapped, 0, sizeof(mReadOverlapped)); + memset(&mWriteOverlapped, 0, sizeof(mWriteOverlapped)); } bool CEXIETHERNET::IsActivated() @@ -241,101 +263,103 @@ bool CEXIETHERNET::IsActivated() return mHAdapter != INVALID_HANDLE_VALUE; } -bool CEXIETHERNET::SendFrame(u8 *frame, u32 size) +static void ReadThreadHandler(CEXIETHERNET* self) { - DEBUG_LOG(SP1, "SendFrame %x\n%s", - size, ArrayToString(frame, size, 0x10).c_str()); - - OVERLAPPED overlap; - ZeroMemory(&overlap, sizeof(overlap)); - - // WriteFile will always return false because the TAP handle is async - WriteFile(mHAdapter, frame, size, nullptr, &overlap); - - DWORD res = GetLastError(); - if (res != ERROR_IO_PENDING) + while (!self->readThreadShutdown.IsSet()) { - ERROR_LOG(SP1, "Failed to send packet with error 0x%X", res); + DWORD transferred; + + // Read from TAP into internal buffer. + if (ReadFile(self->mHAdapter, self->mRecvBuffer.get(), BBA_RECV_SIZE, &transferred, &self->mReadOverlapped)) + { + // Returning immediately is not likely to happen, but if so, reset the event state manually. + ResetEvent(self->mReadOverlapped.hEvent); + } + else + { + // IO should be pending. + if (GetLastError() != ERROR_IO_PENDING) + { + ERROR_LOG(SP1, "ReadFile failed (err=0x%X)", GetLastError()); + continue; + } + + // Block until the read completes. + if (!GetOverlappedResult(self->mHAdapter, &self->mReadOverlapped, &transferred, TRUE)) + { + // If CancelIO was called, we should exit (the flag will be set). + if (GetLastError() == ERROR_OPERATION_ABORTED) + continue; + + // Something else went wrong. + ERROR_LOG(SP1, "GetOverlappedResult failed (err=0x%X)", GetLastError()); + continue; + } + } + + // 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()); + if (self->readEnabled.IsSet()) + { + self->mRecvBufferLength = transferred; + self->RecvHandlePacket(); + } + } +} + +bool CEXIETHERNET::SendFrame(u8* frame, u32 size) +{ + DEBUG_LOG(SP1, "SendFrame %u bytes:\n%s", size, ArrayToString(frame, size, 0x10).c_str()); + + // Check for a background write. We can't issue another one until this one has completed. + DWORD transferred; + if (mWritePending) + { + // Wait for previous write to complete. + if (!GetOverlappedResult(mHAdapter, &mWriteOverlapped, &transferred, TRUE)) + ERROR_LOG(SP1, "GetOverlappedResult failed (err=0x%X)", GetLastError()); + } + + // Copy to write buffer. + mWriteBuffer.resize(size); + memcpy(mWriteBuffer.data(), frame, size); + mWritePending = true; + + // Queue async write. + if (WriteFile(mHAdapter, mWriteBuffer.data(), size, &transferred, &mWriteOverlapped)) + { + // Returning immediately is not likely to happen, but if so, reset the event state manually. + ResetEvent(mWriteOverlapped.hEvent); + } + else + { + // IO should be pending. + if (GetLastError() != ERROR_IO_PENDING) + { + ERROR_LOG(SP1, "WriteFile failed (err=0x%X)", GetLastError()); + ResetEvent(mWriteOverlapped.hEvent); + mWritePending = false; + return false; + } } // Always report the packet as being sent successfully, even though it might be a lie SendComplete(); - return true; } -VOID CALLBACK CEXIETHERNET::ReadWaitCallback(PVOID lpParameter, BOOLEAN TimerFired) -{ - CEXIETHERNET* self = (CEXIETHERNET*)lpParameter; - - GetOverlappedResult(self->mHAdapter, &self->mReadOverlapped, - (LPDWORD)&self->mRecvBufferLength, false); - - self->RecvHandlePacket(); -} - bool CEXIETHERNET::RecvInit() { - // Set up recv event - - if ((mHRecvEvent = CreateEvent(nullptr, false, false, nullptr)) == nullptr) - { - ERROR_LOG(SP1, "Failed to create recv event:%x", GetLastError()); - return false; - } - - ZeroMemory(&mReadOverlapped, sizeof(mReadOverlapped)); - - RegisterWaitForSingleObject(&mHReadWait, mHRecvEvent, ReadWaitCallback, - this, INFINITE, WT_EXECUTEDEFAULT); - - mReadOverlapped.hEvent = mHRecvEvent; - + readThread = std::thread(ReadThreadHandler, this); return true; } -bool CEXIETHERNET::RecvStart() +void CEXIETHERNET::RecvStart() { - if (!IsActivated()) - return false; - - if (mHRecvEvent == INVALID_HANDLE_VALUE) - RecvInit(); - - DWORD res = ReadFile(mHAdapter, mRecvBuffer, BBA_RECV_SIZE, - (LPDWORD)&mRecvBufferLength, &mReadOverlapped); - - if (res) - { - // Since the read is synchronous here, complete immediately - RecvHandlePacket(); - return true; - } - else - { - DWORD err = GetLastError(); - if (err == ERROR_IO_PENDING) - { - return true; - } - - // Unexpected error - ERROR_LOG(SP1, "Failed to recieve packet with error 0x%X", err); - return false; - } - + readEnabled.Set(); } void CEXIETHERNET::RecvStop() { - if (!IsActivated()) - return; - - UnregisterWaitEx(mHReadWait, INVALID_HANDLE_VALUE); - - if (mHRecvEvent != INVALID_HANDLE_VALUE) - { - CloseHandle(mHRecvEvent); - mHRecvEvent = INVALID_HANDLE_VALUE; - } + readEnabled.Clear(); } diff --git a/Source/Core/Core/HW/EXI_DeviceEthernet.cpp b/Source/Core/Core/HW/EXI_DeviceEthernet.cpp index 2859a62c45..76cf3aea72 100644 --- a/Source/Core/Core/HW/EXI_DeviceEthernet.cpp +++ b/Source/Core/Core/HW/EXI_DeviceEthernet.cpp @@ -20,10 +20,10 @@ CEXIETHERNET::CEXIETHERNET() { - tx_fifo = new u8[1518]; - mBbaMem = new u8[BBA_MEM_SIZE]; + tx_fifo = std::make_unique(BBA_TXFIFO_SIZE); + mBbaMem = std::make_unique(BBA_MEM_SIZE); - mRecvBuffer = new u8[BBA_RECV_SIZE]; + mRecvBuffer = std::make_unique(BBA_RECV_SIZE); mRecvBufferLength = 0; MXHardReset(); @@ -47,8 +47,9 @@ CEXIETHERNET::CEXIETHERNET() #if defined(_WIN32) mHAdapter = INVALID_HANDLE_VALUE; - mHRecvEvent = INVALID_HANDLE_VALUE; - mHReadWait = INVALID_HANDLE_VALUE; + memset(&mReadOverlapped, 0, sizeof(mReadOverlapped)); + memset(&mWriteOverlapped, 0, sizeof(mWriteOverlapped)); + mWritePending = false; #elif defined(__linux__) || defined(__APPLE__) fd = -1; #endif @@ -57,10 +58,6 @@ CEXIETHERNET::CEXIETHERNET() CEXIETHERNET::~CEXIETHERNET() { Deactivate(); - - delete[] tx_fifo; - delete[] mBbaMem; - delete[] mRecvBuffer; } void CEXIETHERNET::SetCS(int cs) @@ -205,9 +202,8 @@ void CEXIETHERNET::DMARead(u32 addr, u32 size) void CEXIETHERNET::DoState(PointerWrap &p) { - p.Do(mBbaMem); - // TODO ... the rest... - ERROR_LOG(SP1, "CEXIETHERNET::DoState not implemented!"); + p.DoArray(tx_fifo.get(), BBA_TXFIFO_SIZE); + p.DoArray(mBbaMem.get(), BBA_MEM_SIZE); } bool CEXIETHERNET::IsMXCommand(u32 const data) @@ -298,7 +294,7 @@ const char* CEXIETHERNET::GetRegisterName() const void CEXIETHERNET::MXHardReset() { - memset(mBbaMem, 0, BBA_MEM_SIZE); + memset(mBbaMem.get(), 0, BBA_MEM_SIZE); mBbaMem[BBA_NCRB] = NCRB_PR; mBbaMem[BBA_NWAYC] = NWAYC_LTE | NWAYC_ANE; @@ -384,7 +380,7 @@ void CEXIETHERNET::DirectFIFOWrite(u8 *data, u32 size) u16 *tx_fifo_count = (u16 *)&mBbaMem[BBA_TXFIFOCNT]; - memcpy(tx_fifo + *tx_fifo_count, data, size); + memcpy(tx_fifo.get() + *tx_fifo_count, data, size); *tx_fifo_count += size; // TODO: not sure this mask is correct. @@ -395,7 +391,7 @@ void CEXIETHERNET::DirectFIFOWrite(u8 *data, u32 size) void CEXIETHERNET::SendFromDirectFIFO() { - SendFrame(tx_fifo, *(u16 *)&mBbaMem[BBA_TXFIFOCNT]); + SendFrame(tx_fifo.get(), *(u16 *)&mBbaMem[BBA_TXFIFOCNT]); } void CEXIETHERNET::SendFromPacketBuffer() @@ -452,9 +448,9 @@ inline bool CEXIETHERNET::RecvMACFilter() // Unicast? if ((mRecvBuffer[0] & 0x01) == 0) { - return memcmp(mRecvBuffer, &mBbaMem[BBA_NAFR_PAR0], 6) == 0; + return memcmp(mRecvBuffer.get(), &mBbaMem[BBA_NAFR_PAR0], 6) == 0; } - else if (memcmp(mRecvBuffer, broadcast, 6) == 0) + else if (memcmp(mRecvBuffer.get(), broadcast, 6) == 0) { // Accept broadcast? return !!(mBbaMem[BBA_NCRB] & NCRB_AB); @@ -467,7 +463,7 @@ inline bool CEXIETHERNET::RecvMACFilter() else { // Lookup the dest eth address in the hashmap - u16 index = HashIndex(mRecvBuffer); + u16 index = HashIndex(mRecvBuffer.get()); return !!(mBbaMem[BBA_NAFR_MAR0 + index / 8] & (1 << (index % 8))); } } diff --git a/Source/Core/Core/HW/EXI_DeviceEthernet.h b/Source/Core/Core/HW/EXI_DeviceEthernet.h index 4f9cd81e09..d5ea42ffaa 100644 --- a/Source/Core/Core/HW/EXI_DeviceEthernet.h +++ b/Source/Core/Core/HW/EXI_DeviceEthernet.h @@ -6,11 +6,13 @@ #include #include +#include #ifdef _WIN32 #include #endif +#include "Common/Flag.h" #include "Core/HW/EXI_Device.h" class PointerWrap; @@ -157,7 +159,8 @@ enum { BBA_NUM_PAGES = 0x10, BBA_PAGE_SIZE = 0x100, - BBA_MEM_SIZE = BBA_NUM_PAGES * BBA_PAGE_SIZE + BBA_MEM_SIZE = BBA_NUM_PAGES * BBA_PAGE_SIZE, + BBA_TXFIFO_SIZE = 1518 }; enum @@ -307,8 +310,8 @@ public: void inc_rwp(); bool RecvHandlePacket(); - u8 *tx_fifo; - u8 *mBbaMem; + std::unique_ptr mBbaMem; + std::unique_ptr tx_fifo; // TAP interface bool Activate(); @@ -316,21 +319,26 @@ public: bool IsActivated(); bool SendFrame(u8 *frame, u32 size); bool RecvInit(); - bool RecvStart(); + void RecvStart(); void RecvStop(); - u8 *mRecvBuffer; + std::unique_ptr mRecvBuffer; u32 mRecvBufferLength; #if defined(_WIN32) - HANDLE mHAdapter, mHRecvEvent, mHReadWait; - DWORD mMtu; + HANDLE mHAdapter; OVERLAPPED mReadOverlapped; - static VOID CALLBACK ReadWaitCallback(PVOID lpParameter, BOOLEAN TimerFired); + OVERLAPPED mWriteOverlapped; + std::vector mWriteBuffer; + bool mWritePending; #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) int fd; +#endif + +#if defined(WIN32) || defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) std::thread readThread; - std::atomic readEnabled; + Common::Flag readEnabled; + Common::Flag readThreadShutdown; #endif };