Compare commits

...

2 Commits

Author SHA1 Message Date
8678653095 clang-format'ed 2019-09-23 14:24:14 +02:00
be72157399 BBA refactorings 2019-09-23 14:24:14 +02:00
18 changed files with 655 additions and 457 deletions

View File

@ -203,8 +203,9 @@ add_library(core
HW/EXI/EXI_DeviceAGP.h
HW/EXI/EXI_DeviceDummy.cpp
HW/EXI/EXI_DeviceDummy.h
HW/EXI/EXI_DeviceEthernet.cpp
HW/EXI/EXI_DeviceEthernet.h
HW/EXI/EXI_DeviceEthernetBase.cpp
HW/EXI/EXI_DeviceEthernetBase.h
HW/EXI/EXI_DeviceEthernetTAP.h
HW/EXI/EXI_DeviceGecko.cpp
HW/EXI/EXI_DeviceGecko.h
HW/EXI/EXI_DeviceIPL.cpp
@ -589,8 +590,8 @@ endif()
if(WIN32)
target_sources(core PRIVATE
HW/EXI/BBA-TAP/TAP_Win32.cpp
HW/EXI/BBA-TAP/TAP_Win32.h
HW/EXI/EXI_DeviceEthernetTap_Win32.cpp
HW/EXI/EXI_DeviceEthernetTap_Win32.h
HW/WiimoteReal/IOWin.cpp
HW/WiimoteReal/IOWin.h
)
@ -603,7 +604,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/EXI_DeviceEthernetTAP_Apple.cpp
HW/WiimoteReal/IOdarwin.h
HW/WiimoteReal/IOdarwin_private.h
HW/WiimoteReal/IOdarwin.mm
@ -611,7 +612,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/EXI_DeviceEthernetTAP_Unix.cpp
)
if(ANDROID)
target_sources(core PRIVATE

View File

@ -132,14 +132,14 @@
<ClCompile Include="HW\DVD\DVDMath.cpp" />
<ClCompile Include="HW\DVD\DVDThread.cpp" />
<ClCompile Include="HW\DVD\FileMonitor.cpp" />
<ClCompile Include="HW\EXI\BBA-TAP\TAP_Win32.cpp" />
<ClCompile Include="HW\EXI\EXI.cpp" />
<ClCompile Include="HW\EXI\EXI_Channel.cpp" />
<ClCompile Include="HW\EXI\EXI_Device.cpp" />
<ClCompile Include="HW\EXI\EXI_DeviceAD16.cpp" />
<ClCompile Include="HW\EXI\EXI_DeviceAGP.cpp" />
<ClCompile Include="HW\EXI\EXI_DeviceDummy.cpp" />
<ClCompile Include="HW\EXI\EXI_DeviceEthernet.cpp" />
<ClCompile Include="HW\EXI\EXI_DeviceEthernetBase.cpp" />
<ClCompile Include="HW\EXI\EXI_DeviceEthernetTAP_Win32.cpp" />
<ClCompile Include="HW\EXI\EXI_DeviceGecko.cpp" />
<ClCompile Include="HW\EXI\EXI_DeviceIPL.cpp" />
<ClCompile Include="HW\EXI\EXI_DeviceMemoryCard.cpp" />
@ -404,14 +404,15 @@
<ClInclude Include="HW\DVD\DVDMath.h" />
<ClInclude Include="HW\DVD\DVDThread.h" />
<ClInclude Include="HW\DVD\FileMonitor.h" />
<ClInclude Include="HW\EXI\BBA-TAP\TAP_Win32.h" />
<ClInclude Include="HW\EXI\EXI.h" />
<ClInclude Include="HW\EXI\EXI_Channel.h" />
<ClInclude Include="HW\EXI\EXI_Device.h" />
<ClInclude Include="HW\EXI\EXI_DeviceAD16.h" />
<ClInclude Include="HW\EXI\EXI_DeviceAGP.h" />
<ClInclude Include="HW\EXI\EXI_DeviceDummy.h" />
<ClInclude Include="HW\EXI\EXI_DeviceEthernet.h" />
<ClInclude Include="HW\EXI\EXI_DeviceEthernetBase.h" />
<ClInclude Include="HW\EXI\EXI_DeviceEthernetTAP.h" />
<ClInclude Include="HW\EXI\EXI_DeviceEthernetTAP_Win32.h" />
<ClInclude Include="HW\EXI\EXI_DeviceGecko.h" />
<ClInclude Include="HW\EXI\EXI_DeviceIPL.h" />
<ClInclude Include="HW\EXI\EXI_DeviceMemoryCard.h" />

View File

@ -442,7 +442,13 @@
<ClCompile Include="HW\EXI\EXI_DeviceDummy.cpp">
<Filter>HW %28Flipper/Hollywood%29\EXI - Expansion Interface</Filter>
</ClCompile>
<ClCompile Include="HW\EXI\EXI_DeviceEthernet.cpp">
<ClCompile Include="HW\EXI\EXI_DeviceEthernetBase.cpp">
<Filter>HW %28Flipper/Hollywood%29\EXI - Expansion Interface</Filter>
</ClCompile>
<ClCompile Include="HW\EXI\EXI_DeviceEthernetTAP.cpp">
<Filter>HW %28Flipper/Hollywood%29\EXI - Expansion Interface</Filter>
</ClCompile>
<ClCompile Include="HW\EXI\EXI_DeviceEthernetTAP_Win32.cpp">
<Filter>HW %28Flipper/Hollywood%29\EXI - Expansion Interface</Filter>
</ClCompile>
<ClCompile Include="HW\EXI\EXI_DeviceGecko.cpp">
@ -457,9 +463,6 @@
<ClCompile Include="HW\EXI\EXI_DeviceMic.cpp">
<Filter>HW %28Flipper/Hollywood%29\EXI - Expansion Interface</Filter>
</ClCompile>
<ClCompile Include="HW\EXI\BBA-TAP\TAP_Win32.cpp">
<Filter>HW %28Flipper/Hollywood%29\EXI - Expansion Interface</Filter>
</ClCompile>
<ClCompile Include="HW\Sram.cpp">
<Filter>HW %28Flipper/Hollywood%29\EXI - Expansion Interface</Filter>
</ClCompile>
@ -1157,7 +1160,13 @@
<ClInclude Include="HW\EXI\EXI_DeviceDummy.h">
<Filter>HW %28Flipper/Hollywood%29\EXI - Expansion Interface</Filter>
</ClInclude>
<ClInclude Include="HW\EXI\EXI_DeviceEthernet.h">
<ClInclude Include="HW\EXI\EXI_DeviceEthernetBase.h">
<Filter>HW %28Flipper/Hollywood%29\EXI - Expansion Interface</Filter>
</ClInclude>
<ClInclude Include="HW\EXI\EXI_DeviceEthernetTAP.h">
<Filter>HW %28Flipper/Hollywood%29\EXI - Expansion Interface</Filter>
</ClInclude>
<ClInclude Include="HW\EXI\EXI_DeviceEthernetTAP_Win32.h">
<Filter>HW %28Flipper/Hollywood%29\EXI - Expansion Interface</Filter>
</ClInclude>
<ClInclude Include="HW\EXI\EXI_DeviceGecko.h">
@ -1172,9 +1181,6 @@
<ClInclude Include="HW\EXI\EXI_DeviceMic.h">
<Filter>HW %28Flipper/Hollywood%29\EXI - Expansion Interface</Filter>
</ClInclude>
<ClInclude Include="HW\EXI\BBA-TAP\TAP_Win32.h">
<Filter>HW %28Flipper/Hollywood%29\EXI - Expansion Interface</Filter>
</ClInclude>
<ClInclude Include="HW\Sram.h">
<Filter>HW %28Flipper/Hollywood%29\EXI - Expansion Interface</Filter>
</ClInclude>

View File

@ -1,110 +0,0 @@
// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <fcntl.h>
#include <unistd.h>
#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
{
bool CEXIETHERNET::Activate()
{
if (IsActivated())
return true;
// Assumes TunTap OS X is installed, and /dev/tun0 is not in use
// and readable / writable by the logged-in user
if ((fd = open("/dev/tap0", O_RDWR)) < 0)
{
ERROR_LOG(SP1, "Couldn't open /dev/tap0, unable to init BBA");
return false;
}
INFO_LOG(SP1, "BBA initialized.");
return RecvInit();
}
void CEXIETHERNET::Deactivate()
{
close(fd);
fd = -1;
readEnabled.Clear();
readThreadShutdown.Set();
if (readThread.joinable())
readThread.join();
}
bool CEXIETHERNET::IsActivated()
{
return fd != -1;
}
bool CEXIETHERNET::SendFrame(const u8* frame, u32 size)
{
INFO_LOG(SP1, "SendFrame %x\n%s", size, ArrayToString(frame, size, 0x10).c_str());
int writtenBytes = write(fd, frame, size);
if ((u32)writtenBytes != size)
{
ERROR_LOG(SP1, "SendFrame(): expected to write %d bytes, instead wrote %d", size, writtenBytes);
return false;
}
else
{
SendComplete();
return true;
}
}
void CEXIETHERNET::ReadThreadHandler(CEXIETHERNET* self)
{
while (!self->readThreadShutdown.IsSet())
{
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(self->fd, &rfds);
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 50000;
if (select(self->fd + 1, &rfds, nullptr, nullptr, &timeout) <= 0)
continue;
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.IsSet())
{
INFO_LOG(SP1, "Read data: %s",
ArrayToString(self->mRecvBuffer.get(), readBytes, 0x10).c_str());
self->mRecvBufferLength = readBytes;
self->RecvHandlePacket();
}
}
}
bool CEXIETHERNET::RecvInit()
{
readThread = std::thread(ReadThreadHandler, this);
return true;
}
void CEXIETHERNET::RecvStart()
{
readEnabled.Set();
}
void CEXIETHERNET::RecvStop()
{
readEnabled.Clear();
}
} // namespace ExpansionInterface

View File

@ -10,7 +10,7 @@
#include "Core/HW/EXI/EXI_DeviceAD16.h"
#include "Core/HW/EXI/EXI_DeviceAGP.h"
#include "Core/HW/EXI/EXI_DeviceDummy.h"
#include "Core/HW/EXI/EXI_DeviceEthernet.h"
#include "Core/HW/EXI/EXI_DeviceEthernetTAP.h"
#include "Core/HW/EXI/EXI_DeviceGecko.h"
#include "Core/HW/EXI/EXI_DeviceIPL.h"
#include "Core/HW/EXI/EXI_DeviceMemoryCard.h"
@ -131,8 +131,8 @@ std::unique_ptr<IEXIDevice> EXIDevice_Create(const TEXIDevices device_type, cons
result = std::make_unique<CEXIMic>(channel_num);
break;
case EXIDEVICE_ETH:
result = std::make_unique<CEXIETHERNET>();
case EXIDEVICE_ETH_TAP:
result = std::make_unique<CEXIEthernetTAP>();
break;
case EXIDEVICE_GECKO:

View File

@ -18,7 +18,7 @@ enum TEXIDevices : int
EXIDEVICE_MASKROM,
EXIDEVICE_AD16,
EXIDEVICE_MIC,
EXIDEVICE_ETH,
EXIDEVICE_ETH_TAP,
// Was used for Triforce in the past, but the implementation is no longer in Dolphin.
// It's kept here so that values below will stay constant.
EXIDEVICE_AM_BASEBOARD,

View File

@ -2,11 +2,7 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "Core/HW/EXI/EXI_DeviceEthernet.h"
#include <memory>
#include <optional>
#include <string>
#include "Core/HW/EXI/EXI_DeviceEthernetBase.h"
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
@ -22,40 +18,42 @@ namespace ExpansionInterface
// XXX: The BBA stores multi-byte elements as little endian.
// Multiple parts of this implementation depend on Dolphin
// being compiled for a little endian host.
CEXIETHERNET::CEXIETHERNET()
CEXIEthernetBase::CEXIEthernetBase()
{
tx_fifo = std::make_unique<u8[]>(BBA_TXFIFO_SIZE);
mBbaMem = std::make_unique<u8[]>(BBA_MEM_SIZE);
mRecvBuffer = std::make_unique<u8[]>(BBA_RECV_SIZE);
MXHardReset();
// 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_mac;
std::optional<Common::MACAddress> mac_addr = Common::StringToMacAddress(mac_addr_setting);
const std::string& mac_addr_setting = SConfig::GetInstance().m_bba_mac;
Common::MACAddress mac_addr;
if (!mac_addr)
if (mac_addr_setting.empty())
{
mac_addr = Common::GenerateMacAddress(Common::MACConsumer::BBA);
mac_addr_setting = Common::MacAddressToString(mac_addr.value());
SConfig::GetInstance().SaveSettings();
}
else
{
std::optional<Common::MACAddress> parsed = Common::StringToMacAddress(mac_addr_setting);
if (!parsed)
{
mac_addr = Common::GenerateMacAddress(Common::MACConsumer::BBA);
ERROR_LOG(SP1, "Invalid mac address (%s), generated a temporary one (%s)",
mac_addr_setting.c_str(), Common::MacAddressToString(mac_addr).c_str());
}
else
{
mac_addr = parsed.value();
}
}
const auto& mac = mac_addr.value();
memcpy(&mBbaMem[BBA_NAFR_PAR0], mac.data(), mac.size());
memcpy(&m_bba_mem.nafr_par, mac_addr.data(), mac_addr.size());
// HACK: .. fully established 100BASE-T link
mBbaMem[BBA_NWAYS] = NWAYS_LS100 | NWAYS_LPNWAY | NWAYS_100TXF | NWAYS_ANCLPT;
m_bba_mem.nways = NWAYS_LS100 | NWAYS_LPNWAY | NWAYS_100TXF | NWAYS_ANCLPT;
}
CEXIETHERNET::~CEXIETHERNET()
{
Deactivate();
}
void CEXIETHERNET::SetCS(int cs)
void CEXIEthernetBase::SetCS(int cs)
{
if (cs)
{
@ -64,17 +62,17 @@ void CEXIETHERNET::SetCS(int cs)
}
}
bool CEXIETHERNET::IsPresent() const
bool CEXIEthernetBase::IsPresent() const
{
return true;
}
bool CEXIETHERNET::IsInterruptSet()
bool CEXIEthernetBase::IsInterruptSet()
{
return !!(exi_status.interrupt & exi_status.interrupt_mask);
}
void CEXIETHERNET::ImmWrite(u32 data, u32 size)
void CEXIEthernetBase::ImmWrite(u32 data, u32 size)
{
data >>= (4 - size) * 8;
@ -125,7 +123,7 @@ void CEXIETHERNET::ImmWrite(u32 data, u32 size)
}
}
u32 CEXIETHERNET::ImmRead(u32 size)
u32 CEXIEthernetBase::ImmRead(u32 size)
{
u32 ret = 0;
@ -155,7 +153,7 @@ u32 CEXIETHERNET::ImmRead(u32 size)
else
{
for (int i = size - 1; i >= 0; i--)
ret |= mBbaMem[transfer.address++] << (i * 8);
ret |= m_bba_mem.raw[transfer.address++] << (i * 8);
}
DEBUG_LOG(SP1, "imm r%i: %0*x", size, size * 2, ret);
@ -165,7 +163,7 @@ u32 CEXIETHERNET::ImmRead(u32 size)
return ret;
}
void CEXIETHERNET::DMAWrite(u32 addr, u32 size)
void CEXIEthernetBase::DMAWrite(u32 addr, u32 size)
{
DEBUG_LOG(SP1, "DMA write: %08x %x", addr, size);
@ -182,32 +180,32 @@ void CEXIETHERNET::DMAWrite(u32 addr, u32 size)
}
}
void CEXIETHERNET::DMARead(u32 addr, u32 size)
void CEXIEthernetBase::DMARead(u32 addr, u32 size)
{
DEBUG_LOG(SP1, "DMA read: %08x %x", addr, size);
Memory::CopyToEmu(addr, &mBbaMem[transfer.address], size);
Memory::CopyToEmu(addr, &m_bba_mem.raw[transfer.address], size);
transfer.address += size;
}
void CEXIETHERNET::DoState(PointerWrap& p)
void CEXIEthernetBase::DoState(PointerWrap& p)
{
p.DoArray(tx_fifo.get(), BBA_TXFIFO_SIZE);
p.DoArray(mBbaMem.get(), BBA_MEM_SIZE);
p.DoArray(&m_tx_fifo, BBA_TXFIFO_SIZE);
p.DoArray(&m_bba_mem.raw, BBA_MEM_SIZE);
}
bool CEXIETHERNET::IsMXCommand(u32 const data)
bool CEXIEthernetBase::IsMXCommand(u32 const data)
{
return !!(data & (1 << 31));
}
bool CEXIETHERNET::IsWriteCommand(u32 const data)
bool CEXIEthernetBase::IsWriteCommand(u32 const data)
{
return IsMXCommand(data) ? !!(data & (1 << 30)) : !!(data & (1 << 14));
}
const char* CEXIETHERNET::GetRegisterName() const
const char* CEXIEthernetBase::GetRegisterName() const
{
#define STR_RETURN(x) \
case x: \
@ -285,16 +283,16 @@ const char* CEXIETHERNET::GetRegisterName() const
#undef STR_RETURN
}
void CEXIETHERNET::MXHardReset()
void CEXIEthernetBase::MXHardReset()
{
memset(mBbaMem.get(), 0, BBA_MEM_SIZE);
memset(&m_bba_mem.raw, 0, BBA_MEM_SIZE);
mBbaMem[BBA_NCRB] = NCRB_PR;
mBbaMem[BBA_NWAYC] = NWAYC_LTE | NWAYC_ANE;
mBbaMem[BBA_MISC] = MISC1_TPF | MISC1_TPH | MISC1_TXF | MISC1_TXH;
m_bba_mem.ncrb = NCRB_PR;
m_bba_mem.nwayc = NWAYC_LTE | NWAYC_ANE;
m_bba_mem.misc = MISC1_TPF | MISC1_TPH | MISC1_TXF | MISC1_TXH;
}
void CEXIETHERNET::MXCommandHandler(u32 data, u32 size)
void CEXIEthernetBase::MXCommandHandler(u32 data, u32 size)
{
switch (transfer.address)
{
@ -306,7 +304,7 @@ void CEXIETHERNET::MXCommandHandler(u32 data, u32 size)
Activate();
}
if ((mBbaMem[BBA_NCRA] & NCRA_SR) ^ (data & NCRA_SR))
if ((m_bba_mem.ncra & NCRA_SR) ^ (data & NCRA_SR))
{
DEBUG_LOG(SP1, "%s rx", (data & NCRA_SR) ? "start" : "stop");
@ -317,7 +315,7 @@ void CEXIETHERNET::MXCommandHandler(u32 data, u32 size)
}
// Only start transfer if there isn't one currently running
if (!(mBbaMem[BBA_NCRA] & (NCRA_ST0 | NCRA_ST1)))
if (!(m_bba_mem.ncra & (NCRA_ST0 | NCRA_ST1)))
{
// Technically transfer DMA status is kept in TXDMA - not implemented
@ -362,55 +360,57 @@ void CEXIETHERNET::MXCommandHandler(u32 data, u32 size)
default:
for (int i = size - 1; i >= 0; i--)
{
mBbaMem[transfer.address++] = (data >> (i * 8)) & 0xff;
m_bba_mem.raw[transfer.address++] = (data >> (i * 8)) & 0xff;
}
return;
}
}
void CEXIETHERNET::DirectFIFOWrite(const u8* data, u32 size)
void CEXIEthernetBase::DirectFIFOWrite(const u8* data, u32 size)
{
// In direct mode, the hardware handles creating the state required by the
// GMAC instead of finagling with packet descriptors and such
u16* tx_fifo_count = (u16*)&mBbaMem[BBA_TXFIFOCNT];
memcpy(&m_tx_fifo[0] + m_bba_mem.txfifocnt, data, size);
memcpy(tx_fifo.get() + *tx_fifo_count, data, size);
*tx_fifo_count += size;
m_bba_mem.txfifocnt += size;
// TODO: not sure this mask is correct.
// However, BBA_TXFIFOCNT should never get even close to this amount,
// so it shouldn't matter
*tx_fifo_count &= (1 << 12) - 1;
m_bba_mem.txfifocnt &= (1 << 12) - 1;
}
void CEXIETHERNET::SendFromDirectFIFO()
void CEXIEthernetBase::SendFromDirectFIFO()
{
SendFrame(tx_fifo.get(), *(u16*)&mBbaMem[BBA_TXFIFOCNT]);
const auto size = m_bba_mem.txfifocnt;
const auto frame = &m_tx_fifo[0];
INFO_LOG(SP1, "SendFrame %x", size);
DEBUG_LOG(SP1, "%s", ArrayToString(frame, size, 0x10).c_str());
SendFrame(frame, size);
}
void CEXIETHERNET::SendFromPacketBuffer()
void CEXIEthernetBase::SendFromPacketBuffer()
{
ERROR_LOG(SP1, "tx packet buffer not implemented.");
}
void CEXIETHERNET::SendComplete()
void CEXIEthernetBase::SendComplete()
{
mBbaMem[BBA_NCRA] &= ~(NCRA_ST0 | NCRA_ST1);
*(u16*)&mBbaMem[BBA_TXFIFOCNT] = 0;
m_bba_mem.ncra &= ~(NCRA_ST0 | NCRA_ST1);
m_bba_mem.txfifocnt = 0;
if (mBbaMem[BBA_IMR] & INT_T)
if (m_bba_mem.imr & INT_T)
{
mBbaMem[BBA_IR] |= INT_T;
m_bba_mem.ir |= INT_T;
exi_status.interrupt |= exi_status.TRANSFER;
ExpansionInterface::ScheduleUpdateInterrupts(CoreTiming::FromThread::CPU, 0);
}
mBbaMem[BBA_LTPS] = 0;
m_bba_mem.ltps = 0;
}
inline u8 CEXIETHERNET::HashIndex(const u8* dest_eth_addr)
inline u8 CEXIEthernetBase::HashIndex(const u8* dest_eth_addr) const
{
// Calculate CRC
u32 crc = 0xffffffff;
@ -432,25 +432,25 @@ inline u8 CEXIETHERNET::HashIndex(const u8* dest_eth_addr)
return crc >> 26;
}
inline bool CEXIETHERNET::RecvMACFilter()
inline bool CEXIEthernetBase::RecvMACFilter() const
{
static u8 const broadcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
// Accept all destination addrs?
if (mBbaMem[BBA_NCRB] & NCRB_PR)
if (m_bba_mem.ncrb & NCRB_PR)
return true;
// Unicast?
if ((mRecvBuffer[0] & 0x01) == 0)
if ((m_recv_buffer[0] & 0x01) == 0)
{
return memcmp(mRecvBuffer.get(), &mBbaMem[BBA_NAFR_PAR0], 6) == 0;
return memcmp(&m_recv_buffer, &m_bba_mem.nafr_par, 6) == 0;
}
else if (memcmp(mRecvBuffer.get(), broadcast, 6) == 0)
else if (memcmp(&m_recv_buffer, broadcast, 6) == 0)
{
// Accept broadcast?
return !!(mBbaMem[BBA_NCRB] & NCRB_AB);
return !!(m_bba_mem.ncrb & NCRB_AB);
}
else if (mBbaMem[BBA_NCRB] & NCRB_PM)
else if (m_bba_mem.ncrb & NCRB_PM)
{
// Accept all multicast
return true;
@ -458,25 +458,26 @@ inline bool CEXIETHERNET::RecvMACFilter()
else
{
// Lookup the dest eth address in the hashmap
u16 index = HashIndex(mRecvBuffer.get());
return !!(mBbaMem[BBA_NAFR_MAR0 + index / 8] & (1 << (index % 8)));
u16 index = HashIndex(&m_recv_buffer[0]);
return !!(m_bba_mem.nafr_mar[index / 8] & (1 << (index % 8)));
}
}
inline void CEXIETHERNET::inc_rwp()
inline void CEXIEthernetBase::inc_rwp()
{
u16* rwp = (u16*)&mBbaMem[BBA_RWP];
if (*rwp + 1 == page_ptr(BBA_RHBP))
*rwp = page_ptr(BBA_BP);
if (m_bba_mem.rwp + 1 == page_ptr(BBA_RHBP))
m_bba_mem.rwp = page_ptr(BBA_BP);
else
(*rwp)++;
m_bba_mem.rwp++;
}
// This function is on the critical path for receiving data.
// Be very careful about calling into the logger and other slow things
bool CEXIETHERNET::RecvHandlePacket()
bool CEXIEthernetBase::RecvHandlePacket()
{
INFO_LOG(SP1, "RecvHandlePacket: %x", m_recv_buffer_length);
DEBUG_LOG(SP1, "%s", ArrayToString(&m_recv_buffer[0], m_recv_buffer_length, 0x10).c_str());
u8* write_ptr;
u8* end_ptr;
u8* read_ptr;
@ -488,23 +489,23 @@ bool CEXIETHERNET::RecvHandlePacket()
goto wait_for_next;
#ifdef BBA_TRACK_PAGE_PTRS
INFO_LOG(SP1, "RecvHandlePacket %x\n%s", mRecvBufferLength,
ArrayToString(mRecvBuffer, mRecvBufferLength, 0x100).c_str());
INFO_LOG(SP1, "RecvHandlePacket %x\n%s", m_recv_buffer_length,
ArrayToString(m_recv_buffer, m_recv_buffer_length, 0x100).c_str());
INFO_LOG(SP1, "%x %x %x %x", page_ptr(BBA_BP), page_ptr(BBA_RRP), page_ptr(BBA_RWP),
page_ptr(BBA_RHBP));
#endif
write_ptr = ptr_from_page_ptr(BBA_RWP);
end_ptr = ptr_from_page_ptr(BBA_RHBP);
read_ptr = ptr_from_page_ptr(BBA_RRP);
write_ptr = PtrFromPagePtr(BBA_RWP);
end_ptr = PtrFromPagePtr(BBA_RHBP);
read_ptr = PtrFromPagePtr(BBA_RRP);
descriptor = (Descriptor*)write_ptr;
write_ptr += 4;
for (u32 i = 0, off = 4; i < mRecvBufferLength; ++i, ++off)
for (u32 i = 0, off = 4; i < m_recv_buffer_length; ++i, ++off)
{
*write_ptr++ = mRecvBuffer[i];
*write_ptr++ = m_recv_buffer[i];
if (off == 0xff)
{
@ -513,7 +514,7 @@ bool CEXIETHERNET::RecvHandlePacket()
}
if (write_ptr == end_ptr)
write_ptr = ptr_from_page_ptr(BBA_BP);
write_ptr = PtrFromPagePtr(BBA_BP);
if (write_ptr == read_ptr)
{
@ -529,13 +530,13 @@ bool CEXIETHERNET::RecvHandlePacket()
inc MPC instead of receiving packets
*/
status |= DESC_FO | DESC_BF;
mBbaMem[BBA_IR] |= mBbaMem[BBA_IMR] & INT_RBF;
m_bba_mem.ir |= m_bba_mem.imr & INT_RBF;
break;
}
}
// Align up to next page
if ((mRecvBufferLength + 4) % 256)
if ((m_recv_buffer_length + 4) % 256)
inc_rwp();
#ifdef BBA_TRACK_PAGE_PTRS
@ -544,14 +545,14 @@ bool CEXIETHERNET::RecvHandlePacket()
#endif
// Is the current frame multicast?
if (mRecvBuffer[0] & 0x01)
if (m_recv_buffer[0] & 0x01)
status |= DESC_MF;
if (status & DESC_BF)
{
if (mBbaMem[BBA_MISC2] & MISC2_AUTORCVR)
if (m_bba_mem.misc2 & MISC2_AUTORCVR)
{
*(u16*)&mBbaMem[BBA_RWP] = rwp_initial;
m_bba_mem.rwp = rwp_initial;
}
else
{
@ -559,14 +560,14 @@ bool CEXIETHERNET::RecvHandlePacket()
}
}
descriptor->set(*(u16*)&mBbaMem[BBA_RWP], 4 + mRecvBufferLength, status);
descriptor->set(m_bba_mem.rwp, 4 + m_recv_buffer_length, status);
mBbaMem[BBA_LRPS] = status;
m_bba_mem.lrps = status;
// Raise interrupt
if (mBbaMem[BBA_IMR] & INT_R)
if (m_bba_mem.imr & INT_R)
{
mBbaMem[BBA_IR] |= INT_R;
m_bba_mem.ir |= INT_R;
exi_status.interrupt |= exi_status.TRANSFER;
ExpansionInterface::ScheduleUpdateInterrupts(CoreTiming::FromThread::NON_CPU, 0);
@ -578,7 +579,7 @@ bool CEXIETHERNET::RecvHandlePacket()
}
wait_for_next:
if (mBbaMem[BBA_NCRA] & NCRA_SR)
if (m_bba_mem.ncra & NCRA_SR)
RecvStart();
return true;

View File

@ -4,17 +4,10 @@
#pragma once
#include <atomic>
#include <thread>
#include <vector>
#ifdef _WIN32
#include <Windows.h>
#endif
#include "Common/Flag.h"
#include "Core/HW/EXI/EXI_Device.h"
#include <array>
class PointerWrap;
namespace ExpansionInterface
@ -131,6 +124,7 @@ enum
BBA_NAFR_PAR3 = 0x23,
BBA_NAFR_PAR4 = 0x24,
BBA_NAFR_PAR5 = 0x25,
BBA_NAFR_MAR0 = 0x26,
BBA_NAFR_MAR1 = 0x27,
BBA_NAFR_MAR2 = 0x28,
@ -157,14 +151,6 @@ enum
BBA_SI_ACTRL2 = 0x60
};
enum
{
BBA_NUM_PAGES = 0x10,
BBA_PAGE_SIZE = 0x100,
BBA_MEM_SIZE = BBA_NUM_PAGES * BBA_PAGE_SIZE,
BBA_TXFIFO_SIZE = 1518
};
enum
{
EXI_DEVTYPE_ETHER = 0x04020200
@ -194,13 +180,11 @@ enum RecvStatus
DESC_RERR = 0x80
};
#define BBA_RECV_SIZE 0x800
class CEXIETHERNET : public IEXIDevice
class CEXIEthernetBase : public IEXIDevice
{
public:
CEXIETHERNET();
virtual ~CEXIETHERNET();
CEXIEthernetBase();
void SetCS(int cs) override;
bool IsPresent() const override;
bool IsInterruptSet() override;
@ -210,7 +194,99 @@ public:
void DMARead(u32 addr, u32 size) override;
void DoState(PointerWrap& p) override;
private:
protected:
static constexpr std::size_t BBA_RECV_SIZE = 0x800;
static constexpr std::size_t BBA_NUM_PAGES = 0x10;
static constexpr std::size_t BBA_PAGE_SIZE = 0x100;
static constexpr std::size_t BBA_MEM_SIZE = BBA_NUM_PAGES * BBA_PAGE_SIZE;
static constexpr std::size_t BBA_TXFIFO_SIZE = 1518;
virtual bool Activate() = 0;
virtual bool IsActivated() const = 0;
virtual bool SendFrame(const u8* frame, u32 size) = 0;
virtual bool RecvInit() = 0;
virtual void RecvStart() = 0;
virtual void RecvStop() = 0;
inline u16 page_ptr(int const index) const
{
return ((u16)m_bba_mem.raw[index + 1] << 8) | m_bba_mem.raw[index];
}
inline u8* PtrFromPagePtr(int const index) { return &m_bba_mem.raw[page_ptr(index) << 8]; }
inline const u8* PtrFromPagePtr(int const index) const
{
return &m_bba_mem.raw[page_ptr(index) << 8];
}
bool IsMXCommand(u32 const data);
bool IsWriteCommand(u32 const data);
const char* GetRegisterName() const;
void MXHardReset();
void MXCommandHandler(u32 data, u32 size);
void DirectFIFOWrite(const u8* data, u32 size);
void SendFromDirectFIFO();
void SendFromPacketBuffer();
void SendComplete();
u8 HashIndex(const u8* dest_eth_addr) const;
bool RecvMACFilter() const;
void inc_rwp();
bool RecvHandlePacket();
union
{
std::array<u8, BBA_MEM_SIZE> raw;
#pragma pack(push, 1)
struct
{
u8 ncra; // 0x00
u8 ncrb; // 0x01
u16 : 16;
u8 ltps; // 0x04
u8 lrps; // 0x05
u16 : 16;
u8 imr; // 0x08
u8 ir; // 0x09
u16 bp; // 0x0a
u16 tlbp; // 0x0c
u16 twp; // 0x0e
u16 iob; // 0x10
u16 trp; // 0x12
u16 rxintt; // 0x14
u16 rwp; // 0x16
u16 rrp; // 0x18
u16 rhbp; // 0x1a
u32 : 32;
std::array<u8, 6> nafr_par; // 0x20
std::array<u8, 8> nafr_mar; // 0x26
u16 : 16;
u8 nwayc; // 0x30
u8 nways; // 0x31
u8 gca; // 0x32
u64 : 64;
u16 : 16;
u8 misc; // 0x3d
u16 txfifocnt; // 0x3e
u64 : 64;
u16 wrtxfifod; // 0x48
u64 : 48;
u8 misc2; // 0x50
u64 : 64;
u16 : 16;
u8 : 8;
u8 si_actrl; // 0x5c
u8 si_status; // 0x5d
u16 : 16;
u8 si_actrl2; // 0x60
};
#pragma pack(pop)
} m_bba_mem;
std::array<u8, BBA_TXFIFO_SIZE> m_tx_fifo;
std::array<u8, BBA_RECV_SIZE> m_recv_buffer;
u32 m_recv_buffer_length = 0;
struct
{
enum
@ -273,58 +349,5 @@ private:
word |= next_page & 0xfff;
}
};
inline u16 page_ptr(int const index) const
{
return ((u16)mBbaMem[index + 1] << 8) | mBbaMem[index];
}
inline u8* ptr_from_page_ptr(int const index) const { return &mBbaMem[page_ptr(index) << 8]; }
bool IsMXCommand(u32 const data);
bool IsWriteCommand(u32 const data);
const char* GetRegisterName() const;
void MXHardReset();
void MXCommandHandler(u32 data, u32 size);
void DirectFIFOWrite(const u8* data, u32 size);
void SendFromDirectFIFO();
void SendFromPacketBuffer();
void SendComplete();
u8 HashIndex(const u8* dest_eth_addr);
bool RecvMACFilter();
void inc_rwp();
bool RecvHandlePacket();
std::unique_ptr<u8[]> mBbaMem;
std::unique_ptr<u8[]> 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();
std::unique_ptr<u8[]> mRecvBuffer;
u32 mRecvBufferLength = 0;
#if defined(_WIN32)
HANDLE mHAdapter = INVALID_HANDLE_VALUE;
OVERLAPPED mReadOverlapped = {};
OVERLAPPED mWriteOverlapped = {};
std::vector<u8> 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;
#endif
};
} // namespace ExpansionInterface

View File

@ -0,0 +1,52 @@
// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <thread>
#ifdef _WIN32
#include <Windows.h>
#include <vector>
#endif
#include "Common/Flag.h"
#include "Core/HW/EXI/EXI_DeviceEthernetBase.h"
namespace ExpansionInterface
{
class CEXIEthernetTAP : public CEXIEthernetBase
{
public:
~CEXIEthernetTAP() override;
protected:
bool Activate() override;
bool IsActivated() const override;
bool SendFrame(const u8* frame, u32 size) override;
bool RecvInit() override;
void RecvStart() override;
void RecvStop() override;
private:
static void ReadThreadHandler(CEXIEthernetTAP* self);
#ifdef _WIN32
HANDLE m_h_adapter = INVALID_HANDLE_VALUE;
OVERLAPPED m_read_overlapped = {};
OVERLAPPED m_write_overlapped = {};
std::vector<u8> m_write_buffer;
bool m_write_pending = false;
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
int m_fd = -1;
#endif
#if defined(WIN32) || defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
defined(__OpenBSD__)
std::thread m_read_thread;
Common::Flag m_read_enabled;
Common::Flag m_read_thread_shutdown;
#endif
};
} // namespace ExpansionInterface

View File

@ -0,0 +1,104 @@
// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <fcntl.h>
#include <unistd.h>
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
#include "Core/HW/EXI/EXI_DeviceEthernetTAP.h"
namespace ExpansionInterface
{
CEXIEthernetTAP::~CEXIEthernetTAP()
{
close(m_fd);
m_read_enabled.Clear();
m_read_thread_shutdown.Set();
if (m_read_thread.joinable())
m_read_thread.join();
}
bool CEXIEthernetTAP::Activate()
{
if (IsActivated())
return true;
// Assumes TunTap OS X is installed, and /dev/tun0 is not in use
// and readable / writable by the logged-in user
if ((m_fd = open("/dev/tap0", O_RDWR)) < 0)
{
ERROR_LOG(SP1, "Couldn't open /dev/tap0, unable to init BBA");
return false;
}
INFO_LOG(SP1, "BBA initialized.");
return RecvInit();
}
bool CEXIEthernetTAP::IsActivated() const
{
return m_fd != -1;
}
bool CEXIEthernetTAP::SendFrame(const u8* frame, u32 size)
{
int writtenBytes = write(m_fd, frame, size);
if ((u32)writtenBytes != size)
{
ERROR_LOG(SP1, "SendFrame(): expected to write %d bytes, instead wrote %d", size, writtenBytes);
return false;
}
else
{
SendComplete();
return true;
}
}
void CEXIEthernetTAP::ReadThreadHandler(CEXIEthernetTAP* self)
{
while (!self->m_read_thread_shutdown.IsSet())
{
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(self->m_fd, &rfds);
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 50000;
if (select(self->m_fd + 1, &rfds, nullptr, nullptr, &timeout) <= 0)
continue;
int readBytes = read(self->m_fd, &self->m_recv_buffer, BBA_RECV_SIZE);
if (readBytes < 0)
{
ERROR_LOG(SP1, "Failed to read from BBA, err=%d", readBytes);
}
else if (self->m_read_enabled.IsSet())
{
self->m_recv_buffer_length = readBytes;
self->RecvHandlePacket();
}
}
}
bool CEXIEthernetTAP::RecvInit()
{
m_read_thread = std::thread(ReadThreadHandler, this);
return true;
}
void CEXIEthernetTAP::RecvStart()
{
m_read_enabled.Set();
}
void CEXIEthernetTAP::RecvStop()
{
m_read_enabled.Clear();
}
} // namespace ExpansionInterface

View File

@ -11,7 +11,7 @@
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
#include "Core/HW/EXI/EXI_Device.h"
#include "Core/HW/EXI/EXI_DeviceEthernet.h"
#include "Core/HW/EXI/EXI_DeviceEthernetTAP.h"
#ifdef __linux__
#include <fcntl.h>
@ -25,10 +25,23 @@
namespace ExpansionInterface
{
#define NOTIMPLEMENTED(Name) \
NOTICE_LOG(SP1, "CEXIETHERNET::%s not implemented for your UNIX", Name);
#define NOTIMPLEMENTED NOTICE_LOG(SP1, "%s not implemented for your UNIX", __PRETTY_FUNCTION__);
bool CEXIETHERNET::Activate()
CEXIEthernetTAP::~CEXIEthernetTAP()
{
#ifdef __linux__
close(m_fd);
m_read_enabled.Clear();
m_read_thread_shutdown.Set();
if (m_read_thread.joinable())
m_read_thread.join();
#else
NOTIMPLEMENTED;
#endif
}
bool CEXIEthernetTAP::Activate()
{
#ifdef __linux__
if (IsActivated())
@ -37,7 +50,7 @@ bool CEXIETHERNET::Activate()
// Assumes that there is a TAP device named "Dolphin" preconfigured for
// bridge/NAT/whatever the user wants it configured.
if ((fd = open("/dev/net/tun", O_RDWR)) < 0)
if ((m_fd = open("/dev/net/tun", O_RDWR)) < 0)
{
ERROR_LOG(SP1, "Couldn't open /dev/net/tun, unable to init BBA");
return false;
@ -53,12 +66,12 @@ bool CEXIETHERNET::Activate()
strncpy(ifr.ifr_name, StringFromFormat("Dolphin%d", i).c_str(), IFNAMSIZ);
int err;
if ((err = ioctl(fd, TUNSETIFF, (void*)&ifr)) < 0)
if ((err = ioctl(m_fd, TUNSETIFF, (void*)&ifr)) < 0)
{
if (i == (MAX_INTERFACES - 1))
{
close(fd);
fd = -1;
close(m_fd);
m_fd = -1;
ERROR_LOG(SP1, "TUNSETIFF failed: Interface=%s err=%d", ifr.ifr_name, err);
return false;
}
@ -68,46 +81,29 @@ bool CEXIETHERNET::Activate()
break;
}
}
ioctl(fd, TUNSETNOCSUM, 1);
ioctl(m_fd, TUNSETNOCSUM, 1);
INFO_LOG(SP1, "BBA initialized with associated tap %s", ifr.ifr_name);
return RecvInit();
#else
NOTIMPLEMENTED("Activate");
NOTIMPLEMENTED;
return false;
#endif
}
void CEXIETHERNET::Deactivate()
bool CEXIEthernetTAP::IsActivated() const
{
#ifdef __linux__
close(fd);
fd = -1;
readEnabled.Clear();
readThreadShutdown.Set();
if (readThread.joinable())
readThread.join();
#else
NOTIMPLEMENTED("Deactivate");
#endif
}
bool CEXIETHERNET::IsActivated()
{
#ifdef __linux__
return fd != -1 ? true : false;
return m_fd != -1;
#else
return false;
#endif
}
bool CEXIETHERNET::SendFrame(const u8* frame, u32 size)
bool CEXIEthernetTAP::SendFrame(const u8* frame, u32 size)
{
#ifdef __linux__
DEBUG_LOG(SP1, "SendFrame %x\n%s", size, ArrayToString(frame, size, 0x10).c_str());
int writtenBytes = write(fd, frame, size);
int writtenBytes = write(m_fd, frame, size);
if ((u32)writtenBytes != size)
{
ERROR_LOG(SP1, "SendFrame(): expected to write %d bytes, instead wrote %d", size, writtenBytes);
@ -119,68 +115,66 @@ bool CEXIETHERNET::SendFrame(const u8* frame, u32 size)
return true;
}
#else
NOTIMPLEMENTED("SendFrame");
NOTIMPLEMENTED;
return false;
#endif
}
#ifdef __linux__
void CEXIETHERNET::ReadThreadHandler(CEXIETHERNET* self)
bool CEXIEthernetTAP::RecvInit()
{
while (!self->readThreadShutdown.IsSet())
#ifdef __linux__
m_read_thread = std::thread(ReadThreadHandler, this);
return true;
#else
NOTIMPLEMENTED;
return false;
#endif
}
void CEXIEthernetTAP::RecvStart()
{
#ifdef __linux__
m_read_enabled.Set();
#else
NOTIMPLEMENTED;
#endif
}
void CEXIEthernetTAP::RecvStop()
{
#ifdef __linux__
m_read_enabled.Clear();
#else
NOTIMPLEMENTED("RecvStop");
#endif
}
#ifdef __linux__
void CEXIEthernetTAP::ReadThreadHandler(CEXIEthernetTAP* self)
{
while (!self->m_read_thread_shutdown.IsSet())
{
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(self->fd, &rfds);
FD_SET(self->m_fd, &rfds);
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 50000;
if (select(self->fd + 1, &rfds, nullptr, nullptr, &timeout) <= 0)
if (select(self->m_fd + 1, &rfds, nullptr, nullptr, &timeout) <= 0)
continue;
int readBytes = read(self->fd, self->mRecvBuffer.get(), BBA_RECV_SIZE);
int readBytes = read(self->m_fd, &self->m_recv_buffer, BBA_RECV_SIZE);
if (readBytes < 0)
{
ERROR_LOG(SP1, "Failed to read from BBA, err=%d", readBytes);
}
else if (self->readEnabled.IsSet())
else if (self->m_read_enabled.IsSet())
{
DEBUG_LOG(SP1, "Read data: %s",
ArrayToString(self->mRecvBuffer.get(), readBytes, 0x10).c_str());
self->mRecvBufferLength = readBytes;
self->m_recv_buffer_length = readBytes;
self->RecvHandlePacket();
}
}
}
#endif
bool CEXIETHERNET::RecvInit()
{
#ifdef __linux__
readThread = std::thread(ReadThreadHandler, this);
return true;
#else
NOTIMPLEMENTED("RecvInit");
return false;
#endif
}
void CEXIETHERNET::RecvStart()
{
#ifdef __linux__
readEnabled.Set();
#else
NOTIMPLEMENTED("RecvStart");
#endif
}
void CEXIETHERNET::RecvStop()
{
#ifdef __linux__
readEnabled.Clear();
#else
NOTIMPLEMENTED("RecvStop");
#endif
}
} // namespace ExpansionInterface

View File

@ -2,13 +2,12 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "Core/HW/EXI/BBA-TAP/TAP_Win32.h"
#include "Core/HW/EXI/EXI_DeviceEthernetTAP_Win32.h"
#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 "Core/HW/EXI/EXI_DeviceEthernetTAP.h"
namespace Win32TAPHelper
{
@ -167,7 +166,32 @@ bool OpenTAP(HANDLE& adapter, const std::basic_string<TCHAR>& device_guid)
namespace ExpansionInterface
{
bool CEXIETHERNET::Activate()
CEXIEthernetTAP::~CEXIEthernetTAP()
{
if (!IsActivated())
return;
// Signal read thread to exit.
m_read_enabled.Clear();
m_read_thread_shutdown.Set();
// Cancel any outstanding requests from both this thread (writes), and the read thread.
CancelIoEx(m_h_adapter, nullptr);
// Wait for read thread to exit.
if (m_read_thread.joinable())
m_read_thread.join();
// Clean-up handles
CloseHandle(m_read_overlapped.hEvent);
CloseHandle(m_write_overlapped.hEvent);
CloseHandle(m_h_adapter);
m_h_adapter = INVALID_HANDLE_VALUE;
memset(&m_read_overlapped, 0, sizeof(m_read_overlapped));
memset(&m_write_overlapped, 0, sizeof(m_write_overlapped));
}
bool CEXIEthernetTAP::Activate()
{
if (IsActivated())
return true;
@ -183,13 +207,13 @@ bool CEXIETHERNET::Activate()
for (size_t i = 0; i < device_guids.size(); i++)
{
if (Win32TAPHelper::OpenTAP(mHAdapter, device_guids.at(i)))
if (Win32TAPHelper::OpenTAP(m_h_adapter, device_guids.at(i)))
{
INFO_LOG(SP1, "OPENED %s", device_guids.at(i).c_str());
break;
}
}
if (mHAdapter == INVALID_HANDLE_VALUE)
if (m_h_adapter == INVALID_HANDLE_VALUE)
{
PanicAlert("Failed to open any TAP");
return false;
@ -197,7 +221,7 @@ bool CEXIETHERNET::Activate()
/* get driver version info */
ULONG info[3];
if (DeviceIoControl(mHAdapter, TAP_IOCTL_GET_VERSION, &info, sizeof(info), &info, sizeof(info),
if (DeviceIoControl(m_h_adapter, TAP_IOCTL_GET_VERSION, &info, sizeof(info), &info, sizeof(info),
&len, nullptr))
{
INFO_LOG(SP1, "TAP-Win32 Driver Version %d.%d %s", info[0], info[1], info[2] ? "(DEBUG)" : "");
@ -215,7 +239,7 @@ bool CEXIETHERNET::Activate()
/* set driver media status to 'connected' */
ULONG status = TRUE;
if (!DeviceIoControl(mHAdapter, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof(status), &status,
if (!DeviceIoControl(m_h_adapter, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof(status), &status,
sizeof(status), &len, nullptr))
{
ERROR_LOG(SP1, "WARNING: The TAP-Win32 driver rejected a"
@ -224,57 +248,32 @@ bool CEXIETHERNET::Activate()
}
/* 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)
m_read_overlapped.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
m_write_overlapped.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
if (m_read_overlapped.hEvent == nullptr || m_write_overlapped.hEvent == nullptr)
return false;
mWriteBuffer.reserve(1518);
m_write_buffer.reserve(1518);
return RecvInit();
}
void CEXIETHERNET::Deactivate()
bool CEXIEthernetTAP::IsActivated() const
{
if (!IsActivated())
return;
// 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));
return m_h_adapter != INVALID_HANDLE_VALUE;
}
bool CEXIETHERNET::IsActivated()
void CEXIEthernetTAP::ReadThreadHandler(CEXIEthernetTAP* self)
{
return mHAdapter != INVALID_HANDLE_VALUE;
}
void CEXIETHERNET::ReadThreadHandler(CEXIETHERNET* self)
{
while (!self->readThreadShutdown.IsSet())
while (!self->m_read_thread_shutdown.IsSet())
{
DWORD transferred;
// Read from TAP into internal buffer.
if (ReadFile(self->mHAdapter, self->mRecvBuffer.get(), BBA_RECV_SIZE, &transferred,
&self->mReadOverlapped))
if (ReadFile(self->m_h_adapter, &self->m_recv_buffer, BBA_RECV_SIZE, &transferred,
&self->m_read_overlapped))
{
// Returning immediately is not likely to happen, but if so, reset the event state manually.
ResetEvent(self->mReadOverlapped.hEvent);
ResetEvent(self->m_read_overlapped.hEvent);
}
else
{
@ -286,7 +285,7 @@ void CEXIETHERNET::ReadThreadHandler(CEXIETHERNET* self)
}
// Block until the read completes.
if (!GetOverlappedResult(self->mHAdapter, &self->mReadOverlapped, &transferred, TRUE))
if (!GetOverlappedResult(self->m_h_adapter, &self->m_read_overlapped, &transferred, TRUE))
{
// If CancelIO was called, we should exit (the flag will be set).
if (GetLastError() == ERROR_OPERATION_ABORTED)
@ -299,38 +298,34 @@ 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());
if (self->readEnabled.IsSet())
if (self->m_read_enabled.IsSet())
{
self->mRecvBufferLength = transferred;
self->m_recv_buffer_length = transferred;
self->RecvHandlePacket();
}
}
}
bool CEXIETHERNET::SendFrame(const u8* frame, u32 size)
bool CEXIEthernetTAP::SendFrame(const 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)
if (m_write_pending)
{
// Wait for previous write to complete.
if (!GetOverlappedResult(mHAdapter, &mWriteOverlapped, &transferred, TRUE))
if (!GetOverlappedResult(m_h_adapter, &m_write_overlapped, &transferred, TRUE))
ERROR_LOG(SP1, "GetOverlappedResult failed (err=0x%X)", GetLastError());
}
// Copy to write buffer.
mWriteBuffer.assign(frame, frame + size);
mWritePending = true;
m_write_buffer.assign(frame, frame + size);
m_write_pending = true;
// Queue async write.
if (WriteFile(mHAdapter, mWriteBuffer.data(), size, &transferred, &mWriteOverlapped))
if (WriteFile(m_h_adapter, m_write_buffer.data(), size, &transferred, &m_write_overlapped))
{
// Returning immediately is not likely to happen, but if so, reset the event state manually.
ResetEvent(mWriteOverlapped.hEvent);
ResetEvent(m_write_overlapped.hEvent);
}
else
{
@ -338,8 +333,8 @@ bool CEXIETHERNET::SendFrame(const u8* frame, u32 size)
if (GetLastError() != ERROR_IO_PENDING)
{
ERROR_LOG(SP1, "WriteFile failed (err=0x%X)", GetLastError());
ResetEvent(mWriteOverlapped.hEvent);
mWritePending = false;
ResetEvent(m_write_overlapped.hEvent);
m_write_pending = false;
return false;
}
}
@ -349,19 +344,19 @@ bool CEXIETHERNET::SendFrame(const u8* frame, u32 size)
return true;
}
bool CEXIETHERNET::RecvInit()
bool CEXIEthernetTAP::RecvInit()
{
readThread = std::thread(ReadThreadHandler, this);
m_read_thread = std::thread(ReadThreadHandler, this);
return true;
}
void CEXIETHERNET::RecvStart()
void CEXIEthernetTAP::RecvStart()
{
readEnabled.Set();
m_read_enabled.Set();
}
void CEXIETHERNET::RecvStop()
void CEXIEthernetTAP::RecvStop()
{
readEnabled.Clear();
m_read_enabled.Clear();
}
} // namespace ExpansionInterface

View File

@ -48,6 +48,8 @@ add_executable(dolphin-emu
Config/ARCodeWidget.cpp
Config/ARCodeWidget.h
Config/CheatCodeEditor.cpp
Config/ARCodeWidget.cpp
Config/BBAConfigWidget.cpp
Config/CheatCodeEditor.h
Config/CheatWarningWidget.cpp
Config/CheatWarningWidget.h

View File

@ -0,0 +1,94 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "DolphinQt/Config/BBAConfigWidget.h"
#include <QDialogButtonBox>
#include <QFormLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
#include <QSpinBox>
#include <QToolButton>
#include <QVBoxLayout>
#include "Common/Network.h"
BBAConfigWidget::BBAConfigWidget(QWidget* parent) : QDialog(parent)
{
setWindowTitle(tr("Broadband Adapter Configuration"));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
auto* vbox_layout = new QVBoxLayout(this);
{
auto* form_layout = new QFormLayout;
{
auto* hbox_layout = new QHBoxLayout;
m_mac_addr = new QLineEdit(this);
m_mac_addr->setPlaceholderText(tr("Leave empty for random"));
connect(m_mac_addr, &QLineEdit::textChanged, this, &BBAConfigWidget::TextChanged);
hbox_layout->addWidget(m_mac_addr);
{
auto* button = new QToolButton(this);
button->setText(tr("Randomize"));
connect(button, &QAbstractButton::pressed, this, &BBAConfigWidget::GenerateMac);
hbox_layout->addWidget(button);
}
form_layout->addRow(tr("MAC address:"), hbox_layout);
}
vbox_layout->addLayout(form_layout, 1);
}
{
auto* button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Close, this);
connect(button_box, &QDialogButtonBox::accepted, this, &BBAConfigWidget::Submit);
connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
vbox_layout->addWidget(button_box, 1);
}
setLayout(vbox_layout);
}
QString BBAConfigWidget::MacAddr() const
{
return m_mac_addr->text();
}
void BBAConfigWidget::SetMacAddr(const QString& mac_addr)
{
m_mac_addr->setText(mac_addr);
}
void BBAConfigWidget::Submit()
{
if (!MacAddr().isEmpty() && !Common::StringToMacAddress(MacAddr().toStdString()))
{
QMessageBox::warning(this, tr("Invalid MAC address!"), tr("Invalid MAC address!"));
return;
}
accept();
}
void BBAConfigWidget::GenerateMac()
{
m_mac_addr->setText(QString::fromStdString(
Common::MacAddressToString(Common::GenerateMacAddress(Common::MACConsumer::BBA))));
}
void BBAConfigWidget::TextChanged(const QString& text)
{
QString inputMask;
if (!text.isEmpty() && text != QStringLiteral(":::::"))
inputMask = QStringLiteral("HH:HH:HH:HH:HH:HH;_");
if (m_mac_addr->inputMask() != inputMask)
m_mac_addr->setInputMask(inputMask);
}

View File

@ -0,0 +1,29 @@
// Copyright 2019 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <QDialog>
class QLineEdit;
class QSpinBox;
class BBAConfigWidget : public QDialog
{
Q_OBJECT
public:
explicit BBAConfigWidget(QWidget* parent = nullptr);
QString MacAddr() const;
void SetMacAddr(const QString& mac_addr);
private slots:
void Submit();
void GenerateMac();
void TextChanged(const QString& text);
private:
QLineEdit* m_mac_addr;
};

View File

@ -60,6 +60,7 @@
<QtMoc Include="CheatsManager.h" />
<QtMoc Include="Config\CheatCodeEditor.h" />
<QtMoc Include="Config\ARCodeWidget.h" />
<QtMoc Include="Config\BBAConfigWidget.h" />
<QtMoc Include="Config\CheatWarningWidget.h" />
<QtMoc Include="Config\ControllersWindow.h" />
<QtMoc Include="Config\FilesystemWidget.h" />
@ -178,6 +179,7 @@
<ClCompile Include="$(QtMocOutPrefix)AdvancedWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)AspectRatioWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)AudioPane.cpp" />
<ClCompile Include="$(QtMocOutPrefix)BBAConfigWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)BlockUserInputFilter.cpp" />
<ClCompile Include="$(QtMocOutPrefix)BreakpointWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)CheatCodeEditor.cpp" />
@ -288,6 +290,7 @@
<ClCompile Include="CheatsManager.cpp" />
<ClCompile Include="Config\CheatCodeEditor.cpp" />
<ClCompile Include="Config\ARCodeWidget.cpp" />
<ClCompile Include="Config\BBAConfigWidget.cpp" />
<ClCompile Include="Config\CheatWarningWidget.cpp" />
<ClCompile Include="Config\ControllersWindow.cpp" />
<ClCompile Include="Config\FilesystemWidget.cpp" />

View File

@ -27,6 +27,7 @@
#include "Core/HW/EXI/EXI.h"
#include "Core/HW/GCMemcard/GCMemcard.h"
#include "DolphinQt/Config/BBAConfigWidget.h"
#include "DolphinQt/Config/Mapping/MappingWindow.h"
#include "DolphinQt/GCMemcardManager.h"
#include "DolphinQt/QtUtils/ModalMessageBox.h"
@ -100,10 +101,10 @@ void GameCubePane::CreateWidgets()
// Add SP1 devices
for (const auto& entry :
{std::make_pair(tr("<Nothing>"), ExpansionInterface::EXIDEVICE_NONE),
std::make_pair(tr("Dummy"), ExpansionInterface::EXIDEVICE_DUMMY),
std::make_pair(tr("Broadband Adapter"), ExpansionInterface::EXIDEVICE_ETH)})
for (const auto& entry : {std::make_pair(tr("<Nothing>"), ExpansionInterface::EXIDEVICE_NONE),
std::make_pair(tr("Dummy"), ExpansionInterface::EXIDEVICE_DUMMY),
std::make_pair(tr("Broadband Adapter (TAP)", "virtual network device"),
ExpansionInterface::EXIDEVICE_ETH_TAP)})
{
m_slot_combos[2]->addItem(entry.first, entry.second);
}
@ -158,7 +159,7 @@ 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_TAP);
break;
}
@ -170,7 +171,9 @@ void GameCubePane::OnConfigPressed(int slot)
QString filter;
bool memcard = false;
switch (m_slot_combos[slot]->currentData().toInt())
const auto current_data = m_slot_combos[slot]->currentData().toInt();
switch (current_data)
{
case ExpansionInterface::EXIDEVICE_MEMORYCARD:
filter = tr("GameCube Memory Cards (*.raw *.gcp)");
@ -182,14 +185,14 @@ void GameCubePane::OnConfigPressed(int slot)
case ExpansionInterface::EXIDEVICE_MIC:
MappingWindow(this, MappingWindow::Type::MAPPING_GC_MICROPHONE, slot).exec();
return;
case ExpansionInterface::EXIDEVICE_ETH:
case ExpansionInterface::EXIDEVICE_ETH_TAP:
{
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);
if (ok)
SConfig::GetInstance().m_bba_mac = new_mac.toStdString();
BBAConfigWidget dialog(this);
dialog.SetMacAddr(QString::fromStdString(SConfig::GetInstance().m_bba_mac));
if (dialog.exec() == QDialog::Accepted)
{
SConfig::GetInstance().m_bba_mac = dialog.MacAddr().toStdString();
}
return;
}
default:
@ -198,7 +201,7 @@ void GameCubePane::OnConfigPressed(int slot)
QString filename = QFileDialog::getSaveFileName(
this, tr("Choose a file to open"), QString::fromStdString(File::GetUserPath(D_GCUSER_IDX)),
filter, 0, QFileDialog::DontConfirmOverwrite);
filter, nullptr, QFileDialog::DontConfirmOverwrite);
if (filename.isEmpty())
return;