summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorsktsqrl <sktsqrl@gmail.com>2012-07-21 12:53:46 -0700
committersktsqrl <sktsqrl@gmail.com>2012-07-21 12:53:46 -0700
commitc0060f9ef87122cf5e76e43aaefb17a22480f935 (patch)
tree8c2eb1165ca0b357dbc72c4d76dffa516c571633 /Source/Core
parent0e1348c839cd55a2321dda3a47f13dd5ee7075ef (diff)
parent601b9fc03b2a23c711ad6ade57acb8d58f0383f1 (diff)
Merge branch 'bba'
OS X support not impl
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Core/Src/HW/BBA-TAP/TAP_Apple.cpp37
-rw-r--r--Source/Core/Core/Src/HW/BBA-TAP/TAP_Unix.cpp352
-rw-r--r--Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp317
-rw-r--r--Source/Core/Core/Src/HW/EXI_Device.cpp2
-rw-r--r--Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp862
-rw-r--r--Source/Core/Core/Src/HW/EXI_DeviceEthernet.h351
6 files changed, 934 insertions, 987 deletions
diff --git a/Source/Core/Core/Src/HW/BBA-TAP/TAP_Apple.cpp b/Source/Core/Core/Src/HW/BBA-TAP/TAP_Apple.cpp
index 11530512c8..5309d9fc6a 100644
--- a/Source/Core/Core/Src/HW/BBA-TAP/TAP_Apple.cpp
+++ b/Source/Core/Core/Src/HW/BBA-TAP/TAP_Apple.cpp
@@ -15,33 +15,38 @@
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
-#include "../Memmap.h"
#include "../EXI_Device.h"
#include "../EXI_DeviceEthernet.h"
-bool CEXIETHERNET::deactivate() {
- return true;
-}
-bool CEXIETHERNET::isActivated() {
- return false;
-}
-bool CEXIETHERNET::activate() {
+
+bool CEXIETHERNET::Activate()
+{
return false;
}
-bool CEXIETHERNET::CheckRecieved() {
- return false;
+
+void CEXIETHERNET::Deactivate()
+{
}
-bool CEXIETHERNET::resume() {
+
+bool CEXIETHERNET::IsActivated()
+{
return false;
}
-bool CEXIETHERNET::startRecv() {
+
+bool CEXIETHERNET::SendFrame(u8 *, u32)
+{
return false;
}
-bool CEXIETHERNET::sendPacket(u8 *etherpckt, int size) {
+
+bool CEXIETHERNET::RecvInit()
+{
return false;
}
-bool CEXIETHERNET::handleRecvdPacket() {
+
+bool CEXIETHERNET::RecvStart()
+{
return false;
}
-bool CEXIETHERNET::cbwriteDescriptor(u32 size) {
- return false;
+
+void CEXIETHERNET::RecvStop()
+{
}
diff --git a/Source/Core/Core/Src/HW/BBA-TAP/TAP_Unix.cpp b/Source/Core/Core/Src/HW/BBA-TAP/TAP_Unix.cpp
index 5be59eb658..97c73eeb40 100644
--- a/Source/Core/Core/Src/HW/BBA-TAP/TAP_Unix.cpp
+++ b/Source/Core/Core/Src/HW/BBA-TAP/TAP_Unix.cpp
@@ -15,305 +15,171 @@
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
-#include "../Memmap.h"
+#include "StringUtil.h"
#include "../EXI_Device.h"
#include "../EXI_DeviceEthernet.h"
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <net/if.h>
+
#ifdef __linux__
+#include <fcntl.h>
#include <linux/if_tun.h>
-#else
-#include <net/if_tun.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
+#include <sys/socket.h>
#endif
-#include <assert.h>
-
-int fd = -1;
-std::thread cpuThread;
-
-bool CEXIETHERNET::deactivate()
-{
- close(fd);
- fd = -1;
- cpuThread.join();
- return true;
-}
-bool CEXIETHERNET::isActivated()
-{
- return fd != -1 ? true : false;
-}
+#define NOTIMPLEMENTED(Name) \
+ NOTICE_LOG(SP1, "CEXIETHERNET::%s not implemented for your UNIX", Name);
-bool CEXIETHERNET::activate()
+bool CEXIETHERNET::Activate()
{
#ifdef __linux__
- if(isActivated())
+ if (IsActivated())
return true;
- if( (fd = open("/dev/net/tun", O_RDWR)) < 0)
+
+ // 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)
{
- INFO_LOG(SP1, "Couldn't Open device\n");
+ ERROR_LOG(SP1, "Couldn't open /dev/net/tun, unable to init BBA");
return false;
}
- struct ifreq ifr;
- int err;
+ struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE;
-
+
strncpy(ifr.ifr_name, "Dolphin", IFNAMSIZ);
-
- if( (err = ioctl(fd, TUNSETIFF, (void*) &ifr)) < 0)
+
+ int err;
+ if ((err = ioctl(fd, TUNSETIFF, (void*)&ifr)) < 0)
{
close(fd);
fd = -1;
- INFO_LOG(SP1, " Error with IOCTL: 0x%X\n", err);
+ ERROR_LOG(SP1, "TUNSETIFF failed: err=%d", err);
return false;
}
- ioctl( fd, TUNSETNOCSUM, 1 );
- /*int flags;
- if ((flags = fcntl( fd, F_GETFL)) < 0)
- {
- INFO_LOG(SP1, "getflags on tun device: %s", strerror (errno));
- }
- flags |= O_NONBLOCK;
- if (fcntl( fd, F_SETFL, flags ) < 0)
- {
- INFO_LOG(SP1, "set tun device flags: %s", strerror (errno));
- }*/
+ ioctl(fd, TUNSETNOCSUM, 1);
+
+ readEnabled = false;
- INFO_LOG(SP1, "Returned Socket name is: %s\n", ifr.ifr_name);
- system("brctl addif pan0 Dolphin");
- system("ifconfig Dolphin 0.0.0.0 promisc up");
- resume();
+ INFO_LOG(SP1, "BBA initialized with associated tap %s", ifr.ifr_name);
return true;
#else
+ NOTIMPLEMENTED("Activate");
return false;
#endif
}
-bool CEXIETHERNET::CheckRecieved()
+void CEXIETHERNET::Deactivate()
{
- if(!isActivated())
- return false;
- int maxfd;
- int retval;
- struct timeval tv;
- int timeout = 9999; // 3 seconds will kill him
- fd_set mask;
-
- /* Find the largest file descriptor */
- maxfd = fd;
-
- /* Check the file descriptors for available data */
- errno = 0;
-
- /* Set up the mask of file descriptors */
- FD_ZERO(&mask);
-
- FD_SET(fd, &mask);
-
- /* Set up the timeout */
- tv.tv_sec = timeout/1000;
- tv.tv_usec = (timeout%1000)*1000;
+#ifdef __linux__
+ close(fd);
+ fd = -1;
- /* Look! */
- retval = select(maxfd+1, &mask, NULL, NULL, &tv);
+ readEnabled = false;
+ if (readThread.joinable())
+ readThread.join();
+#else
+ NOTIMPLEMENTED("Deactivate");
+#endif
+}
- /* Mark all file descriptors ready that have data available */
- if ( retval > 0 ) {
- if ( FD_ISSET(fd, &mask) )
- {
- INFO_LOG(SP1, "\t\t\t\tWe have data!\n");
- return true;
- }
- }
+bool CEXIETHERNET::IsActivated()
+{
+#ifdef __linux__
+ return fd != -1 ? true : false;
+#else
return false;
+#endif
}
-bool CEXIETHERNET::resume()
+bool CEXIETHERNET::SendFrame(u8* frame, u32 size)
{
- if(!isActivated())
- return true;
- INFO_LOG(SP1, "BBA resume\n");
- if(mBbaMem[BBA_NCRA] & NCRA_SR) {
- startRecv();
- }
- INFO_LOG(SP1, "BBA resume complete\n");
- return true;
-}
+#ifdef __linux__
+ INFO_LOG(SP1, "SendFrame %x\n%s", size, ArrayToString(frame, size, 0x10).c_str());
-void CpuThread(CEXIETHERNET *self)
-{
- while(1)
+ int writtenBytes = write(fd, frame, size);
+ if ((u32)writtenBytes != size)
{
- if(self->CheckRecieved())
- {
- u8 B[1514];
- self->mRecvBufferLength = read(fd, B, 1500);
- //INFO_LOG(SP1, "read return of 0x%x\n", self->mRecvBufferLength);
- if (self->mRecvBufferLength == 0xffffffff)
- {
- //Fail Boat
- continue;
- }
- else if(self->mRecvBufferLength > 0)
- {
- //mRecvBuffer.write(B, BytesRead);
- //strncat(mRecvBuffer.p(), B, BytesRead);
- memcpy(self->mRecvBuffer, B, self->mRecvBufferLength);
- }
- else if(self->mRecvBufferLength == -1U)
- {
- continue;
- }
- else
- {
- INFO_LOG(SP1, "Unknown read return of 0x%x\n", self->mRecvBufferLength);
- exit(0);
- }
- INFO_LOG(SP1, "Received %d bytes of data\n", self->mRecvBufferLength);
- self->mWaiting = false;
- self->handleRecvdPacket();
- return;
- }
- //sleep(1);
- }
-}
-
-bool CEXIETHERNET::startRecv()
-{
- INFO_LOG(SP1, "Start Receive!\n");
- //exit(0);
- INFO_LOG(SP1, "startRecv... ");
- if(mWaiting) {
- INFO_LOG(SP1, "already waiting\n");
- return true;
- }
- cpuThread = std::thread(CpuThread, this);
- mWaiting = true;
-
- return true;
-}
-
-bool CEXIETHERNET::sendPacket(u8 *etherpckt, int size)
-{
- if(!isActivated())
+ ERROR_LOG(SP1, "SendFrame(): expected to write %d bytes, instead wrote %d",
+ size, writtenBytes);
return false;
- INFO_LOG(SP1, "Packet: 0x");
- for(int a = 0; a < size; ++a)
- {
- INFO_LOG(SP1, "%02X ", etherpckt[a]);
}
- INFO_LOG(SP1, " : Size: %d\n", size);
- int numBytesWrit = write(fd, etherpckt, size);
- if(numBytesWrit != size)
+ else
{
- INFO_LOG(SP1, "BBA sendPacket %i only got %i bytes sent!errno: %d\n", size, numBytesWrit, errno);
- return false;
+ SendComplete();
+ return true;
}
- recordSendComplete();
- return true;
+#else
+ NOTIMPLEMENTED("SendFrame");
+ return false;
+#endif
}
-bool CEXIETHERNET::handleRecvdPacket()
+void ReadThreadHandler(CEXIETHERNET* self)
{
- int rbwpp = 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);
-
- //DUMPWORD(rbwpp);
- //DUMPWORD(mRBRPP);
- //DUMPWORD(available_bytes_in_cb);
+ while (true)
+ {
+ if (self->fd < 0)
+ return;
- assert(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 = mCbw.p_write() + CB_OFFSET;
- //DUMPWORD(rbwpp);
+ fd_set rfds;
+ FD_ZERO(&rfds);
+ FD_SET(self->fd, &rfds);
- //mPacketsRcvd++;
- mRecvBufferLength = 0;
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 50000;
+ if (select(self->fd + 1, &rfds, NULL, NULL, &timeout) <= 0)
+ continue;
- if(mBbaMem[BBA_IMR] & INT_R)
- {
- if(!(mBbaMem[BBA_IR] & INT_R))
+ int readBytes = read(self->fd, self->mRecvBuffer, BBA_RECV_SIZE);
+ if (readBytes < 0)
{
- mBbaMem[BBA_IR] |= INT_R;
- INFO_LOG(SP1, "BBA Recv interrupt raised\n");
- m_bInterruptSet = true;
+ ERROR_LOG(SP1, "Failed to read from BBA, err=%d", readBytes);
+ }
+ else if (self->readEnabled)
+ {
+ INFO_LOG(SP1, "Read data: %s", ArrayToString(self->mRecvBuffer, readBytes, 0x10).c_str());
+ self->mRecvBufferLength = readBytes;
+ self->RecvHandlePacket();
}
}
+}
- if(mBbaMem[BBA_NCRA] & NCRA_SR)
- {
- startRecv();
- }
-
+bool CEXIETHERNET::RecvInit()
+{
+#ifdef __linux__
+ readThread = std::thread(ReadThreadHandler, this);
return true;
+#else
+ NOTIMPLEMENTED("RecvInit");
+ return false;
+#endif
}
-union bba_descr {
- struct { u32 next_packet_ptr:12, packet_len:12, status:8; };
- u32 word;
-};
-
-bool CEXIETHERNET::cbwriteDescriptor(u32 size)
+bool CEXIETHERNET::RecvStart()
{
- if(size < SIZEOF_ETH_HEADER)
- {
- INFO_LOG(SP1, "Packet too small: %i bytes\n", size);
- return false;
- }
-
- size += SIZEOF_RECV_DESCRIPTOR; //The descriptor supposed to include the size of itself
-
- //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\n");
- return false;
- }
- if(size >= CB_SIZE)
- {
- INFO_LOG(SP1, "Packet too big: %i bytes\n", 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 = mCbw.p_write() + size + CB_OFFSET;
- }
- else
- {
- npp = 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%04lX: next 0x%03X len 0x%03X status 0x%02X\n",
- descr.word, (unsigned long)mCbw.p_write() + CB_OFFSET, descr.next_packet_ptr,
- descr.packet_len, descr.status);
- mCbw.write(&descr.word, SIZEOF_RECV_DESCRIPTOR);
+#ifdef __linux__
+ if (!readThread.joinable())
+ RecvInit();
+ readEnabled = true;
return true;
+#else
+ NOTIMPLEMENTED("RecvStart");
+ return false;
+#endif
+}
+
+void CEXIETHERNET::RecvStop()
+{
+#ifdef __linux__
+ readEnabled = false;
+#else
+ NOTIMPLEMENTED("RecvStop");
+#endif
}
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..87ebd90344 100644
--- a/Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp
+++ b/Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp
@@ -17,7 +17,6 @@
#include "StringUtil.h"
#include "../Memmap.h"
-//#pragma optimize("",off)
// GROSS CODE ALERT: headers need to be included in the following order
#include "TAP_Win32.h"
#include "../EXI_Device.h"
@@ -187,66 +186,45 @@ bool OpenTAP(HANDLE& adapter, const std::string device_guid)
} // namespace Win32TAPHelper
-bool CEXIETHERNET::deactivate()
+bool CEXIETHERNET::Activate()
{
- INFO_LOG(SP1, "Deactivating BBA...");
- if (!isActivated())
+ 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())
- return true;
-
- INFO_LOG(SP1, "Activating BBA...");
DWORD len;
std::vector<std::string> device_guids;
if (!Win32TAPHelper::GetGUIDs(device_guids))
{
- INFO_LOG(SP1, "Failed to find a TAP GUID");
+ ERROR_LOG(SP1, "Failed to find a TAP GUID");
return false;
}
- for (int i = 0; i < device_guids.size(); i++)
+ for (size_t i = 0; i < device_guids.size(); i++)
{
if (Win32TAPHelper::OpenTAP(mHAdapter, device_guids.at(i)))
{
- ERROR_LOG(SP1, "OPENED %s", device_guids.at(i).c_str());
+ INFO_LOG(SP1, "OPENED %s", device_guids.at(i).c_str());
i = device_guids.size();
}
}
if (mHAdapter == INVALID_HANDLE_VALUE)
{
- INFO_LOG(SP1, "Failed to open any TAP");
+ ERROR_LOG(SP1, "Failed to open any TAP");
return false;
}
/* 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,279 +232,124 @@ 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"
+ ERROR_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();
-
- INFO_LOG(SP1, "Success!");
return true;
}
-bool CEXIETHERNET::CheckRecieved()
+void CEXIETHERNET::Deactivate()
{
- if (!isActivated())
- return false;
-
- return false;
-}
+ if (!IsActivated())
+ return;
-// Required for lwip...not sure why
-void fixup_ip_checksum(u16 *dataptr, u16 len)
-{
- u32 acc = 0;
- u16 *data = dataptr;
- u16 *chksum = &dataptr[5];
+ RecvStop();
- *chksum = 0;
+ CloseHandle(mHAdapter);
+ mHAdapter = INVALID_HANDLE_VALUE;
+}
- while (len > 1) {
- u16 s = *data++;
- acc += Common::swap16(s);
- len -= 2;
- }
- acc = (acc >> 16) + (acc & 0xffff);
- acc += (acc >> 16);
- *chksum = Common::swap16(~acc);
+bool CEXIETHERNET::IsActivated()
+{
+ return mHAdapter != INVALID_HANDLE_VALUE;
}
-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());
+ 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);
+ ERROR_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);
- return false;
+ ERROR_LOG(SP1, "BBA SendFrame %i only got %i bytes sent!", size, numBytesWrit);
}
- recordSendComplete();
+
+ // Always report the packet as being sent successfully, even though it might be a lie
+ 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,
+ (LPDWORD)&self->mRecvBufferLength, false);
- return true;
+ self->RecvHandlePacket();
}
-bool CEXIETHERNET::resume()
+bool CEXIETHERNET::RecvInit()
{
- if (!isActivated())
- return true;
+ // Set up recv event
+
+ if ((mHRecvEvent = CreateEvent(NULL, false, false, NULL)) == NULL)
+ {
+ ERROR_LOG(SP1, "Failed to create recv event:%x", GetLastError());
+ return false;
+ }
- INFO_LOG(SP1, "BBA resume");
- //mStop = false;
+ ZeroMemory(&mReadOverlapped, sizeof(mReadOverlapped));
RegisterWaitForSingleObject(&mHReadWait, mHRecvEvent, ReadWaitCallback,
- this, INFINITE, WT_EXECUTEDEFAULT);//WT_EXECUTEINWAITTHREAD
+ this, INFINITE, WT_EXECUTEDEFAULT);
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
-
- INFO_LOG(SP1, "startRecv... ");
+ if (!IsActivated())
+ return false;
- 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);
+ (LPDWORD)&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;
- }
+ // Completed immediately
+ RecvHandlePacket();
}
return true;
}
-VOID CALLBACK CEXIETHERNET::ReadWaitCallback(PVOID lpParameter, BOOLEAN TimerFired)
-{
- 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
+void CEXIETHERNET::RecvStop()
{
- 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 (!IsActivated())
+ return;
- 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);
-
- mCbw.write(&descr.word, SIZEOF_RECV_DESCRIPTOR);
-
- return true;
+ UnregisterWaitEx(mHReadWait, INVALID_HANDLE_VALUE);
+
+ CloseHandle(mHRecvEvent);
+ mHRecvEvent = INVALID_HANDLE_VALUE;
}
-//#pragma optimize("",on)
diff --git a/Source/Core/Core/Src/HW/EXI_Device.cpp b/Source/Core/Core/Src/HW/EXI_Device.cpp
index 9c40a8f4d1..9fdd9a0cd0 100644
--- a/Source/Core/Core/Src/HW/EXI_Device.cpp
+++ b/Source/Core/Core/Src/HW/EXI_Device.cpp
@@ -129,7 +129,7 @@ IEXIDevice* EXIDevice_Create(TEXIDevices device_type, const int channel_num)
break;
case EXIDEVICE_ETH:
- result = new CEXIETHERNET(SConfig::GetInstance().m_bba_mac);
+ result = new CEXIETHERNET();
break;
case EXIDEVICE_AM_BASEBOARD:
diff --git a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp
index 6572233b20..4197d01350 100644
--- a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp
+++ b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp
@@ -16,87 +16,130 @@
// http://code.google.com/p/dolphin-emu/
#include "Memmap.h"
-//#pragma optimize("",off)
#include "EXI_Device.h"
#include "EXI_DeviceEthernet.h"
+#include "StringUtil.h"
+#include "../ConfigManager.h"
-#define MAKE(type, arg) (*(type *)&(arg))
+// 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(const std::string& mac_addr) :
- m_uPosition(0),
- m_uCommand(0),
- mWriteBuffer(2048),
- mCbw(mBbaMem + CB_OFFSET, CB_SIZE)
+
+// TODO move this code into Common or something, for IOS to use
+
+enum MACConsumer
{
- const u8 mac_address_default[6] =
- { 0x00, 0x09, 0xbf, 0x01, 0x00, 0xc1 };
+ BBA,
+ IOS
+};
- memset(mBbaMem, 0, BBA_MEM_SIZE);
-
- int x = 0;
- u8 new_addr[6] = { 0 };
- for (int i = 0; i < (int)mac_addr.size() && x < 12; i++)
+void GenerateMAC(MACConsumer type, u8 (&mac)[6])
+{
+ memset(mac, 0, 6);
+
+ u8 const oui_bba[] = { 0x00, 0x09, 0xbf };
+ u8 const oui_ios[] = { 0x00, 0x17, 0xab };
+
+ switch (type)
+ {
+ case BBA:
+ memcpy(mac, oui_bba, 3);
+ break;
+ case IOS:
+ memcpy(mac, oui_ios, 3);
+ break;
+ }
+
+ srand((unsigned int)time(nullptr));
+
+ u8 id[3] =
{
- char c = mac_addr.at(i);
- if (c >= '0' && c <= '9') {
- new_addr[x / 2] |= (c - '0') << ((x & 1) ? 0 : 4); x++;
- } else if (c >= 'A' && c <= 'F') {
- new_addr[x / 2] |= (c - 'A' + 10) << ((x & 1) ? 0 : 4); x++;
- } else if (c >= 'a' && c <= 'f') {
- new_addr[x / 2] |= (c - 'a' + 10) << ((x & 1) ? 0 : 4); x++;
+ (u8)rand(),
+ (u8)rand(),
+ (u8)rand()
+ };
+
+ memcpy(&mac[3], id, 3);
+}
+
+CEXIETHERNET::CEXIETHERNET()
+{
+ tx_fifo = new u8[1518];
+ mBbaMem = new u8[BBA_MEM_SIZE];
+
+ mRecvBuffer = new u8 [BBA_RECV_SIZE];
+ mRecvBufferLength = 0;
+
+ MXHardReset();
+
+ // Parse MAC address from config, and generate a new one if it doesn't
+ // exist or can't be parsed.
+
+ auto &mac_addr_setting = SConfig::GetInstance().m_bba_mac;
+ bool mac_addr_valid = false;
+ u8 mac_addr[6] = { 0 };
+
+ if (!mac_addr_setting.empty())
+ {
+ int x = 0;
+
+ for (size_t i = 0; i < mac_addr_setting.size() && x < 12; i++)
+ {
+ char c = mac_addr_setting.at(i);
+ if (c >= '0' && c <= '9') {
+ mac_addr[x / 2] |= (c - '0') << ((x & 1) ? 0 : 4); x++;
+ } else if (c >= 'A' && c <= 'F') {
+ mac_addr[x / 2] |= (c - 'A' + 10) << ((x & 1) ? 0 : 4); x++;
+ } else if (c >= 'a' && c <= 'f') {
+ mac_addr[x / 2] |= (c - 'a' + 10) << ((x & 1) ? 0 : 4); x++;
+ }
+ }
+
+ if (x / 2 == 6)
+ {
+ memcpy(&mBbaMem[BBA_NAFR_PAR0], mac_addr, 6);
+ mac_addr_valid = true;
}
}
- if (x / 2 == 6)
- memcpy(mac_address, 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]);
+ if (!mac_addr_valid)
+ {
+ GenerateMAC(BBA, mac_addr);
- mWriteP = INVALID_P;
- mReadP = INVALID_P;
- mWaiting = false;
- mReadyToSend = false;
- Activated = false;
- m_bInterruptSet = false;
+ mac_addr_setting = ArrayToString(mac_addr, 6, 10, false);
+ SConfig::GetInstance().SaveSettings();
-#ifdef _WIN32
+ memcpy(&mBbaMem[BBA_NAFR_PAR0], mac_addr, 6);
+ }
+
+ // hax .. fully established 100BASE-T link
+ mBbaMem[BBA_NWAYS] = NWAYS_LS100 | NWAYS_LPNWAY | NWAYS_100TXF | NWAYS_ANCLPT;
+
+#if defined(_WIN32)
mHAdapter = INVALID_HANDLE_VALUE;
mHRecvEvent = INVALID_HANDLE_VALUE;
mHReadWait = INVALID_HANDLE_VALUE;
+#elif defined(__linux__)
+ fd = -1;
#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)
+ 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 +148,515 @@ 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)
- {
- mBbaMem[BBA_IR] |= INT_T;
- INFO_LOG(SP1, "\t\tBBA Send interrupt raised");
- m_bInterruptSet = true;
- }
- // TODO why did sonic put this here?
- //startRecv();
-}
+ data >>= (4 - size) * 8;
-bool CEXIETHERNET::checkRecvBuffer()
-{
- if (mRecvBufferLength)
- handleRecvdPacket();
- return true;
-}
+ if (!transfer.valid)
+ {
+ 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;
-void CEXIETHERNET::ImmWrite(u32 data, u32 size)
-{
- if (size == 1)
- data = (u8)Common::swap32(data);
- else if (size == 2)
- data >>= 16;
+ DEBUG_LOG(SP1, "%s %s %s %x",
+ IsMXCommand(data) ? "mx " : "exi",
+ IsWriteCommand(data) ? "write" : "read ",
+ GetRegisterName(),
+ transfer.address);
- INFO_LOG(SP1, "imm write %0*x writep 0x%x", size * 2, data, mWriteP);
+ if (transfer.address == BBA_IOB && transfer.region == transfer.MX)
+ {
+ ERROR_LOG(SP1, "Usage of BBA_IOB indicates that the rx packet descriptor has been corrupted, killing dolphin...");
+ exit(0);
+ }
- 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.
+
+ DEBUG_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;
- }
- }
- break;
- case 0x03:
- mBbaMem[0x03] = ~data;
- if (data & 0x80)
- m_bInterruptSet = false;
+ case EXI_ID:
+ ret = EXI_DEVTYPE_ETHER;
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 REVISION_ID:
+ ret = exi_status.revision_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());
- }
+ case DEVICE_ID:
+ ret = exi_status.device_id;
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);
+ }
+
+ DEBUG_LOG(SP1, "imm r%i: %0*x", size, size * 2, ret);
+
+ ret <<= (4 - size) * 8;
+
+ return ret;
+}
+
+void CEXIETHERNET::DMAWrite(u32 addr, u32 size)
+{
+ DEBUG_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");
+ }
+}
+
+void CEXIETHERNET::DMARead(u32 addr, u32 size)
+{
+ DEBUG_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...
+ ERROR_LOG(SP1, "CEXIETHERNET::DoState not implemented!");
+}
+
+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));
+}
- if (mWriteP == BBA_WRTXFIFOD)
+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)
+ {
+ DEBUG_LOG(SP1, "software reset");
+ //MXSoftReset();
+ Activate();
+ }
+
+ if ((mBbaMem[BBA_NCRA] & NCRA_SR) ^ (data & NCRA_SR))
+ {
+ DEBUG_LOG(SP1, "%s rx", (data & NCRA_SR) ? "start" : "stop");
- INFO_LOG(SP1, "Read from BBA register 0x%x", mReadP);
+ if (data & NCRA_SR)
+ RecvStart();
+ else
+ RecvStop();
+ }
- switch (mReadP)
+ // 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;
+ // Technically transfer dma status is kept in TXDMA - not implemented
+
+ if (data & NCRA_ST0)
+ {
+ WARN_LOG(SP1, "start tx - local dma");
+ SendFromPacketBuffer();
+ }
+ else if (data & NCRA_ST1)
+ {
+ DEBUG_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;
}
return;
}
+}
+
+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;
+ // 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;
}
-u32 CEXIETHERNET::ImmRead(u32 size)
+void CEXIETHERNET::SendFromDirectFIFO()
{
- INFO_LOG(SP1, "imm read %i readp %x", size, mReadP);
+ SendFrame(tx_fifo, *(u16 *)&mBbaMem[BBA_TXFIFOCNT]);
+}
+
+void CEXIETHERNET::SendFromPacketBuffer()
+{
+ ERROR_LOG(SP1, "tx packet buffer not implemented");
+}
- if (mExpectSpecialImmRead)
+void CEXIETHERNET::SendComplete()
+{
+ mBbaMem[BBA_NCRA] &= ~(NCRA_ST0 | NCRA_ST1);
+ *(u16 *)&mBbaMem[BBA_TXFIFOCNT] = 0;
+
+ 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;
+}
+
+inline 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);
+ }
+
+ // return bits used as index
+ return crc >> 26;
+}
+
+inline bool CEXIETHERNET::RecvMACFilter()
+{
+ static u8 const broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
- uResult = Common::swap32(uResult);
+ // Accept all destination addrs?
+ if (mBbaMem[BBA_NCRB] & NCRB_PR)
+ return true;
- 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;
+ // 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)
+inline 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))
+ *rwp = page_ptr(BBA_BP);
+ else
+ (*rwp)++;
}
-void CEXIETHERNET::DMARead(u32 addr, u32 size)
+// This function is on the critical path for recving data.
+// Be very careful about calling into the logger and other slow things
+bool CEXIETHERNET::RecvHandlePacket()
{
- if (mReadP != INVALID_P)
+ u8 *write_ptr;
+ u8 *end_ptr;
+ u8 *read_ptr;
+ Descriptor *descriptor;
+ u32 status = 0;
+ u16 rwp_initial = page_ptr(BBA_RWP);
+
+ if (!RecvMACFilter())
+ goto wait_for_next;
+
+#ifdef BBA_TRACK_PAGE_PTRS
+ WARN_LOG(SP1, "RecvHandlePacket %x\n%s", mRecvBufferLength,
+ ArrayToString(mRecvBuffer, mRecvBufferLength, 0x100).c_str());
+
+ WARN_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);
+
+ descriptor = (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)
+ write_ptr = ptr_from_page_ptr(BBA_BP);
+
+ if (write_ptr == read_ptr)
+ {
+ /*
+ halt copy
+ if (cur_packet_size >= PAGE_SIZE)
+ desc.status |= FO | BF
+ if (RBFIM)
+ raise RBFI
+ if (AUTORCVR)
+ discard bad packet
+ else
+ inc MPC instad of recving packets
+ */
+ status |= DESC_FO | DESC_BF;
+ mBbaMem[BBA_IR] |= mBbaMem[BBA_IMR] & INT_RBF;
+ break;
}
- 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();
+
+#ifdef BBA_TRACK_PAGE_PTRS
+ WARN_LOG(SP1, "%x %x %x %x",
+ page_ptr(BBA_BP),
+ page_ptr(BBA_RRP),
+ page_ptr(BBA_RWP),
+ page_ptr(BBA_RHBP));
+#endif
+
+ // Is the current frame multicast?
+ if (mRecvBuffer[0] & 0x01)
+ status |= DESC_MF;
+
+ if (status & DESC_BF)
+ {
+ if (mBbaMem[BBA_MISC2] & MISC2_AUTORCVR)
+ {
+ *(u16 *)&mBbaMem[BBA_RWP] = rwp_initial;
+ }
+ else
+ {
+ ERROR_LOG(SP1, "RBF while AUTORCVR == 0!");
+ }
+ }
+
+ descriptor->set(*(u16 *)&mBbaMem[BBA_RWP], 4 + mRecvBufferLength, status);
-//#pragma optimize("",on)
+ mBbaMem[BBA_LRPS] = status;
+
+ // Raise interrupt
+ if (mBbaMem[BBA_IMR] & INT_R)
+ {
+ mBbaMem[BBA_IR] |= INT_R;
+
+ exi_status.interrupt |= exi_status.TRANSFER;
+ }
+ else
+ {
+ // This occurs if software is still processing the last raised recv interrupt
+ WARN_LOG(SP1, "NOT raising recv interrupt");
+ }
+
+wait_for_next:
+ if (mBbaMem[BBA_NCRA] & NCRA_SR)
+ RecvStart();
+
+ return true;
+}
diff --git a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h
index 3fa3a42d34..6a3a552f38 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,30 @@ 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 MISC2
+{
+ MISC2_HBRLEN0 = 0x01,
+ MISC2_HBRLEN1 = 0x02,
+ MISC2_RUNTSIZE = 0x04,
+ MISC2_DREQBCTRL = 0x08,
+ MISC2_RINTSEL = 0x10,
+ MISC2_ITPSEL = 0x20,
+ MISC2_A11A8EN = 0x40,
+ MISC2_AUTORCVR = 0x80
+};
+
enum
{
BBA_NCRA = 0x00,
@@ -182,6 +118,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 +132,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 +155,51 @@ 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_NUM_PAGES = 0x10,
+ BBA_PAGE_SIZE = 0x100,
+ BBA_MEM_SIZE = BBA_NUM_PAGES * BBA_PAGE_SIZE
+};
- 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
};
+#define BBA_RECV_SIZE 0x800
+
class CEXIETHERNET : public IEXIDevice
{
public:
- CEXIETHERNET(const std::string& mac_addr);
- ~CEXIETHERNET();
- void SetMAC(u8 *new_addr);
- void SetCS(int _iCS);
+ CEXIETHERNET();
+ virtual ~CEXIETHERNET();
+ void SetCS(int cs);
bool IsPresent();
- void Update();
bool IsInterruptSet();
void ImmWrite(u32 data, u32 size);
u32 ImmRead(u32 size);
@@ -249,61 +208,133 @@ 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;
+
+ inline 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;
+ }
};
- void recordSendComplete();
- bool sendPacket(u8 *etherpckt, int size);
- bool checkRecvBuffer();
- bool handleRecvdPacket();
-
- //TAP interface
- bool activate();
- bool CheckRecieved();
- bool deactivate();
- bool isActivated();
- bool resume();
- bool startRecv();
- bool cbwriteDescriptor(u32 size);
-
-
- volatile bool mWaiting;
- u8 mac_address[6];
- u8 mRecvBuffer[BBA_RECV_SIZE];
-#ifdef _WIN32
+ 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);
+ 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;
+ u32 mRecvBufferLength;
+
+#if defined(_WIN32)
HANDLE mHAdapter, mHRecvEvent, mHReadWait;
DWORD mMtu;
OVERLAPPED mReadOverlapped;
- DWORD mRecvBufferLength;
static VOID CALLBACK ReadWaitCallback(PVOID lpParameter, BOOLEAN TimerFired);
-#else
- u32 mRecvBufferLength;
+#elif defined(__linux__)
+ int fd;
+ std::thread readThread;
+ volatile bool readEnabled;
#endif
+
};
#endif