Merge pull request #3432 from stenzek/bba-tap-win
EXI: Refactor Windows BBA-TAP interface to a read thread, crash fixes, cleanups
This commit is contained in:
commit
1d07fee367
|
@ -3,12 +3,13 @@
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
#include "Common/Network.h"
|
#include "Common/Network.h"
|
||||||
#include "Common/StringUtil.h"
|
#include "Common/StringUtil.h"
|
||||||
|
#include "Common/Timer.h"
|
||||||
|
|
||||||
void GenerateMacAddress(const MACConsumer type, u8* mac)
|
void GenerateMacAddress(const MACConsumer type, u8* mac)
|
||||||
{
|
{
|
||||||
|
@ -27,16 +28,12 @@ void GenerateMacAddress(const MACConsumer type, u8* mac)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
srand((unsigned int)time(nullptr));
|
// Generate the 24-bit NIC-specific portion of the MAC address.
|
||||||
|
std::default_random_engine generator(Common::Timer::GetTimeMs());
|
||||||
u8 id[3] =
|
std::uniform_int_distribution<int> distribution(0x00, 0xFF);
|
||||||
{
|
mac[3] = static_cast<u8>(distribution(generator));
|
||||||
(u8)rand(),
|
mac[4] = static_cast<u8>(distribution(generator));
|
||||||
(u8)rand(),
|
mac[5] = static_cast<u8>(distribution(generator));
|
||||||
(u8)rand()
|
|
||||||
};
|
|
||||||
|
|
||||||
memcpy(&mac[3], id, 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string MacAddressToString(const u8* mac)
|
std::string MacAddressToString(const u8* mac)
|
||||||
|
|
|
@ -23,10 +23,8 @@ bool CEXIETHERNET::Activate()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
readEnabled.store(false);
|
|
||||||
|
|
||||||
INFO_LOG(SP1, "BBA initialized.");
|
INFO_LOG(SP1, "BBA initialized.");
|
||||||
return true;
|
return RecvInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEXIETHERNET::Deactivate()
|
void CEXIETHERNET::Deactivate()
|
||||||
|
@ -34,7 +32,8 @@ void CEXIETHERNET::Deactivate()
|
||||||
close(fd);
|
close(fd);
|
||||||
fd = -1;
|
fd = -1;
|
||||||
|
|
||||||
readEnabled.store(false);
|
readEnabled.Clear();
|
||||||
|
readThreadShutdown.Set();
|
||||||
if (readThread.joinable())
|
if (readThread.joinable())
|
||||||
readThread.join();
|
readThread.join();
|
||||||
}
|
}
|
||||||
|
@ -64,11 +63,8 @@ bool CEXIETHERNET::SendFrame(u8* frame, u32 size)
|
||||||
|
|
||||||
static void ReadThreadHandler(CEXIETHERNET* self)
|
static void ReadThreadHandler(CEXIETHERNET* self)
|
||||||
{
|
{
|
||||||
while (true)
|
while (!self->readThreadShutdown.IsSet())
|
||||||
{
|
{
|
||||||
if (self->fd < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
fd_set rfds;
|
fd_set rfds;
|
||||||
FD_ZERO(&rfds);
|
FD_ZERO(&rfds);
|
||||||
FD_SET(self->fd, &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)
|
if (select(self->fd + 1, &rfds, nullptr, nullptr, &timeout) <= 0)
|
||||||
continue;
|
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)
|
if (readBytes < 0)
|
||||||
{
|
{
|
||||||
ERROR_LOG(SP1, "Failed to read from BBA, err=%d", readBytes);
|
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->mRecvBufferLength = readBytes;
|
||||||
self->RecvHandlePacket();
|
self->RecvHandlePacket();
|
||||||
}
|
}
|
||||||
|
@ -99,16 +95,12 @@ bool CEXIETHERNET::RecvInit()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CEXIETHERNET::RecvStart()
|
void CEXIETHERNET::RecvStart()
|
||||||
{
|
{
|
||||||
if (!readThread.joinable())
|
readEnabled.Set();
|
||||||
RecvInit();
|
|
||||||
|
|
||||||
readEnabled.store(true);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEXIETHERNET::RecvStop()
|
void CEXIETHERNET::RecvStop()
|
||||||
{
|
{
|
||||||
readEnabled.store(false);
|
readEnabled.Clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,10 +68,8 @@ bool CEXIETHERNET::Activate()
|
||||||
}
|
}
|
||||||
ioctl(fd, TUNSETNOCSUM, 1);
|
ioctl(fd, TUNSETNOCSUM, 1);
|
||||||
|
|
||||||
readEnabled.store(false);
|
|
||||||
|
|
||||||
INFO_LOG(SP1, "BBA initialized with associated tap %s", ifr.ifr_name);
|
INFO_LOG(SP1, "BBA initialized with associated tap %s", ifr.ifr_name);
|
||||||
return true;
|
return RecvInit();
|
||||||
#else
|
#else
|
||||||
NOTIMPLEMENTED("Activate");
|
NOTIMPLEMENTED("Activate");
|
||||||
return false;
|
return false;
|
||||||
|
@ -84,7 +82,8 @@ void CEXIETHERNET::Deactivate()
|
||||||
close(fd);
|
close(fd);
|
||||||
fd = -1;
|
fd = -1;
|
||||||
|
|
||||||
readEnabled.store(false);
|
readEnabled.Clear();
|
||||||
|
readThreadShutdown.Set();
|
||||||
if (readThread.joinable())
|
if (readThread.joinable())
|
||||||
readThread.join();
|
readThread.join();
|
||||||
#else
|
#else
|
||||||
|
@ -126,11 +125,8 @@ bool CEXIETHERNET::SendFrame(u8* frame, u32 size)
|
||||||
|
|
||||||
static void ReadThreadHandler(CEXIETHERNET* self)
|
static void ReadThreadHandler(CEXIETHERNET* self)
|
||||||
{
|
{
|
||||||
while (true)
|
while (!self->readThreadShutdown.IsSet())
|
||||||
{
|
{
|
||||||
if (self->fd < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
fd_set rfds;
|
fd_set rfds;
|
||||||
FD_ZERO(&rfds);
|
FD_ZERO(&rfds);
|
||||||
FD_SET(self->fd, &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)
|
if (select(self->fd + 1, &rfds, nullptr, nullptr, &timeout) <= 0)
|
||||||
continue;
|
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)
|
if (readBytes < 0)
|
||||||
{
|
{
|
||||||
ERROR_LOG(SP1, "Failed to read from BBA, err=%d", readBytes);
|
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->mRecvBufferLength = readBytes;
|
||||||
self->RecvHandlePacket();
|
self->RecvHandlePacket();
|
||||||
}
|
}
|
||||||
|
@ -166,24 +162,19 @@ bool CEXIETHERNET::RecvInit()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CEXIETHERNET::RecvStart()
|
void CEXIETHERNET::RecvStart()
|
||||||
{
|
{
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
if (!readThread.joinable())
|
readEnabled.Set();
|
||||||
RecvInit();
|
|
||||||
|
|
||||||
readEnabled.store(true);
|
|
||||||
return true;
|
|
||||||
#else
|
#else
|
||||||
NOTIMPLEMENTED("RecvStart");
|
NOTIMPLEMENTED("RecvStart");
|
||||||
return false;
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEXIETHERNET::RecvStop()
|
void CEXIETHERNET::RecvStop()
|
||||||
{
|
{
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
readEnabled.store(false);
|
readEnabled.Clear();
|
||||||
#else
|
#else
|
||||||
NOTIMPLEMENTED("RecvStop");
|
NOTIMPLEMENTED("RecvStop");
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under GPLv2+
|
// Licensed under GPLv2+
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "Common/Assert.h"
|
||||||
#include "Common/MsgHandler.h"
|
#include "Common/MsgHandler.h"
|
||||||
#include "Common/StringUtil.h"
|
#include "Common/StringUtil.h"
|
||||||
#include "Common/Logging/Log.h"
|
#include "Common/Logging/Log.h"
|
||||||
|
@ -222,7 +223,14 @@ bool CEXIETHERNET::Activate()
|
||||||
return false;
|
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()
|
void CEXIETHERNET::Deactivate()
|
||||||
|
@ -230,10 +238,24 @@ void CEXIETHERNET::Deactivate()
|
||||||
if (!IsActivated())
|
if (!IsActivated())
|
||||||
return;
|
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);
|
CloseHandle(mHAdapter);
|
||||||
mHAdapter = INVALID_HANDLE_VALUE;
|
mHAdapter = INVALID_HANDLE_VALUE;
|
||||||
|
memset(&mReadOverlapped, 0, sizeof(mReadOverlapped));
|
||||||
|
memset(&mWriteOverlapped, 0, sizeof(mWriteOverlapped));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CEXIETHERNET::IsActivated()
|
bool CEXIETHERNET::IsActivated()
|
||||||
|
@ -241,101 +263,103 @@ bool CEXIETHERNET::IsActivated()
|
||||||
return mHAdapter != INVALID_HANDLE_VALUE;
|
return mHAdapter != INVALID_HANDLE_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CEXIETHERNET::SendFrame(u8 *frame, u32 size)
|
static void ReadThreadHandler(CEXIETHERNET* self)
|
||||||
{
|
{
|
||||||
DEBUG_LOG(SP1, "SendFrame %x\n%s",
|
while (!self->readThreadShutdown.IsSet())
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
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
|
// Always report the packet as being sent successfully, even though it might be a lie
|
||||||
SendComplete();
|
SendComplete();
|
||||||
|
|
||||||
return true;
|
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()
|
bool CEXIETHERNET::RecvInit()
|
||||||
{
|
{
|
||||||
// Set up recv event
|
readThread = std::thread(ReadThreadHandler, this);
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CEXIETHERNET::RecvStart()
|
void CEXIETHERNET::RecvStart()
|
||||||
{
|
{
|
||||||
if (!IsActivated())
|
readEnabled.Set();
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEXIETHERNET::RecvStop()
|
void CEXIETHERNET::RecvStop()
|
||||||
{
|
{
|
||||||
if (!IsActivated())
|
readEnabled.Clear();
|
||||||
return;
|
|
||||||
|
|
||||||
UnregisterWaitEx(mHReadWait, INVALID_HANDLE_VALUE);
|
|
||||||
|
|
||||||
if (mHRecvEvent != INVALID_HANDLE_VALUE)
|
|
||||||
{
|
|
||||||
CloseHandle(mHRecvEvent);
|
|
||||||
mHRecvEvent = INVALID_HANDLE_VALUE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,10 @@
|
||||||
|
|
||||||
CEXIETHERNET::CEXIETHERNET()
|
CEXIETHERNET::CEXIETHERNET()
|
||||||
{
|
{
|
||||||
tx_fifo = new u8[1518];
|
tx_fifo = std::make_unique<u8[]>(BBA_TXFIFO_SIZE);
|
||||||
mBbaMem = new u8[BBA_MEM_SIZE];
|
mBbaMem = std::make_unique<u8[]>(BBA_MEM_SIZE);
|
||||||
|
|
||||||
mRecvBuffer = new u8[BBA_RECV_SIZE];
|
mRecvBuffer = std::make_unique<u8[]>(BBA_RECV_SIZE);
|
||||||
mRecvBufferLength = 0;
|
mRecvBufferLength = 0;
|
||||||
|
|
||||||
MXHardReset();
|
MXHardReset();
|
||||||
|
@ -47,8 +47,9 @@ CEXIETHERNET::CEXIETHERNET()
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
mHAdapter = INVALID_HANDLE_VALUE;
|
mHAdapter = INVALID_HANDLE_VALUE;
|
||||||
mHRecvEvent = INVALID_HANDLE_VALUE;
|
memset(&mReadOverlapped, 0, sizeof(mReadOverlapped));
|
||||||
mHReadWait = INVALID_HANDLE_VALUE;
|
memset(&mWriteOverlapped, 0, sizeof(mWriteOverlapped));
|
||||||
|
mWritePending = false;
|
||||||
#elif defined(__linux__) || defined(__APPLE__)
|
#elif defined(__linux__) || defined(__APPLE__)
|
||||||
fd = -1;
|
fd = -1;
|
||||||
#endif
|
#endif
|
||||||
|
@ -57,10 +58,6 @@ CEXIETHERNET::CEXIETHERNET()
|
||||||
CEXIETHERNET::~CEXIETHERNET()
|
CEXIETHERNET::~CEXIETHERNET()
|
||||||
{
|
{
|
||||||
Deactivate();
|
Deactivate();
|
||||||
|
|
||||||
delete[] tx_fifo;
|
|
||||||
delete[] mBbaMem;
|
|
||||||
delete[] mRecvBuffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEXIETHERNET::SetCS(int cs)
|
void CEXIETHERNET::SetCS(int cs)
|
||||||
|
@ -205,9 +202,8 @@ void CEXIETHERNET::DMARead(u32 addr, u32 size)
|
||||||
|
|
||||||
void CEXIETHERNET::DoState(PointerWrap &p)
|
void CEXIETHERNET::DoState(PointerWrap &p)
|
||||||
{
|
{
|
||||||
p.Do(mBbaMem);
|
p.DoArray(tx_fifo.get(), BBA_TXFIFO_SIZE);
|
||||||
// TODO ... the rest...
|
p.DoArray(mBbaMem.get(), BBA_MEM_SIZE);
|
||||||
ERROR_LOG(SP1, "CEXIETHERNET::DoState not implemented!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CEXIETHERNET::IsMXCommand(u32 const data)
|
bool CEXIETHERNET::IsMXCommand(u32 const data)
|
||||||
|
@ -298,7 +294,7 @@ const char* CEXIETHERNET::GetRegisterName() const
|
||||||
|
|
||||||
void CEXIETHERNET::MXHardReset()
|
void CEXIETHERNET::MXHardReset()
|
||||||
{
|
{
|
||||||
memset(mBbaMem, 0, BBA_MEM_SIZE);
|
memset(mBbaMem.get(), 0, BBA_MEM_SIZE);
|
||||||
|
|
||||||
mBbaMem[BBA_NCRB] = NCRB_PR;
|
mBbaMem[BBA_NCRB] = NCRB_PR;
|
||||||
mBbaMem[BBA_NWAYC] = NWAYC_LTE | NWAYC_ANE;
|
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];
|
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;
|
*tx_fifo_count += size;
|
||||||
// TODO: not sure this mask is correct.
|
// TODO: not sure this mask is correct.
|
||||||
|
@ -395,7 +391,7 @@ void CEXIETHERNET::DirectFIFOWrite(u8 *data, u32 size)
|
||||||
|
|
||||||
void CEXIETHERNET::SendFromDirectFIFO()
|
void CEXIETHERNET::SendFromDirectFIFO()
|
||||||
{
|
{
|
||||||
SendFrame(tx_fifo, *(u16 *)&mBbaMem[BBA_TXFIFOCNT]);
|
SendFrame(tx_fifo.get(), *(u16 *)&mBbaMem[BBA_TXFIFOCNT]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEXIETHERNET::SendFromPacketBuffer()
|
void CEXIETHERNET::SendFromPacketBuffer()
|
||||||
|
@ -452,9 +448,9 @@ inline bool CEXIETHERNET::RecvMACFilter()
|
||||||
// Unicast?
|
// Unicast?
|
||||||
if ((mRecvBuffer[0] & 0x01) == 0)
|
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?
|
// Accept broadcast?
|
||||||
return !!(mBbaMem[BBA_NCRB] & NCRB_AB);
|
return !!(mBbaMem[BBA_NCRB] & NCRB_AB);
|
||||||
|
@ -467,7 +463,7 @@ inline bool CEXIETHERNET::RecvMACFilter()
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Lookup the dest eth address in the hashmap
|
// 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)));
|
return !!(mBbaMem[BBA_NAFR_MAR0 + index / 8] & (1 << (index % 8)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,13 @@
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "Common/Flag.h"
|
||||||
#include "Core/HW/EXI_Device.h"
|
#include "Core/HW/EXI_Device.h"
|
||||||
|
|
||||||
class PointerWrap;
|
class PointerWrap;
|
||||||
|
@ -157,7 +159,8 @@ enum
|
||||||
{
|
{
|
||||||
BBA_NUM_PAGES = 0x10,
|
BBA_NUM_PAGES = 0x10,
|
||||||
BBA_PAGE_SIZE = 0x100,
|
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
|
enum
|
||||||
|
@ -307,8 +310,8 @@ public:
|
||||||
void inc_rwp();
|
void inc_rwp();
|
||||||
bool RecvHandlePacket();
|
bool RecvHandlePacket();
|
||||||
|
|
||||||
u8 *tx_fifo;
|
std::unique_ptr<u8[]> mBbaMem;
|
||||||
u8 *mBbaMem;
|
std::unique_ptr<u8[]> tx_fifo;
|
||||||
|
|
||||||
// TAP interface
|
// TAP interface
|
||||||
bool Activate();
|
bool Activate();
|
||||||
|
@ -316,21 +319,26 @@ public:
|
||||||
bool IsActivated();
|
bool IsActivated();
|
||||||
bool SendFrame(u8 *frame, u32 size);
|
bool SendFrame(u8 *frame, u32 size);
|
||||||
bool RecvInit();
|
bool RecvInit();
|
||||||
bool RecvStart();
|
void RecvStart();
|
||||||
void RecvStop();
|
void RecvStop();
|
||||||
|
|
||||||
u8 *mRecvBuffer;
|
std::unique_ptr<u8[]> mRecvBuffer;
|
||||||
u32 mRecvBufferLength;
|
u32 mRecvBufferLength;
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
HANDLE mHAdapter, mHRecvEvent, mHReadWait;
|
HANDLE mHAdapter;
|
||||||
DWORD mMtu;
|
|
||||||
OVERLAPPED mReadOverlapped;
|
OVERLAPPED mReadOverlapped;
|
||||||
static VOID CALLBACK ReadWaitCallback(PVOID lpParameter, BOOLEAN TimerFired);
|
OVERLAPPED mWriteOverlapped;
|
||||||
|
std::vector<u8> mWriteBuffer;
|
||||||
|
bool mWritePending;
|
||||||
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
|
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
|
||||||
int fd;
|
int fd;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(WIN32) || defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
|
||||||
std::thread readThread;
|
std::thread readThread;
|
||||||
std::atomic<bool> readEnabled;
|
Common::Flag readEnabled;
|
||||||
|
Common::Flag readThreadShutdown;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue