summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorsktsqrl <sktsqrl@gmail.com>2012-06-29 17:33:56 -0700
committersktsqrl <sktsqrl@gmail.com>2012-06-29 17:33:56 -0700
commit3cb6e4a8647e62a6074caeeff18392096e619e47 (patch)
tree3acd0aea8ef8bd9c14241a7f40a17c4a057a362d /Source/Core
parentb4ec836aca4392a86b864dc58b1030ca616fe0d5 (diff)
Redo BBA once again. Now it works on windows, if crappily.
To use, install OpenVPN's TAP device driver. Then create a network bridge between the TAP and your device connected to the internet. TODO: proper overlapped read - can look at qemu impl non-windows impl
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp282
-rw-r--r--Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp728
-rw-r--r--Source/Core/Core/Src/HW/EXI_DeviceEthernet.h312
3 files changed, 654 insertions, 668 deletions
diff --git a/Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp b/Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp
index eee496ace6..d56cc9363e 100644
--- a/Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp
+++ b/Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp
@@ -187,27 +187,9 @@ bool OpenTAP(HANDLE& adapter, const std::string device_guid)
} // namespace Win32TAPHelper
-bool CEXIETHERNET::deactivate()
+bool CEXIETHERNET::Activate()
{
- INFO_LOG(SP1, "Deactivating BBA...");
- if (!isActivated())
- return true;
- CloseHandle(mHRecvEvent);
- mHRecvEvent = INVALID_HANDLE_VALUE;
- CloseHandle(mHAdapter);
- mHAdapter = INVALID_HANDLE_VALUE;
- INFO_LOG(SP1, "Success!");
- return true;
-}
-
-bool CEXIETHERNET::isActivated()
-{
- return mHAdapter != INVALID_HANDLE_VALUE;
-}
-
-bool CEXIETHERNET::activate()
-{
- if (isActivated())
+ if (IsActivated())
return true;
INFO_LOG(SP1, "Activating BBA...");
@@ -237,16 +219,15 @@ bool CEXIETHERNET::activate()
/* get driver version info */
ULONG info[3];
- if (DeviceIoControl (mHAdapter, TAP_IOCTL_GET_VERSION,
- &info, sizeof (info), &info, sizeof (info), &len, NULL))
+ if (DeviceIoControl(mHAdapter, TAP_IOCTL_GET_VERSION,
+ &info, sizeof(info), &info, sizeof(info), &len, NULL))
{
INFO_LOG(SP1, "TAP-Win32 Driver Version %d.%d %s",
- info[0], info[1], (info[2] ? "(DEBUG)" : ""));
+ info[0], info[1], info[2] ? "(DEBUG)" : "");
}
- if ( !(info[0] > TAP_WIN32_MIN_MAJOR
- || (info[0] == TAP_WIN32_MIN_MAJOR && info[1] >= TAP_WIN32_MIN_MINOR)) )
+ if (!(info[0] > TAP_WIN32_MIN_MAJOR || (info[0] == TAP_WIN32_MIN_MAJOR && info[1] >= TAP_WIN32_MIN_MINOR)))
{
- PanicAlertT("ERROR: This version of Dolphin requires a TAP-Win32 driver"
+ PanicAlertT("ERROR: This version of Dolphin requires a TAP-Win32 driver"
" that is at least version %d.%d -- If you recently upgraded your Dolphin"
" distribution, a reboot is probably required at this point to get"
" Windows to see the new driver.",
@@ -254,46 +235,42 @@ bool CEXIETHERNET::activate()
return false;
}
- /* get driver MTU */
- if (!DeviceIoControl(mHAdapter, TAP_IOCTL_GET_MTU,
- &mMtu, sizeof (mMtu), &mMtu, sizeof (mMtu), &len, NULL))
- {
- INFO_LOG(SP1, "Couldn't get device MTU");
- return false;
- }
- INFO_LOG(SP1, "TAP-Win32 MTU=%d (ignored)", mMtu);
-
/* set driver media status to 'connected' */
ULONG status = TRUE;
if (!DeviceIoControl(mHAdapter, TAP_IOCTL_SET_MEDIA_STATUS,
- &status, sizeof (status), &status, sizeof (status), &len, NULL))
+ &status, sizeof(status), &status, sizeof(status), &len, NULL))
{
INFO_LOG(SP1, "WARNING: The TAP-Win32 driver rejected a"
"TAP_IOCTL_SET_MEDIA_STATUS DeviceIoControl call.");
return false;
}
- //set up recv event
- if ((mHRecvEvent = CreateEvent(NULL, false, false, NULL)) == NULL)
- {
- INFO_LOG(SP1, "Failed to create recv event:%x", GetLastError());
- return false;
- }
- ZeroMemory(&mReadOverlapped, sizeof(mReadOverlapped));
- resume();
+ //RecvInit();
INFO_LOG(SP1, "Success!");
return true;
}
-bool CEXIETHERNET::CheckRecieved()
+void CEXIETHERNET::Deactivate()
{
- if (!isActivated())
- return false;
+ if (!IsActivated())
+ return;
- return false;
+ RecvStop();
+
+ CloseHandle(mHRecvEvent);
+ CloseHandle(mHAdapter);
+
+ mHRecvEvent = INVALID_HANDLE_VALUE;
+ mHAdapter = INVALID_HANDLE_VALUE;
+}
+
+bool CEXIETHERNET::IsActivated()
+{
+ return mHAdapter != INVALID_HANDLE_VALUE;
}
+// TODO check if still needed
// Required for lwip...not sure why
void fixup_ip_checksum(u16 *dataptr, u16 len)
{
@@ -313,220 +290,97 @@ void fixup_ip_checksum(u16 *dataptr, u16 len)
*chksum = Common::swap16(~acc);
}
-bool CEXIETHERNET::sendPacket(u8 *etherpckt, int size)
+bool CEXIETHERNET::SendFrame(u8 *frame, u32 size)
{
- fixup_ip_checksum((u16*)etherpckt + 7, 20);
- INFO_LOG(SP1, "Packet (%zu):\n%s", size, ArrayToString(etherpckt, size, 0, 16).c_str());
+ //fixup_ip_checksum((u16*)frame + 7, 20);
+
+ DEBUG_LOG(SP1, "SendFrame %x\n%s", size, ArrayToString(frame, size, 0x10).c_str());
DWORD numBytesWrit;
OVERLAPPED overlap;
ZeroMemory(&overlap, sizeof(overlap));
//overlap.hEvent = mHRecvEvent;
- if (!WriteFile(mHAdapter, etherpckt, size, &numBytesWrit, &overlap))
+
+ if (!WriteFile(mHAdapter, frame, size, &numBytesWrit, &overlap))
{
// Fail Boat
DWORD res = GetLastError();
- INFO_LOG(SP1, "Failed to send packet with error 0x%X", res);
+ WARN_LOG(SP1, "Failed to send packet with error 0x%X", res);
}
+
if (numBytesWrit != size)
{
- INFO_LOG(SP1, "BBA sendPacket %i only got %i bytes sent!", size, numBytesWrit);
+ WARN_LOG(SP1, "BBA SendFrame %i only got %i bytes sent!", size, numBytesWrit);
return false;
}
- recordSendComplete();
+
+ SendComplete();
+
return true;
}
-bool CEXIETHERNET::handleRecvdPacket()
+VOID CALLBACK CEXIETHERNET::ReadWaitCallback(PVOID lpParameter, BOOLEAN TimerFired)
{
- static u32 mPacketsRecvd = 0;
- INFO_LOG(SP1, "handleRecvdPacket Packet %i (%i):\n%s", mPacketsRecvd, mRecvBufferLength,
- ArrayToString(mRecvBuffer, mRecvBufferLength, 0, 16).c_str());
-
- static u8 broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
- if (memcmp(mRecvBuffer, broadcast, 6) && memcmp(mRecvBuffer, mac_address, 6))
- {
- INFO_LOG(SP1, "dropping packet - not for us");
- goto wait_for_next;
- }
-
- int rbwpp = (int)(mCbw.p_write() + CB_OFFSET); // read buffer write page pointer
- u32 available_bytes_in_cb;
- if (rbwpp < mRBRPP)
- available_bytes_in_cb = mRBRPP - rbwpp;
- else if (rbwpp == mRBRPP)
- available_bytes_in_cb = mRBEmpty ? CB_SIZE : 0;
- else //rbwpp > mRBRPP
- available_bytes_in_cb = CB_SIZE - rbwpp + (mRBRPP - CB_OFFSET);
-
- INFO_LOG(SP1, "rbwpp %i mRBRPP %04x available_bytes_in_cb %i",
- rbwpp, mRBRPP, available_bytes_in_cb);
-
- _dbg_assert_(SP1, available_bytes_in_cb <= CB_SIZE);
- if (available_bytes_in_cb != CB_SIZE)//< mRecvBufferLength + SIZEOF_RECV_DESCRIPTOR)
- return true;
- cbwriteDescriptor(mRecvBufferLength);
- mCbw.write(mRecvBuffer, mRecvBufferLength);
- mCbw.align();
- rbwpp = (int)(mCbw.p_write() + CB_OFFSET);
+ CEXIETHERNET* self = (CEXIETHERNET*)lpParameter;
- INFO_LOG(SP1, "rbwpp %i", rbwpp);
-
- mPacketsRecvd++;
- mRecvBufferLength = 0;
-
- if ((mBbaMem[BBA_IMR] & INT_R) && !(mBbaMem[BBA_IR] & INT_R))
- {
- if (mBbaMem[2])
- {
- mBbaMem[BBA_IR] |= INT_R;
- INFO_LOG(SP1, "BBA Recv interrupt raised");
- m_bInterruptSet = true;
- }
- }
-
-wait_for_next:
- if (mBbaMem[BBA_NCRA] & NCRA_SR)
- startRecv();
+ GetOverlappedResult(self->mHAdapter, &self->mReadOverlapped,
+ &self->mRecvBufferLength, false);
- return true;
+ self->RecvHandlePacket();
}
-bool CEXIETHERNET::resume()
+bool CEXIETHERNET::RecvInit()
{
- if (!isActivated())
- return true;
+ // Set up recv event
- INFO_LOG(SP1, "BBA resume");
- //mStop = false;
+ if ((mHRecvEvent = CreateEvent(NULL, false, false, NULL)) == NULL)
+ {
+ INFO_LOG(SP1, "Failed to create recv event:%x", GetLastError());
+ return false;
+ }
+
+ ZeroMemory(&mReadOverlapped, sizeof(mReadOverlapped));
RegisterWaitForSingleObject(&mHReadWait, mHRecvEvent, ReadWaitCallback,
this, INFINITE, WT_EXECUTEDEFAULT);//WT_EXECUTEINWAITTHREAD
mReadOverlapped.hEvent = mHRecvEvent;
- if (mBbaMem[BBA_NCRA] & NCRA_SR)
- startRecv();
-
- INFO_LOG(SP1, "BBA resume complete");
return true;
}
-bool CEXIETHERNET::startRecv()
+bool CEXIETHERNET::RecvStart()
{
- if (!isActivated())
- return false;// Should actually be an assert
+ if (!IsActivated())
+ return false;
- INFO_LOG(SP1, "startRecv... ");
-
- if (mWaiting)
- {
- INFO_LOG(SP1, "already waiting");
- return true;
- }
+ if (mHRecvEvent == INVALID_HANDLE_VALUE)
+ RecvInit();
DWORD res = ReadFile(mHAdapter, mRecvBuffer, BBA_RECV_SIZE,
&mRecvBufferLength, &mReadOverlapped);
- if (res)
+ if (!res && (GetLastError() != ERROR_IO_PENDING))
{
- // Operation completed immediately
- INFO_LOG(SP1, "completed, res %i", res);
- mWaiting = true;
+ // error occurred
+ return false;
}
- else
+
+ if (res)
{
- res = GetLastError();
- if (res == ERROR_IO_PENDING)
- {
- //'s ok :)
- INFO_LOG(SP1, "pending");
- // WaitCallback will be called
- mWaiting = true;
- }
- else
- {
- // error occurred
- return false;
- }
+ ERROR_LOG(SP1, "RECV COMPLETED IMMEDIATELY");
+ RecvHandlePacket();
}
return true;
}
-VOID CALLBACK CEXIETHERNET::ReadWaitCallback(PVOID lpParameter, BOOLEAN TimerFired)
+void CEXIETHERNET::RecvStop()
{
- static int sNumber = 0;
- int cNumber = sNumber++;
- INFO_LOG(SP1, "WaitCallback %i", cNumber);
- __assume(!TimerFired);
- CEXIETHERNET* self = (CEXIETHERNET*)lpParameter;
- __assume(self->mHAdapter != INVALID_HANDLE_VALUE);
- GetOverlappedResult(self->mHAdapter, &self->mReadOverlapped,
- &self->mRecvBufferLength, false);
- self->mWaiting = false;
- self->handleRecvdPacket();
- INFO_LOG(SP1, "WaitCallback %i done", cNumber);
-}
-
-union bba_descr
-{
- struct { u32 next_packet_ptr:12, packet_len:12, status:8; };
- u32 word;
-};
-
-bool CEXIETHERNET::cbwriteDescriptor(u32 size)
-{
- if (size < SIZEOF_ETH_HEADER)
- {
- INFO_LOG(SP1, "Packet too small: %i bytes", size);
- return false;
- }
-
- // The descriptor supposed to include the size of itself
- size += SIZEOF_RECV_DESCRIPTOR;
-
- //We should probably not implement wraparound here,
- //since neither tmbinc, riptool.dol, or libogc does...
- if (mCbw.p_write() + SIZEOF_RECV_DESCRIPTOR >= CB_SIZE)
- {
- INFO_LOG(SP1, "The descriptor won't fit");
- return false;
- }
- if (size >= CB_SIZE)
- {
- INFO_LOG(SP1, "Packet too big: %i bytes", size);
- return false;
- }
-
- bba_descr descr;
- descr.word = 0;
- descr.packet_len = size;
- descr.status = 0;
- u32 npp;
- if (mCbw.p_write() + size < CB_SIZE)
- {
- npp = (u32)(mCbw.p_write() + size + CB_OFFSET);
- }
- else
- {
- npp = (u32)(mCbw.p_write() + size + CB_OFFSET - CB_SIZE);
- }
-
- npp = (npp + 0xff) & ~0xff;
-
- if (npp >= CB_SIZE + CB_OFFSET)
- npp -= CB_SIZE;
-
- descr.next_packet_ptr = npp >> 8;
- //DWORD swapped = swapw(descr.word);
- //next_packet_ptr:12, packet_len:12, status:8;
- INFO_LOG(SP1, "Writing descriptor 0x%08X @ 0x%04X: next 0x%03X len 0x%03X status 0x%02X",
- descr.word, mCbw.p_write() + CB_OFFSET, descr.next_packet_ptr,
- descr.packet_len, descr.status);
+ if (!IsActivated())
+ return;
- mCbw.write(&descr.word, SIZEOF_RECV_DESCRIPTOR);
-
- return true;
+ CancelIo(mHAdapter);
}
+
//#pragma optimize("",on)
diff --git a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp
index 6572233b20..d4351d35ab 100644
--- a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp
+++ b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp
@@ -19,19 +19,18 @@
//#pragma optimize("",off)
#include "EXI_Device.h"
#include "EXI_DeviceEthernet.h"
+#include "StringUtil.h"
-#define MAKE(type, arg) (*(type *)&(arg))
-
-CEXIETHERNET::CEXIETHERNET(const std::string& mac_addr) :
- m_uPosition(0),
- m_uCommand(0),
- mWriteBuffer(2048),
- mCbw(mBbaMem + CB_OFFSET, CB_SIZE)
+CEXIETHERNET::CEXIETHERNET(const std::string& mac_addr)
{
+ tx_fifo = new u8[1518];
+ mBbaMem = new u8[BBA_MEM_SIZE];
+ mRecvBuffer = new u8 [BBA_RECV_SIZE];
+
+ MXHardReset();
+
const u8 mac_address_default[6] =
{ 0x00, 0x09, 0xbf, 0x01, 0x00, 0xc1 };
-
- memset(mBbaMem, 0, BBA_MEM_SIZE);
int x = 0;
u8 new_addr[6] = { 0 };
@@ -46,21 +45,14 @@ CEXIETHERNET::CEXIETHERNET(const std::string& mac_addr) :
new_addr[x / 2] |= (c - 'a' + 10) << ((x & 1) ? 0 : 4); x++;
}
}
+
if (x / 2 == 6)
- memcpy(mac_address, new_addr, 6);
+ memcpy(&mBbaMem[BBA_NAFR_PAR0], new_addr, 6);
else
- memcpy(mac_address, mac_address_default, 6);
-
- ERROR_LOG(SP1, "BBA MAC %x%x%x%x%x%x",
- mac_address[0], mac_address[1], mac_address[2],
- mac_address[3], mac_address[4], mac_address[5]);
+ memcpy(&mBbaMem[BBA_NAFR_PAR0], mac_address_default, 6);
- mWriteP = INVALID_P;
- mReadP = INVALID_P;
- mWaiting = false;
- mReadyToSend = false;
- Activated = false;
- m_bInterruptSet = false;
+ // hax .. fully established 100BASE-T link
+ mBbaMem[BBA_NWAYS] = NWAYS_LS100 | NWAYS_LPNWAY | NWAYS_100TXF | NWAYS_ANCLPT;
#ifdef _WIN32
mHAdapter = INVALID_HANDLE_VALUE;
@@ -69,34 +61,25 @@ CEXIETHERNET::CEXIETHERNET(const std::string& mac_addr) :
#endif
mRecvBufferLength = 0;
-
- mExpectSpecialImmRead = false;
- mExpectVariableLengthImmWrite = false;
}
CEXIETHERNET::~CEXIETHERNET()
{
- /*/ crashy crashy
- if (isActivated())
- deactivate();
- //*/
+ Deactivate();
+
+ delete tx_fifo;
+ delete mBbaMem;
+ delete mRecvBuffer;
}
void CEXIETHERNET::SetCS(int cs)
{
- INFO_LOG(SP1, "chip select: %s%s", cs ? "true" : "false",
- mExpectVariableLengthImmWrite ? ", expecting variable write" : "");
- if (!cs)
+ DEBUG_LOG(SP1, "chip select: %s", cs ? "true" : "false");
+
+ if (cs)
{
- if (mExpectVariableLengthImmWrite)
- {
- INFO_LOG(SP1, "Variable write complete. Final size: %i bytes",
- mWriteBuffer.size());
- mExpectVariableLengthImmWrite = false;
- mReadyToSend = true;
- }
- mExpectSpecialImmRead = false;
- mWriteP = mReadP = INVALID_P;
+ // Invalidate the previous transfer
+ transfer.valid = false;
}
}
@@ -105,336 +88,477 @@ bool CEXIETHERNET::IsPresent()
return true;
}
-void CEXIETHERNET::Update()
-{
- return;
-}
-
bool CEXIETHERNET::IsInterruptSet()
{
- return m_bInterruptSet;
+ return !!(exi_status.interrupt & exi_status.interrupt_mask);
}
-void CEXIETHERNET::recordSendComplete()
+void CEXIETHERNET::ImmWrite(u32 data, u32 size)
{
- mBbaMem[BBA_NCRA] &= ~(NCRA_ST0 | NCRA_ST1);
- if (mBbaMem[BBA_IMR] & INT_T)
+ data >>= (4 - size) * 8;
+
+ if (!transfer.valid)
{
- mBbaMem[BBA_IR] |= INT_T;
- INFO_LOG(SP1, "\t\tBBA Send interrupt raised");
- m_bInterruptSet = true;
- }
- // TODO why did sonic put this here?
- //startRecv();
-}
+ transfer.valid = true;
+ transfer.region = IsMXCommand(data) ? transfer.MX : transfer.EXI;
+ if (transfer.region == transfer.EXI)
+ transfer.address = ((data & ~0xc000) >> 8) & 0xff;
+ else
+ transfer.address = (data >> 8) & 0xffff;
+ transfer.direction = IsWriteCommand(data) ? transfer.WRITE : transfer.READ;
-bool CEXIETHERNET::checkRecvBuffer()
-{
- if (mRecvBufferLength)
- handleRecvdPacket();
- return true;
-}
+ WARN_LOG(SP1, "%s %s %s %x",
+ IsMXCommand(data) ? "mx " : "exi",
+ IsWriteCommand(data) ? "write" : "read ",
+ GetRegisterName(),
+ transfer.address);
-void CEXIETHERNET::ImmWrite(u32 data, u32 size)
-{
- if (size == 1)
- data = (u8)Common::swap32(data);
- else if (size == 2)
- data >>= 16;
+ if (transfer.address == BBA_IOB && transfer.region == transfer.MX)
+ exit(0);
- INFO_LOG(SP1, "imm write %0*x writep 0x%x", size * 2, data, mWriteP);
-
- if (mExpectVariableLengthImmWrite)
- {
- INFO_LOG(SP1, "\tvariable length imm write");
- if (size == 4)
- data = Common::swap32(data);
- else if (size == 2)
- data = Common::swap16((u16)data);
- mWriteBuffer.write(size, &data);
+ // transfer has been setup
return;
}
- else if (mWriteP != INVALID_P)
+
+ // Reach here if we're actually writing data to the EXI or MX region.
+
+ WARN_LOG(SP1, "%s write %0*x",
+ transfer.region == transfer.MX ? "mx " : "exi", size * 2, data);
+
+ if (transfer.region == transfer.EXI)
{
- if (mWriteP + size > BBA_MEM_SIZE)
+ switch (transfer.address)
{
- ERROR_LOG(SP1, "write error: writep + size = %04x + %i", mWriteP, size);
- return;
+ case INTERRUPT:
+ exi_status.interrupt &= data ^ 0xff;
+ break;
+ case INTERRUPT_MASK:
+ exi_status.interrupt_mask = data;
+ break;
}
- INFO_LOG(SP1, "\twrite to BBA address %0*x, %i byte%s: %0*x",
- mWriteP >= CB_OFFSET ? 4 : 2, mWriteP, size, (size==1?"":"s"), size*2, data);
+ }
+ else
+ {
+ MXCommandHandler(data, size);
+ }
+}
- switch (mWriteP)
+u32 CEXIETHERNET::ImmRead(u32 size)
+{
+ u32 ret = 0;
+
+ if (transfer.region == transfer.EXI)
+ {
+ switch (transfer.address)
{
- case BBA_NCRA:
- {
- INFO_LOG(SP1, "\t\tNCRA");
-
- // TODO is it really necessary to check last value?
- u8 NCRA_old = mBbaMem[BBA_NCRA];
- mBbaMem[BBA_NCRA] = data;
- #define RISE(flags) ((mBbaMem[BBA_NCRA] & flags) && !(NCRA_old & flags))
-
- if (RISE(NCRA_RESET))
- {
- INFO_LOG(SP1, "\t\treset");
- activate();
- }
- if (RISE(NCRA_SR))
- {
- INFO_LOG(SP1, "\t\tstart receive");
- startRecv();
- }
- if (RISE(NCRA_ST1))
- {
- INFO_LOG(SP1, "\t\tstart transmit");
- if (!mReadyToSend)
- {
- INFO_LOG(SP1, "\t\ttramsit without a packet!");
- }
- sendPacket(mWriteBuffer.p(), mWriteBuffer.size());
- mReadyToSend = false;
- }
- }
+ case EXI_ID:
+ ret = EXI_DEVTYPE_ETHER;
break;
- case 0x03:
- mBbaMem[0x03] = ~data;
- if (data & 0x80)
- m_bInterruptSet = false;
+ case REVISION_ID:
+ ret = exi_status.revision_id;
break;
- case BBA_IR:
- _dbg_assert_(SP1, size == 1);
- INFO_LOG(SP1, "\t\tinterrupt reset %02x & ~(%02x) => %02x",
- mBbaMem[BBA_IR], MAKE(u8, data), mBbaMem[BBA_IR] & ~MAKE(u8, data));
- mBbaMem[BBA_IR] &= ~MAKE(u8, data);
+ case DEVICE_ID:
+ ret = exi_status.device_id;
break;
- case BBA_RWP: // RWP - Receive Buffer Write Page Pointer
- INFO_LOG(SP1, "\t\tRWP");
- _dbg_assert_(SP1, size == 2 || size == 1);
- //_dbg_assert_(SP1, data == (u32)((u16)mCbw.p_write() + CB_OFFSET) >> 8);
- if (data != (u32)((u16)mCbw.p_write() + CB_OFFSET) >> 8)
- {
- ERROR_LOG(SP1, "BBA RWP ASSERT data %x p_write %lx",
- data, (unsigned long)mCbw.p_write());
- }
- break;
- case BBA_RRP: // RRP - Receive Buffer Read Page Pointer
- INFO_LOG(SP1, "\t\tRRP");
- _dbg_assert_(SP1, size == 2 || size == 1);
- mRBRPP = (u8)data << 8; // Hope this works with both write sizes.
- mRBEmpty = mRBRPP == ((u16)mCbw.p_write() + CB_OFFSET);
- checkRecvBuffer();
+ case ACSTART:
+ ret = exi_status.acstart;
break;
- case BBA_NWAYC:
- INFO_LOG(SP1, "\t\tNWAYC");
- mBbaMem[BBA_NWAYC] = data;
- if (mBbaMem[BBA_NWAYC] & (NWAYC_ANE | NWAYC_ANS_RA))
- {
- // say we've successfully negotiated for 10 Mbit full duplex
- // should placate libogc
- mBbaMem[BBA_NWAYS] = NWAYS_LS100 | NWAYS_LPNWAY | NWAYS_ANCLPT | NWAYS_100TXF;
- }
+ case INTERRUPT:
+ ret = exi_status.interrupt;
break;
- default:
- INFO_LOG(SP1, "\t\tdefault: data: %0*x to %x", size * 2, data, mWriteP);
- memcpy(mBbaMem + mWriteP, &data, size);
- mWriteP = mWriteP + (u16)size;
}
- return;
+
+ transfer.address += size;
}
- else if (size == 2 && data == 0)
+ else
{
- INFO_LOG(SP1, "\trequest cid");
- mSpecialImmData = EXI_DEVTYPE_ETHER;
- mExpectSpecialImmRead = true;
- return;
+ for (int i = size - 1; i >= 0; i--)
+ ret |= mBbaMem[transfer.address++] << (i * 8);
+ }
+
+ WARN_LOG(SP1, "imm r%i: %0*x", size, size * 2, ret);
+
+ ret <<= (4 - size) * 8;
+
+ return ret;
+}
+
+void CEXIETHERNET::DMAWrite(u32 addr, u32 size)
+{
+ WARN_LOG(SP1, "dma w: %08x %x", addr, size);
+
+ if (transfer.region == transfer.MX &&
+ transfer.direction == transfer.WRITE &&
+ transfer.address == BBA_WRTXFIFOD)
+ {
+ DirectFIFOWrite(Memory::GetPointer(addr), size);
}
- else if ((size == 4 && (data & 0xC0000000) == 0xC0000000) ||
- (size == 2 && (data & 0x4000) == 0x4000))
+ else
{
- INFO_LOG(SP1, "\twrite to register");
- if (size == 4)
- mWriteP = (u8)getbitsw(data, 16, 23);
- else //size == 2
- mWriteP = (u8)getbitsw(data & ~0x4000, 16, 23); // Dunno about this...
+ ERROR_LOG(SP1, "dma w in %s %s mode - not implemented",
+ transfer.region == transfer.EXI ? "exi" : "mx",
+ transfer.direction == transfer.READ ? "read" : "write");
+ }
+}
- if (mWriteP == BBA_WRTXFIFOD)
+void CEXIETHERNET::DMARead(u32 addr, u32 size)
+{
+ ERROR_LOG(SP1, "dma r: %08x %x", addr, size);
+
+ memcpy(Memory::GetPointer(addr), &mBbaMem[transfer.address], size);
+
+ transfer.address += size;
+}
+
+void CEXIETHERNET::DoState(PointerWrap &p)
+{
+ p.Do(mBbaMem);
+ // TODO ... the rest...
+}
+
+bool CEXIETHERNET::IsMXCommand(u32 const data)
+{
+ return !!(data & (1 << 31));
+}
+
+bool CEXIETHERNET::IsWriteCommand(u32 const data)
+{
+ return IsMXCommand(data) ? !!(data & (1 << 30)) : !!(data & (1 << 14));
+}
+
+char const * const CEXIETHERNET::GetRegisterName() const
+{
+#define STR_RETURN(x) case x: return #x;
+
+ if (transfer.region == transfer.EXI)
+ {
+ switch (transfer.address)
{
- mWriteBuffer.clear();
- mExpectVariableLengthImmWrite = true;
- INFO_LOG(SP1, "\t\tprepared for variable length write to address 0x48");
+ STR_RETURN(EXI_ID)
+ STR_RETURN(REVISION_ID)
+ STR_RETURN(INTERRUPT)
+ STR_RETURN(INTERRUPT_MASK)
+ STR_RETURN(DEVICE_ID)
+ STR_RETURN(ACSTART)
+ STR_RETURN(HASH_READ)
+ STR_RETURN(HASH_WRITE)
+ STR_RETURN(HASH_STATUS)
+ STR_RETURN(RESET)
+ default: return "unknown";
}
- else
+ }
+ else
+ {
+ switch (transfer.address)
{
- INFO_LOG(SP1, "\t\twritep set to %0*x", size * 2, mWriteP);
+ STR_RETURN(BBA_NCRA)
+ STR_RETURN(BBA_NCRB)
+ STR_RETURN(BBA_LTPS)
+ STR_RETURN(BBA_LRPS)
+ STR_RETURN(BBA_IMR)
+ STR_RETURN(BBA_IR)
+ STR_RETURN(BBA_BP)
+ STR_RETURN(BBA_TLBP)
+ STR_RETURN(BBA_TWP)
+ STR_RETURN(BBA_IOB)
+ STR_RETURN(BBA_TRP)
+ STR_RETURN(BBA_RWP)
+ STR_RETURN(BBA_RRP)
+ STR_RETURN(BBA_RHBP)
+ STR_RETURN(BBA_RXINTT)
+ STR_RETURN(BBA_NAFR_PAR0)
+ STR_RETURN(BBA_NAFR_PAR1)
+ STR_RETURN(BBA_NAFR_PAR2)
+ STR_RETURN(BBA_NAFR_PAR3)
+ STR_RETURN(BBA_NAFR_PAR4)
+ STR_RETURN(BBA_NAFR_PAR5)
+ STR_RETURN(BBA_NAFR_MAR0)
+ STR_RETURN(BBA_NAFR_MAR1)
+ STR_RETURN(BBA_NAFR_MAR2)
+ STR_RETURN(BBA_NAFR_MAR3)
+ STR_RETURN(BBA_NAFR_MAR4)
+ STR_RETURN(BBA_NAFR_MAR5)
+ STR_RETURN(BBA_NAFR_MAR6)
+ STR_RETURN(BBA_NAFR_MAR7)
+ STR_RETURN(BBA_NWAYC)
+ STR_RETURN(BBA_NWAYS)
+ STR_RETURN(BBA_GCA)
+ STR_RETURN(BBA_MISC)
+ STR_RETURN(BBA_TXFIFOCNT)
+ STR_RETURN(BBA_WRTXFIFOD)
+ STR_RETURN(BBA_MISC2)
+ STR_RETURN(BBA_SI_ACTRL)
+ STR_RETURN(BBA_SI_STATUS)
+ STR_RETURN(BBA_SI_ACTRL2)
+ default:
+ if (transfer.address >= 0x100 &&
+ transfer.address <= 0xfff)
+ return "packet buffer";
+ else
+ return "unknown";
}
- return;
}
- else if ((size == 4 && (data & 0xC0000000) == 0x80000000) ||
- (size == 2 && (data & 0x4000) == 0x0000))
+
+#undef STR_RETURN
+}
+
+void CEXIETHERNET::MXHardReset()
+{
+ memset(mBbaMem, 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;
+}
+
+void CEXIETHERNET::MXCommandHandler(u32 data, u32 size)
+{
+ switch (transfer.address)
{
- // Read from BBA Register
- if (size == 4)
- mReadP = (data >> 8) & 0xffff;
- else //size == 2
- mReadP = (u8)getbitsw(data, 16, 23);
+ case BBA_NCRA:
+ if (data & NCRA_RESET)
+ {
+ WARN_LOG(SP1, "software reset");
+ //MXSoftReset();
+ Activate();
+ }
- INFO_LOG(SP1, "Read from BBA register 0x%x", mReadP);
+ if ((mBbaMem[BBA_NCRA] & NCRA_SR) ^ (data & NCRA_SR))
+ {
+ WARN_LOG(SP1, "%s rx", (data & NCRA_SR) ? "start" : "stop");
- switch (mReadP)
+ if (data & NCRA_SR)
+ RecvStart();
+ else
+ RecvStop();
+ }
+
+ // Only start transfer if there isn't one currently running
+ if (!(mBbaMem[BBA_NCRA] & (NCRA_ST0 | NCRA_ST1)))
{
- case BBA_NCRA:
- // These Two lines were commented out in Whinecube
- //mBbaMem[mReadP] = 0x00;
- //if(!sendInProgress())
- mBbaMem[BBA_NCRA] &= ~(NCRA_ST0 | NCRA_ST1);
- INFO_LOG(SP1, "\tNCRA %02x", mBbaMem[BBA_NCRA]);
- break;
- case BBA_NCRB: // Revision ID
- INFO_LOG(SP1, "\tNCRB");
- break;
- case BBA_NAFR_PAR0:
- INFO_LOG(SP1, "\tMac Address");
- memcpy(mBbaMem + mReadP, mac_address, 6);
- break;
- case 0x03: // status TODO more fields
- mBbaMem[mReadP] = m_bInterruptSet ? 0x80 : 0;
- INFO_LOG(SP1, "\tStatus %x", mBbaMem[mReadP]);
- break;
- case BBA_LTPS:
- INFO_LOG(SP1, "\tLPTS");
- break;
- case BBA_IMR:
- INFO_LOG(SP1, "\tIMR");
- break;
- case BBA_IR:
- INFO_LOG(SP1, "\tIR");
- break;
- case BBA_RWP:
- case BBA_RWP+1:
- MAKE(u16, mBbaMem[BBA_RWP]) = Common::swap16((u16)mCbw.p_write() + CB_OFFSET);
- INFO_LOG(SP1, "\tRWP 0x%04x", MAKE(u16, mBbaMem[mReadP]));
- break;
- case BBA_RRP:
- case BBA_RRP+1:
- MAKE(u16, mBbaMem[BBA_RRP]) = Common::swap16(mRBRPP);
- INFO_LOG(SP1, "\tRRP 0x%04x", MAKE(u16, mBbaMem[mReadP]));
- break;
- case 0x3A: // bit 1 set if no data available
- INFO_LOG(SP1, "\tBit 1 set!");
- //mBbaMem[mReadP] = !mRBEmpty;
- break;
- case BBA_TWP:
- case BBA_TWP+1:
- INFO_LOG(SP1, "\tTWP");
- break;
- case BBA_NWAYC:
- INFO_LOG(SP1, "\tNWAYC");
- break;
- case BBA_NWAYS:
- mBbaMem[BBA_NWAYS] = NWAYS_LS100 | NWAYS_LPNWAY | NWAYS_ANCLPT | NWAYS_100TXF;
- INFO_LOG(SP1, "\tNWAYS %02x", mBbaMem[BBA_NWAYS]);
- break;
- case BBA_SI_ACTRL:
- INFO_LOG(SP1, "\tSI_ACTRL");
- break;
- default:
- ERROR_LOG(SP1, "UNKNOWN BBA REG READ %02x", mReadP);
- break;
+ // TODO Might have to check TXDMA status as well?
+
+ if (data & NCRA_ST0)
+ {
+ WARN_LOG(SP1, "start tx - local dma");
+ SendFromPacketBuffer();
+ }
+ else if (data & NCRA_ST1)
+ {
+ WARN_LOG(SP1, "start tx - direct fifo");
+ SendFromDirectFIFO();
+ // Kind of a hack: send completes instantly, so we don't
+ // actually write the "send in status" bit to the register
+ data &= ~NCRA_ST1;
+ }
}
+ goto write_to_register;
+
+ case BBA_WRTXFIFOD:
+ if (size == 2)
+ data = Common::swap16(data & 0xffff);
+ else if (size == 4)
+ data = Common::swap32(data);
+ DirectFIFOWrite((u8 *)&data, size);
+ // Do not increment address
return;
+
+ case BBA_IR:
+ data &= (data & 0xff) ^ 0xff;
+ goto write_to_register;
+
+write_to_register:
+ default:
+ {
+ for (int i = size - 1; i >= 0; i--)
+ {
+ mBbaMem[transfer.address++] = (data >> (i * 8)) & 0xff;
+ }
+ }
}
+}
+
+void CEXIETHERNET::DirectFIFOWrite(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];
- ERROR_LOG(SP1, "\tNot expecting imm write of size %d", size);
- ERROR_LOG(SP1, "\t\t SKIPPING!");
+ memcpy(tx_fifo + *tx_fifo_count, data, size);
+
+ *tx_fifo_count += size;
+ *tx_fifo_count &= (1 << 12) - 1;
}
-u32 CEXIETHERNET::ImmRead(u32 size)
+void CEXIETHERNET::SendFromDirectFIFO()
+{
+ SendFrame(tx_fifo, *(u16 *)&mBbaMem[BBA_TXFIFOCNT]);
+}
+
+void CEXIETHERNET::SendFromPacketBuffer()
+{
+ ERROR_LOG(SP1, "tx packet buffer not implemented");
+}
+
+void CEXIETHERNET::SendComplete()
{
- INFO_LOG(SP1, "imm read %i readp %x", size, mReadP);
+ mBbaMem[BBA_NCRA] &= ~(NCRA_ST0 | NCRA_ST1);
+ *(u16 *)&mBbaMem[BBA_TXFIFOCNT] = 0;
- if (mExpectSpecialImmRead)
+ if (mBbaMem[BBA_IMR] & INT_T)
{
- INFO_LOG(SP1, "\tspecial imm read %08x", mSpecialImmData);
- mExpectSpecialImmRead = false;
- return mSpecialImmData;
+ mBbaMem[BBA_IR] |= INT_T;
+
+ exi_status.interrupt |= exi_status.TRANSFER;
}
- if (mReadP != INVALID_P)
+ mBbaMem[BBA_LTPS] = 0;
+}
+
+u8 CEXIETHERNET::HashIndex(u8 *dest_eth_addr)
+{
+ // Calculate crc
+ u32 crc = 0xffffffff;
+
+ for (size_t byte_num = 0; byte_num < 6; ++byte_num)
{
- if (mReadP + size > BBA_MEM_SIZE)
+ u8 cur_byte = dest_eth_addr[byte_num];
+ for (size_t bit = 0; bit < 8; ++bit)
{
- ERROR_LOG(SP1, "\tRead error: readp + size = %04x + %i", mReadP, size);
- return 0;
+ u8 carry = ((crc >> 31) & 1) ^ (cur_byte & 1);
+ crc <<= 1;
+ cur_byte >>= 1;
+ if (carry)
+ crc = (crc ^ 0x4c11db6) | carry;
}
- u32 uResult = 0;
- memcpy(&uResult, mBbaMem + mReadP, size);
+ }
- uResult = Common::swap32(uResult);
+ // return bits used as index
+ return crc >> 26;
+}
- INFO_LOG(SP1, "\tRead from BBA address %0*x, %i byte%s: %0*x",
- mReadP >= CB_OFFSET ? 4 : 2, mReadP,
- size, (size==1?"":"s"),size*2, getbitsw(uResult, 0, size * 8 - 1));
- mReadP = mReadP + size;
- return uResult;
+bool CEXIETHERNET::RecvMACFilter()
+{
+ static u8 const broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ // Accept all destination addrs?
+ if (mBbaMem[BBA_NCRB] & NCRB_PR)
+ return true;
+
+ // Unicast?
+ if ((mRecvBuffer[0] & 0x01) == 0)
+ {
+ return memcmp(mRecvBuffer, &mBbaMem[BBA_NAFR_PAR0], 6) == 0;
+ }
+ else if (memcmp(mRecvBuffer, broadcast, 6) == 0)
+ {
+ // Accept broadcast?
+ return !!(mBbaMem[BBA_NCRB] & NCRB_AB);
+ }
+ else if (mBbaMem[BBA_NCRB] & NCRB_PM)
+ {
+ // Accept all multicast
+ return true;
}
else
{
- ERROR_LOG(SP1, "Unhandled imm read size %d", size);
- return 0;
+ // Lookup the dest eth address in the hashmap
+ u16 index = HashIndex(mRecvBuffer);
+ return !!(mBbaMem[BBA_NAFR_MAR0 + index / 8] & (1 << (index % 8)));
}
}
-void CEXIETHERNET::DMAWrite(u32 addr, u32 size)
+#define PAGE_PTR(x) ((mBbaMem[x + 1] << 8) | mBbaMem[x])
+
+void CEXIETHERNET::inc_rwp()
{
- if (mExpectVariableLengthImmWrite)
- {
- INFO_LOG(SP1, "DMA Write: Address is 0x%x and size is 0x%x", addr, size);
- mWriteBuffer.write(size, Memory::GetPointer(addr));
- return;
- }
+ u16 *rwp = (u16 *)&mBbaMem[BBA_RWP];
- ERROR_LOG(SP1, "Unhandled BBA DMA write: %i, %08x", size, addr);
+ if (*rwp + 1 == PAGE_PTR(BBA_RHBP))
+ // TODO check if BP is used as well
+ *rwp = PAGE_PTR(BBA_BP);
+ else
+ (*rwp)++;
}
-void CEXIETHERNET::DMARead(u32 addr, u32 size)
+bool CEXIETHERNET::RecvHandlePacket()
{
- if (mReadP != INVALID_P)
+ if (!RecvMACFilter())
+ goto wait_for_next;
+
+ WARN_LOG(SP1, "RecvHandlePacket %x\n%s", mRecvBufferLength,
+ ArrayToString(mRecvBuffer, mRecvBufferLength, 0x100).c_str());
+
+#define PTR_FROM_PAGE_PTR(x) &mBbaMem[PAGE_PTR(x) << 8]
+
+ ERROR_LOG(SP1, "%x %x %x %x",
+ PAGE_PTR(BBA_BP),
+ PAGE_PTR(BBA_RRP),
+ PAGE_PTR(BBA_RWP),
+ PAGE_PTR(BBA_RHBP));
+
+ u8 *write_ptr = PTR_FROM_PAGE_PTR(BBA_RWP);
+ u8 *end_ptr = PTR_FROM_PAGE_PTR(BBA_RHBP);
+ u8 *read_ptr = PTR_FROM_PAGE_PTR(BBA_RRP);
+
+ Descriptor *descriptor = (Descriptor *)write_ptr;
+ //u8 *descriptor = write_ptr;
+ write_ptr += 4;
+
+ for (u32 i = 0, off = 4; i < mRecvBufferLength; ++i, ++off)
{
- if (mReadP + size > BBA_MEM_SIZE)
+ *write_ptr++ = mRecvBuffer[i];
+
+ if (off == 0xff)
{
- INFO_LOG(SP1, "Read error: mReadP + size = %04x + %i", mReadP, size);
- return;
+ off = 0;
+ inc_rwp();
+ }
+
+ if (write_ptr == end_ptr)
+ // TODO check if BP is used as well
+ write_ptr = PTR_FROM_PAGE_PTR(BBA_BP);
+
+ if (write_ptr == read_ptr)
+ {
+ ERROR_LOG(SP1, "recv buffer full error - not implemented");
}
- memcpy(Memory::GetPointer(addr), mBbaMem + mReadP, size);
- INFO_LOG(SP1, "DMA Read from BBA address %0*x, %i bytes to %08x",
- mReadP >= CB_OFFSET ? 4 : 2, mReadP, size, addr);
- mReadP = mReadP + (u16)size;
- return;
}
-
- ERROR_LOG(SP1, "Unhandled BBA DMA read: %i, %08x", size, addr);
-}
-void CEXIETHERNET::DoState(PointerWrap &p)
-{
- p.Do(m_uPosition);
- p.Do(m_uCommand);
- p.Do(m_bInterruptSet);
- p.Do(mWriteP);
- p.Do(mReadP);
- p.Do(mExpectSpecialImmRead);
- p.Do(mSpecialImmData);
- p.Do(Activated);
- p.Do(mRBRPP);
- p.Do(mRBEmpty);
- p.Do(mBbaMem);
- p.Do(mExpectVariableLengthImmWrite);
- p.Do(mReadyToSend);
- p.Do(RegisterBlock);
- // TODO?
- //mWriteBuffer.DoState(p);
- //mCbw.DoState(p);
+ // Align up to next page
+ if ((mRecvBufferLength + 4) % 256)
+ inc_rwp();
+
+ ERROR_LOG(SP1, "%x %x %x %x",
+ PAGE_PTR(BBA_BP),
+ PAGE_PTR(BBA_RRP),
+ PAGE_PTR(BBA_RWP),
+ PAGE_PTR(BBA_RHBP));
+
+ // Update descriptor
+ descriptor->set(*(u16 *)&mBbaMem[BBA_RWP], 4 + mRecvBufferLength, 0);
+
+ mBbaMem[BBA_LRPS] = descriptor->get_status();
+
+ // Raise interrupt
+ if (mBbaMem[BBA_IMR] & INT_R)
+ {
+ mBbaMem[BBA_IR] |= INT_R;
+
+ exi_status.interrupt |= exi_status.TRANSFER;
+ }
+ else
+ {
+ ERROR_LOG(SP1, "NOT raising recv interrupt");
+ }
+
+wait_for_next:
+ if (mBbaMem[BBA_NCRA] & NCRA_SR)
+ RecvStart();
+
+#undef PTR_FROM_PAGE_PTR
+ return true;
}
//#pragma optimize("",on)
diff --git a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h
index 3fa3a42d34..5489131b08 100644
--- a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h
+++ b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h
@@ -20,107 +20,16 @@
#include "Thread.h"
-inline u8 makemaskb(int start, int end) {
- return (u8)_rotl((2 << (end - start)) - 1, 7 - end);
-}
-inline u32 makemaskh(int start, int end) {
- return (u32)_rotl((2 << (end - start)) - 1, 15 - end);
-}
-inline u32 makemaskw(int start, int end) {
- return _rotl((2 << (end - start)) - 1, 31 - end);
-}
-inline u8 getbitsb(u8 byte, int start, int end) {
- return (byte & makemaskb(start, end)) >> u8(7 - end);
-}
-inline u32 getbitsh(u32 hword, int start, int end) {
- return (hword & makemaskh(start, end)) >> u32(15 - end);
-}
-inline u32 getbitsw(u32 dword, int start, int end) {
- return (dword & makemaskw(start, end)) >> (31 - end);
-}
-
-class WriteBuffer
-{
-public:
- WriteBuffer(u32 s) : _size(0)
- {
- _buffer = (u8*)malloc(s*sizeof(u8));
- ucapacity = s;
- }
- ~WriteBuffer() { free(_buffer); }
- u32 size() const { return _size; }
- u32 capacity() const { return ucapacity; }
- void write(u32 s, const void *src)
- {
- if (_size + s >= ucapacity)
- {
- INFO_LOG(SP1, "Write too large!");
- exit(0);
- }
-
- memcpy(_buffer + _size, src, s);
- _size = _size + s;
- }
- void clear() { _size = 0; }
- u8* p() { return _buffer; }
-
-private:
- u8* _buffer;
- u32 ucapacity;
- u32 _size;
-};
-
-// Doesn't contain error checks for wraparound writes
-class CyclicBufferWriter
-{
-public:
- CyclicBufferWriter(u8 *buffer, size_t cap) {
- _buffer = buffer; _capacity = cap; _write = 0;
- }
-
- size_t p_write() const { return _write; }
- void reset() { _write = 0; }
-
- void write(void *src, size_t size) {
- _dbg_assert_(SP1, size < _capacity);
- u8* bsrc = (u8*) src;
- if (_write + size >= _capacity)
- {
- // wraparound
- memcpy(_buffer + _write, src, _capacity - _write);
- memcpy(_buffer, bsrc + (_capacity - _write), size - (_capacity - _write));
- _write = size - (_capacity - _write);
- } else {
- memcpy(_buffer + _write, src, size);
- _write += size;
- }
- //DEBUG_LOG(SP1, "CBWrote %i bytes", size);
- }
- // Aligns the write pointer to steps of 0x100, like the real BBA
- void align() {
- _write = (_write + 0xff) & ~0xff;
- if(_write >= _capacity)
- _write -= _capacity;
- }
-
-private:
- size_t _write;
- size_t _capacity;
- u8 *_buffer;
-};
-
-#define INVALID_P 0xFFFF
-
-// Network Control Register A, RW
+// Network Control Register A
enum NCRA
{
NCRA_RESET = 0x01, // RESET
NCRA_ST0 = 0x02, // Start transmit command/status
NCRA_ST1 = 0x04, // "
- NCRA_SR = 0x08, // Start Receive
+ NCRA_SR = 0x08 // Start Receive
};
-// Network Control Register B, RW
+// Network Control Register B
enum NCRB
{
NCRB_PR = 0x01, // Promiscuous Mode
@@ -129,11 +38,11 @@ enum NCRB
NCRB_PB = 0x08, // Pass Bad Frame
NCRB_AB = 0x10, // Accept Broadcast
NCRB_HBD = 0x20, // reserved
- NCRB_RXINTC = 0xC0, // Receive Interrupt Counter (mask)
+ NCRB_RXINTC = 0xC0 // Receive Interrupt Counter (mask)
};
-// Interrupt Mask Register, RW, 00h
-// Interrupt Register, RW, 00h
+// Interrupt Mask Register
+// Interrupt Register
enum Interrupts
{
INT_FRAG = 0x01, // Fragment Counter
@@ -143,17 +52,20 @@ enum Interrupts
INT_T_ERR = 0x10, // Transmit Error
INT_FIFO_ERR = 0x20, // FIFO Error
INT_BUS_ERR = 0x40, // BUS Error
- INT_RBF = 0x80, // RX Buffer Full
+ INT_RBF = 0x80 // RX Buffer Full
};
-// NWAY Configuration Register, RW, 84h
+// NWAY Configuration Register
enum NWAYC
{
NWAYC_FD = 0x01, // Full Duplex Mode
- NWAYC_PS100 = 0x02, // Port Select 100/10
- NWAYC_ANE = 0x03, // Autonegotiation Enable
- NWAYC_ANS_RA = 0x04, // Restart Autonegotiation
- NWAYC_LTE = 0x08, // Link Test Enable
+ NWAYC_PS100_10 = 0x02, // Port Select 100/10
+ NWAYC_ANE = 0x04, // Autonegotiate enable
+
+ // Autonegotiation status bits...
+
+ NWAYC_NTTEST = 0x40, // Reserved
+ NWAYC_LTE = 0x80 // Link Test Enable
};
enum NWAYS
@@ -168,6 +80,18 @@ enum NWAYS
NWAYS_10TXH = 0x80
};
+enum MISC1
+{
+ MISC1_BURSTDMA = 0x01,
+ MISC1_DISLDMA = 0x02,
+ MISC1_TPF = 0x04,
+ MISC1_TPH = 0x08,
+ MISC1_TXF = 0x10,
+ MISC1_TXH = 0x20,
+ MISC1_TXFIFORST = 0x40,
+ MISC1_RXFIFORST = 0x80
+};
+
enum
{
BBA_NCRA = 0x00,
@@ -182,6 +106,7 @@ enum
BBA_BP = 0x0a,
BBA_TLBP = 0x0c,
BBA_TWP = 0x0e,
+ BBA_IOB = 0x10,
BBA_TRP = 0x12,
BBA_RWP = 0x16,
BBA_RRP = 0x18,
@@ -195,6 +120,14 @@ 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,
+ BBA_NAFR_MAR3 = 0x29,
+ BBA_NAFR_MAR4 = 0x2a,
+ BBA_NAFR_MAR5 = 0x2b,
+ BBA_NAFR_MAR6 = 0x2c,
+ BBA_NAFR_MAR7 = 0x2d,
BBA_NWAYC = 0x30,
BBA_NWAYS = 0x31,
@@ -210,37 +143,48 @@ enum
BBA_SI_ACTRL = 0x5c,
BBA_SI_STATUS = 0x5d,
- BBA_SI_ACTRL2 = 0x60,
+ BBA_SI_ACTRL2 = 0x60
};
enum
{
BBA_RECV_SIZE = 0x800,
- BBA_MEM_SIZE = 0x1000,
+ BBA_MEM_SIZE = 0x1000
+};
- CB_OFFSET = 0x100,
- CB_SIZE = (BBA_MEM_SIZE - CB_OFFSET),
- SIZEOF_ETH_HEADER = 0xe,
- SIZEOF_RECV_DESCRIPTOR = 4,
+enum { EXI_DEVTYPE_ETHER = 0x04020200 };
- EXI_DEVTYPE_ETHER = 0x04020200,
+enum SendStatus
+{
+ DESC_CC0 = 0x01,
+ DESC_CC1 = 0x02,
+ DESC_CC2 = 0x04,
+ DESC_CC3 = 0x08,
+ DESC_CRSLOST= 0x10,
+ DESC_UF = 0x20,
+ DESC_OWC = 0x40,
+ DESC_OWN = 0x80
};
-enum
+enum RecvStatus
{
- EXPECT_NONE = 0,
- EXPECT_ID
+ DESC_BF = 0x01,
+ DESC_CRC = 0x02,
+ DESC_FAE = 0x04,
+ DESC_FO = 0x08,
+ DESC_RW = 0x10,
+ DESC_MF = 0x20,
+ DESC_RF = 0x40,
+ DESC_RERR = 0x80
};
class CEXIETHERNET : public IEXIDevice
{
public:
CEXIETHERNET(const std::string& mac_addr);
- ~CEXIETHERNET();
- void SetMAC(u8 *new_addr);
- void SetCS(int _iCS);
+ virtual ~CEXIETHERNET();
+ void SetCS(int cs);
bool IsPresent();
- void Update();
bool IsInterruptSet();
void ImmWrite(u32 data, u32 size);
u32 ImmRead(u32 size);
@@ -249,52 +193,116 @@ public:
void DoState(PointerWrap &p);
//private:
- // STATE_TO_SAVE
- u32 m_uPosition;
- u32 m_uCommand;
+ struct
+ {
+ enum
+ {
+ READ,
+ WRITE
+ } direction;
- bool m_bInterruptSet;
- u16 mWriteP, mReadP;
+ enum
+ {
+ EXI,
+ MX
+ } region;
- bool mExpectSpecialImmRead; //reset to false on deselect
- u32 mSpecialImmData;
- bool Activated;
+ u16 address;
+ bool valid;
+ } transfer;
- u16 mRBRPP; //RRP - Receive Buffer Read Page Pointer
- bool mRBEmpty;
+ enum
+ {
+ EXI_ID,
+ REVISION_ID,
+ INTERRUPT_MASK,
+ INTERRUPT,
+ DEVICE_ID,
+ ACSTART,
+ HASH_READ = 8,
+ HASH_WRITE,
+ HASH_STATUS = 0xb,
+ RESET = 0xf
+ };
- u8 mBbaMem[BBA_MEM_SIZE];
+ // exi regs
+ struct EXIStatus
+ {
+ enum
+ {
+ TRANSFER = 0x80
+ };
+
+ u8 revision_id;
+ u8 interrupt_mask;
+ u8 interrupt;
+ u16 device_id;
+ u8 acstart;
+ u32 hash_challenge;
+ u32 hash_response;
+ u8 hash_status;
+
+ EXIStatus()
+ {
+ device_id = 0xd107;
+ revision_id = 0;//0xf0;
+ acstart = 0x4e;
+
+ interrupt_mask = 0;
+ interrupt = 0;
+ hash_challenge = 0;
+ hash_response = 0;
+ hash_status = 0;
+ }
- WriteBuffer mWriteBuffer;
- CyclicBufferWriter mCbw;
+ } exi_status;
- bool mExpectVariableLengthImmWrite;
- bool mReadyToSend;
- u8 RegisterBlock[0x1000];
- enum
+ struct Descriptor
{
- CMD_ID = 0x00,
- CMD_READ_REG = 0x01,
- };
+ u32 word;
- void recordSendComplete();
- bool sendPacket(u8 *etherpckt, int size);
- bool checkRecvBuffer();
- bool handleRecvdPacket();
+ void set(u32 const next_page, u32 const packet_length, u32 const status)
+ {
+ word = 0;
+ word |= (status & 0xff) << 24;
+ word |= (packet_length & 0xfff) << 12;
+ word |= next_page & 0xfff;
+ }
- //TAP interface
- bool activate();
- bool CheckRecieved();
- bool deactivate();
- bool isActivated();
- bool resume();
- bool startRecv();
- bool cbwriteDescriptor(u32 size);
+ u8 get_status() const
+ {
+ return (word >> 24) & 0xff;
+ }
+ };
+ bool IsMXCommand(u32 const data);
+ bool IsWriteCommand(u32 const data);
+ char const * const GetRegisterName() const;
+ void MXHardReset();
+ void MXCommandHandler(u32 data, u32 size);
+ void DirectFIFOWrite(u8 *data, u32 size);
+ void SendFromDirectFIFO();
+ void SendFromPacketBuffer();
+ void SendComplete();
+ u8 HashIndex(u8 *dest_eth_addr);
+ bool RecvMACFilter();
+ void inc_rwp();
+ bool RecvHandlePacket();
+
+ u8 *tx_fifo;
+ u8 *mBbaMem;
+
+ // TAP interface
+ bool Activate();
+ void Deactivate();
+ bool IsActivated();
+ bool SendFrame(u8 *frame, u32 size);
+ bool RecvInit();
+ bool RecvStart();
+ void RecvStop();
+
+ u8 *mRecvBuffer;
- volatile bool mWaiting;
- u8 mac_address[6];
- u8 mRecvBuffer[BBA_RECV_SIZE];
#ifdef _WIN32
HANDLE mHAdapter, mHRecvEvent, mHReadWait;
DWORD mMtu;