commit
c0060f9ef8
|
@ -15,33 +15,38 @@
|
||||||
// Official SVN repository and contact information can be found at
|
// Official SVN repository and contact information can be found at
|
||||||
// http://code.google.com/p/dolphin-emu/
|
// http://code.google.com/p/dolphin-emu/
|
||||||
|
|
||||||
#include "../Memmap.h"
|
|
||||||
#include "../EXI_Device.h"
|
#include "../EXI_Device.h"
|
||||||
#include "../EXI_DeviceEthernet.h"
|
#include "../EXI_DeviceEthernet.h"
|
||||||
bool CEXIETHERNET::deactivate() {
|
|
||||||
return true;
|
bool CEXIETHERNET::Activate()
|
||||||
}
|
{
|
||||||
bool CEXIETHERNET::isActivated() {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool CEXIETHERNET::activate() {
|
|
||||||
|
void CEXIETHERNET::Deactivate()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CEXIETHERNET::IsActivated()
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool CEXIETHERNET::CheckRecieved() {
|
|
||||||
|
bool CEXIETHERNET::SendFrame(u8 *, u32)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool CEXIETHERNET::resume() {
|
|
||||||
|
bool CEXIETHERNET::RecvInit()
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool CEXIETHERNET::startRecv() {
|
|
||||||
|
bool CEXIETHERNET::RecvStart()
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool CEXIETHERNET::sendPacket(u8 *etherpckt, int size) {
|
|
||||||
return false;
|
void CEXIETHERNET::RecvStop()
|
||||||
}
|
{
|
||||||
bool CEXIETHERNET::handleRecvdPacket() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool CEXIETHERNET::cbwriteDescriptor(u32 size) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,305 +15,171 @@
|
||||||
// Official SVN repository and contact information can be found at
|
// Official SVN repository and contact information can be found at
|
||||||
// http://code.google.com/p/dolphin-emu/
|
// http://code.google.com/p/dolphin-emu/
|
||||||
|
|
||||||
#include "../Memmap.h"
|
#include "StringUtil.h"
|
||||||
#include "../EXI_Device.h"
|
#include "../EXI_Device.h"
|
||||||
#include "../EXI_DeviceEthernet.h"
|
#include "../EXI_DeviceEthernet.h"
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <netinet/in.h>
|
#ifdef __linux__
|
||||||
#include <stdio.h>
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <net/if.h>
|
|
||||||
#ifdef __linux__
|
|
||||||
#include <linux/if_tun.h>
|
#include <linux/if_tun.h>
|
||||||
#else
|
#include <net/if.h>
|
||||||
#include <net/if_tun.h>
|
#include <netinet/in.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
#endif
|
#endif
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
int fd = -1;
|
|
||||||
std::thread cpuThread;
|
|
||||||
|
|
||||||
bool CEXIETHERNET::deactivate()
|
|
||||||
{
|
|
||||||
close(fd);
|
|
||||||
fd = -1;
|
|
||||||
cpuThread.join();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CEXIETHERNET::isActivated()
|
#define NOTIMPLEMENTED(Name) \
|
||||||
{
|
NOTICE_LOG(SP1, "CEXIETHERNET::%s not implemented for your UNIX", Name);
|
||||||
return fd != -1 ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CEXIETHERNET::activate()
|
bool CEXIETHERNET::Activate()
|
||||||
{
|
{
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
if(isActivated())
|
if (IsActivated())
|
||||||
return true;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
struct ifreq ifr;
|
|
||||||
|
|
||||||
int err;
|
struct ifreq ifr;
|
||||||
memset(&ifr, 0, sizeof(ifr));
|
memset(&ifr, 0, sizeof(ifr));
|
||||||
ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE;
|
ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE;
|
||||||
|
|
||||||
strncpy(ifr.ifr_name, "Dolphin", IFNAMSIZ);
|
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);
|
close(fd);
|
||||||
fd = -1;
|
fd = -1;
|
||||||
INFO_LOG(SP1, " Error with IOCTL: 0x%X\n", err);
|
ERROR_LOG(SP1, "TUNSETIFF failed: err=%d", err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ioctl( fd, TUNSETNOCSUM, 1 );
|
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));
|
|
||||||
}*/
|
|
||||||
|
|
||||||
INFO_LOG(SP1, "Returned Socket name is: %s\n", ifr.ifr_name);
|
readEnabled = false;
|
||||||
system("brctl addif pan0 Dolphin");
|
|
||||||
system("ifconfig Dolphin 0.0.0.0 promisc up");
|
INFO_LOG(SP1, "BBA initialized with associated tap %s", ifr.ifr_name);
|
||||||
resume();
|
|
||||||
return true;
|
return true;
|
||||||
|
#else
|
||||||
|
NOTIMPLEMENTED("Activate");
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXIETHERNET::Deactivate()
|
||||||
|
{
|
||||||
|
#ifdef __linux__
|
||||||
|
close(fd);
|
||||||
|
fd = -1;
|
||||||
|
|
||||||
|
readEnabled = false;
|
||||||
|
if (readThread.joinable())
|
||||||
|
readThread.join();
|
||||||
|
#else
|
||||||
|
NOTIMPLEMENTED("Deactivate");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CEXIETHERNET::IsActivated()
|
||||||
|
{
|
||||||
|
#ifdef __linux__
|
||||||
|
return fd != -1 ? true : false;
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CEXIETHERNET::CheckRecieved()
|
bool CEXIETHERNET::SendFrame(u8* frame, u32 size)
|
||||||
{
|
{
|
||||||
if(!isActivated())
|
#ifdef __linux__
|
||||||
|
INFO_LOG(SP1, "SendFrame %x\n%s", size, ArrayToString(frame, size, 0x10).c_str());
|
||||||
|
|
||||||
|
int writtenBytes = write(fd, frame, size);
|
||||||
|
if ((u32)writtenBytes != size)
|
||||||
|
{
|
||||||
|
ERROR_LOG(SP1, "SendFrame(): expected to write %d bytes, instead wrote %d",
|
||||||
|
size, writtenBytes);
|
||||||
return false;
|
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;
|
|
||||||
|
|
||||||
/* Look! */
|
|
||||||
retval = select(maxfd+1, &mask, NULL, NULL, &tv);
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SendComplete();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
NOTIMPLEMENTED("SendFrame");
|
||||||
return false;
|
return false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CEXIETHERNET::resume()
|
void ReadThreadHandler(CEXIETHERNET* self)
|
||||||
{
|
{
|
||||||
if(!isActivated())
|
while (true)
|
||||||
return true;
|
|
||||||
INFO_LOG(SP1, "BBA resume\n");
|
|
||||||
if(mBbaMem[BBA_NCRA] & NCRA_SR) {
|
|
||||||
startRecv();
|
|
||||||
}
|
|
||||||
INFO_LOG(SP1, "BBA resume complete\n");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CpuThread(CEXIETHERNET *self)
|
|
||||||
{
|
|
||||||
while(1)
|
|
||||||
{
|
{
|
||||||
if(self->CheckRecieved())
|
if (self->fd < 0)
|
||||||
{
|
|
||||||
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;
|
return;
|
||||||
}
|
|
||||||
//sleep(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CEXIETHERNET::startRecv()
|
fd_set rfds;
|
||||||
{
|
FD_ZERO(&rfds);
|
||||||
INFO_LOG(SP1, "Start Receive!\n");
|
FD_SET(self->fd, &rfds);
|
||||||
//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)
|
struct timeval timeout;
|
||||||
{
|
timeout.tv_sec = 0;
|
||||||
if(!isActivated())
|
timeout.tv_usec = 50000;
|
||||||
return false;
|
if (select(self->fd + 1, &rfds, NULL, NULL, &timeout) <= 0)
|
||||||
INFO_LOG(SP1, "Packet: 0x");
|
continue;
|
||||||
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)
|
|
||||||
{
|
|
||||||
INFO_LOG(SP1, "BBA sendPacket %i only got %i bytes sent!errno: %d\n", size, numBytesWrit, errno);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
recordSendComplete();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CEXIETHERNET::handleRecvdPacket()
|
int readBytes = read(self->fd, self->mRecvBuffer, BBA_RECV_SIZE);
|
||||||
{
|
if (readBytes < 0)
|
||||||
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);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
//mPacketsRcvd++;
|
|
||||||
mRecvBufferLength = 0;
|
|
||||||
|
|
||||||
if(mBbaMem[BBA_IMR] & INT_R)
|
|
||||||
{
|
|
||||||
if(!(mBbaMem[BBA_IR] & INT_R))
|
|
||||||
{
|
{
|
||||||
mBbaMem[BBA_IR] |= INT_R;
|
ERROR_LOG(SP1, "Failed to read from BBA, err=%d", readBytes);
|
||||||
INFO_LOG(SP1, "BBA Recv interrupt raised\n");
|
}
|
||||||
m_bInterruptSet = true;
|
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
union bba_descr {
|
bool CEXIETHERNET::RecvInit()
|
||||||
struct { u32 next_packet_ptr:12, packet_len:12, status:8; };
|
|
||||||
u32 word;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool CEXIETHERNET::cbwriteDescriptor(u32 size)
|
|
||||||
{
|
{
|
||||||
if(size < SIZEOF_ETH_HEADER)
|
#ifdef __linux__
|
||||||
{
|
readThread = std::thread(ReadThreadHandler, this);
|
||||||
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);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
#else
|
||||||
|
NOTIMPLEMENTED("RecvInit");
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CEXIETHERNET::RecvStart()
|
||||||
|
{
|
||||||
|
#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
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
#include "StringUtil.h"
|
#include "StringUtil.h"
|
||||||
#include "../Memmap.h"
|
#include "../Memmap.h"
|
||||||
//#pragma optimize("",off)
|
|
||||||
// GROSS CODE ALERT: headers need to be included in the following order
|
// GROSS CODE ALERT: headers need to be included in the following order
|
||||||
#include "TAP_Win32.h"
|
#include "TAP_Win32.h"
|
||||||
#include "../EXI_Device.h"
|
#include "../EXI_Device.h"
|
||||||
|
@ -187,66 +186,45 @@ bool OpenTAP(HANDLE& adapter, const std::string device_guid)
|
||||||
|
|
||||||
} // namespace Win32TAPHelper
|
} // namespace Win32TAPHelper
|
||||||
|
|
||||||
bool CEXIETHERNET::deactivate()
|
bool CEXIETHERNET::Activate()
|
||||||
{
|
{
|
||||||
INFO_LOG(SP1, "Deactivating BBA...");
|
if (IsActivated())
|
||||||
if (!isActivated())
|
|
||||||
return true;
|
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;
|
DWORD len;
|
||||||
std::vector<std::string> device_guids;
|
std::vector<std::string> device_guids;
|
||||||
|
|
||||||
if (!Win32TAPHelper::GetGUIDs(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;
|
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)))
|
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();
|
i = device_guids.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mHAdapter == INVALID_HANDLE_VALUE)
|
if (mHAdapter == INVALID_HANDLE_VALUE)
|
||||||
{
|
{
|
||||||
INFO_LOG(SP1, "Failed to open any TAP");
|
ERROR_LOG(SP1, "Failed to open any TAP");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get driver version info */
|
/* get driver version info */
|
||||||
ULONG info[3];
|
ULONG info[3];
|
||||||
if (DeviceIoControl (mHAdapter, TAP_IOCTL_GET_VERSION,
|
if (DeviceIoControl(mHAdapter, TAP_IOCTL_GET_VERSION,
|
||||||
&info, sizeof (info), &info, sizeof (info), &len, NULL))
|
&info, sizeof(info), &info, sizeof(info), &len, NULL))
|
||||||
{
|
{
|
||||||
INFO_LOG(SP1, "TAP-Win32 Driver Version %d.%d %s",
|
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
|
if (!(info[0] > TAP_WIN32_MIN_MAJOR || (info[0] == TAP_WIN32_MIN_MAJOR && info[1] >= TAP_WIN32_MIN_MINOR)))
|
||||||
|| (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"
|
" that is at least version %d.%d -- If you recently upgraded your Dolphin"
|
||||||
" distribution, a reboot is probably required at this point to get"
|
" distribution, a reboot is probably required at this point to get"
|
||||||
" Windows to see the new driver.",
|
" Windows to see the new driver.",
|
||||||
|
@ -254,279 +232,124 @@ bool CEXIETHERNET::activate()
|
||||||
return false;
|
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' */
|
/* set driver media status to 'connected' */
|
||||||
ULONG status = TRUE;
|
ULONG status = TRUE;
|
||||||
if (!DeviceIoControl(mHAdapter, TAP_IOCTL_SET_MEDIA_STATUS,
|
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.");
|
"TAP_IOCTL_SET_MEDIA_STATUS DeviceIoControl call.");
|
||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CEXIETHERNET::CheckRecieved()
|
void CEXIETHERNET::Deactivate()
|
||||||
{
|
{
|
||||||
if (!isActivated())
|
if (!IsActivated())
|
||||||
return false;
|
return;
|
||||||
|
|
||||||
return false;
|
RecvStop();
|
||||||
|
|
||||||
|
CloseHandle(mHAdapter);
|
||||||
|
mHAdapter = INVALID_HANDLE_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Required for lwip...not sure why
|
bool CEXIETHERNET::IsActivated()
|
||||||
void fixup_ip_checksum(u16 *dataptr, u16 len)
|
{
|
||||||
{
|
return mHAdapter != INVALID_HANDLE_VALUE;
|
||||||
u32 acc = 0;
|
|
||||||
u16 *data = dataptr;
|
|
||||||
u16 *chksum = &dataptr[5];
|
|
||||||
|
|
||||||
*chksum = 0;
|
|
||||||
|
|
||||||
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::sendPacket(u8 *etherpckt, int size)
|
bool CEXIETHERNET::SendFrame(u8 *frame, u32 size)
|
||||||
{
|
{
|
||||||
fixup_ip_checksum((u16*)etherpckt + 7, 20);
|
DEBUG_LOG(SP1, "SendFrame %x\n%s",
|
||||||
INFO_LOG(SP1, "Packet (%zu):\n%s", size, ArrayToString(etherpckt, size, 0, 16).c_str());
|
size, ArrayToString(frame, size, 0x10).c_str());
|
||||||
|
|
||||||
DWORD numBytesWrit;
|
DWORD numBytesWrit;
|
||||||
OVERLAPPED overlap;
|
OVERLAPPED overlap;
|
||||||
ZeroMemory(&overlap, sizeof(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();
|
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)
|
if (numBytesWrit != size)
|
||||||
{
|
{
|
||||||
INFO_LOG(SP1, "BBA sendPacket %i only got %i bytes sent!", size, numBytesWrit);
|
ERROR_LOG(SP1, "BBA SendFrame %i only got %i bytes sent!", size, numBytesWrit);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
recordSendComplete();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CEXIETHERNET::handleRecvdPacket()
|
|
||||||
{
|
|
||||||
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
|
// Always report the packet as being sent successfully, even though it might be a lie
|
||||||
u32 available_bytes_in_cb;
|
SendComplete();
|
||||||
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);
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CEXIETHERNET::resume()
|
|
||||||
{
|
|
||||||
if (!isActivated())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
INFO_LOG(SP1, "BBA resume");
|
|
||||||
//mStop = false;
|
|
||||||
|
|
||||||
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()
|
|
||||||
{
|
|
||||||
if (!isActivated())
|
|
||||||
return false;// Should actually be an assert
|
|
||||||
|
|
||||||
INFO_LOG(SP1, "startRecv... ");
|
|
||||||
|
|
||||||
if (mWaiting)
|
|
||||||
{
|
|
||||||
INFO_LOG(SP1, "already waiting");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD res = ReadFile(mHAdapter, mRecvBuffer, BBA_RECV_SIZE,
|
|
||||||
&mRecvBufferLength, &mReadOverlapped);
|
|
||||||
|
|
||||||
if (res)
|
|
||||||
{
|
|
||||||
// Operation completed immediately
|
|
||||||
INFO_LOG(SP1, "completed, res %i", res);
|
|
||||||
mWaiting = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
res = GetLastError();
|
|
||||||
if (res == ERROR_IO_PENDING)
|
|
||||||
{
|
|
||||||
//'s ok :)
|
|
||||||
INFO_LOG(SP1, "pending");
|
|
||||||
// WaitCallback will be called
|
|
||||||
mWaiting = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// error occurred
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID CALLBACK CEXIETHERNET::ReadWaitCallback(PVOID lpParameter, BOOLEAN TimerFired)
|
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;
|
CEXIETHERNET* self = (CEXIETHERNET*)lpParameter;
|
||||||
__assume(self->mHAdapter != INVALID_HANDLE_VALUE);
|
|
||||||
GetOverlappedResult(self->mHAdapter, &self->mReadOverlapped,
|
GetOverlappedResult(self->mHAdapter, &self->mReadOverlapped,
|
||||||
&self->mRecvBufferLength, false);
|
(LPDWORD)&self->mRecvBufferLength, false);
|
||||||
self->mWaiting = false;
|
|
||||||
self->handleRecvdPacket();
|
self->RecvHandlePacket();
|
||||||
INFO_LOG(SP1, "WaitCallback %i done", cNumber);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
union bba_descr
|
bool CEXIETHERNET::RecvInit()
|
||||||
{
|
{
|
||||||
struct { u32 next_packet_ptr:12, packet_len:12, status:8; };
|
// Set up recv event
|
||||||
u32 word;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool CEXIETHERNET::cbwriteDescriptor(u32 size)
|
if ((mHRecvEvent = CreateEvent(NULL, false, false, NULL)) == NULL)
|
||||||
{
|
|
||||||
if (size < SIZEOF_ETH_HEADER)
|
|
||||||
{
|
{
|
||||||
INFO_LOG(SP1, "Packet too small: %i bytes", size);
|
ERROR_LOG(SP1, "Failed to create recv event:%x", GetLastError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The descriptor supposed to include the size of itself
|
ZeroMemory(&mReadOverlapped, sizeof(mReadOverlapped));
|
||||||
size += SIZEOF_RECV_DESCRIPTOR;
|
|
||||||
|
|
||||||
//We should probably not implement wraparound here,
|
RegisterWaitForSingleObject(&mHReadWait, mHRecvEvent, ReadWaitCallback,
|
||||||
//since neither tmbinc, riptool.dol, or libogc does...
|
this, INFINITE, WT_EXECUTEDEFAULT);
|
||||||
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;
|
mReadOverlapped.hEvent = mHRecvEvent;
|
||||||
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);
|
|
||||||
|
|
||||||
mCbw.write(&descr.word, SIZEOF_RECV_DESCRIPTOR);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//#pragma optimize("",on)
|
|
||||||
|
bool CEXIETHERNET::RecvStart()
|
||||||
|
{
|
||||||
|
if (!IsActivated())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (mHRecvEvent == INVALID_HANDLE_VALUE)
|
||||||
|
RecvInit();
|
||||||
|
|
||||||
|
DWORD res = ReadFile(mHAdapter, mRecvBuffer, BBA_RECV_SIZE,
|
||||||
|
(LPDWORD)&mRecvBufferLength, &mReadOverlapped);
|
||||||
|
|
||||||
|
if (!res && (GetLastError() != ERROR_IO_PENDING))
|
||||||
|
{
|
||||||
|
// error occurred
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res)
|
||||||
|
{
|
||||||
|
// Completed immediately
|
||||||
|
RecvHandlePacket();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXIETHERNET::RecvStop()
|
||||||
|
{
|
||||||
|
if (!IsActivated())
|
||||||
|
return;
|
||||||
|
|
||||||
|
UnregisterWaitEx(mHReadWait, INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
|
CloseHandle(mHRecvEvent);
|
||||||
|
mHRecvEvent = INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
|
|
@ -129,7 +129,7 @@ IEXIDevice* EXIDevice_Create(TEXIDevices device_type, const int channel_num)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EXIDEVICE_ETH:
|
case EXIDEVICE_ETH:
|
||||||
result = new CEXIETHERNET(SConfig::GetInstance().m_bba_mac);
|
result = new CEXIETHERNET();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EXIDEVICE_AM_BASEBOARD:
|
case EXIDEVICE_AM_BASEBOARD:
|
||||||
|
|
|
@ -16,87 +16,130 @@
|
||||||
// http://code.google.com/p/dolphin-emu/
|
// http://code.google.com/p/dolphin-emu/
|
||||||
|
|
||||||
#include "Memmap.h"
|
#include "Memmap.h"
|
||||||
//#pragma optimize("",off)
|
|
||||||
#include "EXI_Device.h"
|
#include "EXI_Device.h"
|
||||||
#include "EXI_DeviceEthernet.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),
|
// TODO move this code into Common or something, for IOS to use
|
||||||
m_uCommand(0),
|
|
||||||
mWriteBuffer(2048),
|
enum MACConsumer
|
||||||
mCbw(mBbaMem + CB_OFFSET, CB_SIZE)
|
|
||||||
{
|
{
|
||||||
const u8 mac_address_default[6] =
|
BBA,
|
||||||
{ 0x00, 0x09, 0xbf, 0x01, 0x00, 0xc1 };
|
IOS
|
||||||
|
};
|
||||||
|
|
||||||
memset(mBbaMem, 0, BBA_MEM_SIZE);
|
void GenerateMAC(MACConsumer type, u8 (&mac)[6])
|
||||||
|
{
|
||||||
int x = 0;
|
memset(mac, 0, 6);
|
||||||
u8 new_addr[6] = { 0 };
|
|
||||||
for (int i = 0; i < (int)mac_addr.size() && x < 12; i++)
|
u8 const oui_bba[] = { 0x00, 0x09, 0xbf };
|
||||||
|
u8 const oui_ios[] = { 0x00, 0x17, 0xab };
|
||||||
|
|
||||||
|
switch (type)
|
||||||
{
|
{
|
||||||
char c = mac_addr.at(i);
|
case BBA:
|
||||||
if (c >= '0' && c <= '9') {
|
memcpy(mac, oui_bba, 3);
|
||||||
new_addr[x / 2] |= (c - '0') << ((x & 1) ? 0 : 4); x++;
|
break;
|
||||||
} else if (c >= 'A' && c <= 'F') {
|
case IOS:
|
||||||
new_addr[x / 2] |= (c - 'A' + 10) << ((x & 1) ? 0 : 4); x++;
|
memcpy(mac, oui_ios, 3);
|
||||||
} else if (c >= 'a' && c <= 'f') {
|
break;
|
||||||
new_addr[x / 2] |= (c - 'a' + 10) << ((x & 1) ? 0 : 4); x++;
|
}
|
||||||
|
|
||||||
|
srand((unsigned int)time(nullptr));
|
||||||
|
|
||||||
|
u8 id[3] =
|
||||||
|
{
|
||||||
|
(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",
|
if (!mac_addr_valid)
|
||||||
mac_address[0], mac_address[1], mac_address[2],
|
{
|
||||||
mac_address[3], mac_address[4], mac_address[5]);
|
GenerateMAC(BBA, mac_addr);
|
||||||
|
|
||||||
mWriteP = INVALID_P;
|
mac_addr_setting = ArrayToString(mac_addr, 6, 10, false);
|
||||||
mReadP = INVALID_P;
|
SConfig::GetInstance().SaveSettings();
|
||||||
mWaiting = false;
|
|
||||||
mReadyToSend = false;
|
|
||||||
Activated = false;
|
|
||||||
m_bInterruptSet = false;
|
|
||||||
|
|
||||||
#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;
|
mHAdapter = INVALID_HANDLE_VALUE;
|
||||||
mHRecvEvent = INVALID_HANDLE_VALUE;
|
mHRecvEvent = INVALID_HANDLE_VALUE;
|
||||||
mHReadWait = INVALID_HANDLE_VALUE;
|
mHReadWait = INVALID_HANDLE_VALUE;
|
||||||
|
#elif defined(__linux__)
|
||||||
|
fd = -1;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
mRecvBufferLength = 0;
|
|
||||||
|
|
||||||
mExpectSpecialImmRead = false;
|
|
||||||
mExpectVariableLengthImmWrite = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CEXIETHERNET::~CEXIETHERNET()
|
CEXIETHERNET::~CEXIETHERNET()
|
||||||
{
|
{
|
||||||
/*/ crashy crashy
|
Deactivate();
|
||||||
if (isActivated())
|
|
||||||
deactivate();
|
delete tx_fifo;
|
||||||
//*/
|
delete mBbaMem;
|
||||||
|
delete mRecvBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEXIETHERNET::SetCS(int cs)
|
void CEXIETHERNET::SetCS(int cs)
|
||||||
{
|
{
|
||||||
INFO_LOG(SP1, "chip select: %s%s", cs ? "true" : "false",
|
if (cs)
|
||||||
mExpectVariableLengthImmWrite ? ", expecting variable write" : "");
|
|
||||||
if (!cs)
|
|
||||||
{
|
{
|
||||||
if (mExpectVariableLengthImmWrite)
|
// Invalidate the previous transfer
|
||||||
{
|
transfer.valid = false;
|
||||||
INFO_LOG(SP1, "Variable write complete. Final size: %i bytes",
|
|
||||||
mWriteBuffer.size());
|
|
||||||
mExpectVariableLengthImmWrite = false;
|
|
||||||
mReadyToSend = true;
|
|
||||||
}
|
|
||||||
mExpectSpecialImmRead = false;
|
|
||||||
mWriteP = mReadP = INVALID_P;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,336 +148,515 @@ bool CEXIETHERNET::IsPresent()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEXIETHERNET::Update()
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CEXIETHERNET::IsInterruptSet()
|
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);
|
data >>= (4 - size) * 8;
|
||||||
if (mBbaMem[BBA_IMR] & INT_T)
|
|
||||||
|
if (!transfer.valid)
|
||||||
{
|
{
|
||||||
mBbaMem[BBA_IR] |= INT_T;
|
transfer.valid = true;
|
||||||
INFO_LOG(SP1, "\t\tBBA Send interrupt raised");
|
transfer.region = IsMXCommand(data) ? transfer.MX : transfer.EXI;
|
||||||
m_bInterruptSet = true;
|
if (transfer.region == transfer.EXI)
|
||||||
}
|
transfer.address = ((data & ~0xc000) >> 8) & 0xff;
|
||||||
// TODO why did sonic put this here?
|
|
||||||
//startRecv();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CEXIETHERNET::checkRecvBuffer()
|
|
||||||
{
|
|
||||||
if (mRecvBufferLength)
|
|
||||||
handleRecvdPacket();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CEXIETHERNET::ImmWrite(u32 data, u32 size)
|
|
||||||
{
|
|
||||||
if (size == 1)
|
|
||||||
data = (u8)Common::swap32(data);
|
|
||||||
else if (size == 2)
|
|
||||||
data >>= 16;
|
|
||||||
|
|
||||||
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);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (mWriteP != INVALID_P)
|
|
||||||
{
|
|
||||||
if (mWriteP + size > BBA_MEM_SIZE)
|
|
||||||
{
|
|
||||||
ERROR_LOG(SP1, "write error: writep + size = %04x + %i", mWriteP, size);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
|
|
||||||
switch (mWriteP)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
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);
|
|
||||||
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();
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
else if (size == 2 && data == 0)
|
|
||||||
{
|
|
||||||
INFO_LOG(SP1, "\trequest cid");
|
|
||||||
mSpecialImmData = EXI_DEVTYPE_ETHER;
|
|
||||||
mExpectSpecialImmRead = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if ((size == 4 && (data & 0xC0000000) == 0xC0000000) ||
|
|
||||||
(size == 2 && (data & 0x4000) == 0x4000))
|
|
||||||
{
|
|
||||||
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...
|
|
||||||
|
|
||||||
if (mWriteP == BBA_WRTXFIFOD)
|
|
||||||
{
|
|
||||||
mWriteBuffer.clear();
|
|
||||||
mExpectVariableLengthImmWrite = true;
|
|
||||||
INFO_LOG(SP1, "\t\tprepared for variable length write to address 0x48");
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
|
transfer.address = (data >> 8) & 0xffff;
|
||||||
|
transfer.direction = IsWriteCommand(data) ? transfer.WRITE : transfer.READ;
|
||||||
|
|
||||||
|
DEBUG_LOG(SP1, "%s %s %s %x",
|
||||||
|
IsMXCommand(data) ? "mx " : "exi",
|
||||||
|
IsWriteCommand(data) ? "write" : "read ",
|
||||||
|
GetRegisterName(),
|
||||||
|
transfer.address);
|
||||||
|
|
||||||
|
if (transfer.address == BBA_IOB && transfer.region == transfer.MX)
|
||||||
{
|
{
|
||||||
INFO_LOG(SP1, "\t\twritep set to %0*x", size * 2, mWriteP);
|
ERROR_LOG(SP1, "Usage of BBA_IOB indicates that the rx packet descriptor has been corrupted, killing dolphin...");
|
||||||
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// transfer has been setup
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if ((size == 4 && (data & 0xC0000000) == 0x80000000) ||
|
|
||||||
(size == 2 && (data & 0x4000) == 0x0000))
|
// 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)
|
||||||
{
|
{
|
||||||
// Read from BBA Register
|
switch (transfer.address)
|
||||||
if (size == 4)
|
|
||||||
mReadP = (data >> 8) & 0xffff;
|
|
||||||
else //size == 2
|
|
||||||
mReadP = (u8)getbitsw(data, 16, 23);
|
|
||||||
|
|
||||||
INFO_LOG(SP1, "Read from BBA register 0x%x", mReadP);
|
|
||||||
|
|
||||||
switch (mReadP)
|
|
||||||
{
|
{
|
||||||
case BBA_NCRA:
|
case INTERRUPT:
|
||||||
// These Two lines were commented out in Whinecube
|
exi_status.interrupt &= data ^ 0xff;
|
||||||
//mBbaMem[mReadP] = 0x00;
|
|
||||||
//if(!sendInProgress())
|
|
||||||
mBbaMem[BBA_NCRA] &= ~(NCRA_ST0 | NCRA_ST1);
|
|
||||||
INFO_LOG(SP1, "\tNCRA %02x", mBbaMem[BBA_NCRA]);
|
|
||||||
break;
|
break;
|
||||||
case BBA_NCRB: // Revision ID
|
case INTERRUPT_MASK:
|
||||||
INFO_LOG(SP1, "\tNCRB");
|
exi_status.interrupt_mask = data;
|
||||||
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;
|
break;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
ERROR_LOG(SP1, "\tNot expecting imm write of size %d", size);
|
{
|
||||||
ERROR_LOG(SP1, "\t\t SKIPPING!");
|
MXCommandHandler(data, size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 CEXIETHERNET::ImmRead(u32 size)
|
u32 CEXIETHERNET::ImmRead(u32 size)
|
||||||
{
|
{
|
||||||
INFO_LOG(SP1, "imm read %i readp %x", size, mReadP);
|
u32 ret = 0;
|
||||||
|
|
||||||
if (mExpectSpecialImmRead)
|
if (transfer.region == transfer.EXI)
|
||||||
{
|
{
|
||||||
INFO_LOG(SP1, "\tspecial imm read %08x", mSpecialImmData);
|
switch (transfer.address)
|
||||||
mExpectSpecialImmRead = false;
|
|
||||||
return mSpecialImmData;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mReadP != INVALID_P)
|
|
||||||
{
|
|
||||||
if (mReadP + size > BBA_MEM_SIZE)
|
|
||||||
{
|
{
|
||||||
ERROR_LOG(SP1, "\tRead error: readp + size = %04x + %i", mReadP, size);
|
case EXI_ID:
|
||||||
return 0;
|
ret = EXI_DEVTYPE_ETHER;
|
||||||
|
break;
|
||||||
|
case REVISION_ID:
|
||||||
|
ret = exi_status.revision_id;
|
||||||
|
break;
|
||||||
|
case DEVICE_ID:
|
||||||
|
ret = exi_status.device_id;
|
||||||
|
break;
|
||||||
|
case ACSTART:
|
||||||
|
ret = exi_status.acstart;
|
||||||
|
break;
|
||||||
|
case INTERRUPT:
|
||||||
|
ret = exi_status.interrupt;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
u32 uResult = 0;
|
|
||||||
memcpy(&uResult, mBbaMem + mReadP, size);
|
|
||||||
|
|
||||||
uResult = Common::swap32(uResult);
|
transfer.address += size;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ERROR_LOG(SP1, "Unhandled imm read size %d", size);
|
for (int i = size - 1; i >= 0; i--)
|
||||||
return 0;
|
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)
|
void CEXIETHERNET::DMAWrite(u32 addr, u32 size)
|
||||||
{
|
{
|
||||||
if (mExpectVariableLengthImmWrite)
|
DEBUG_LOG(SP1, "dma w: %08x %x", addr, size);
|
||||||
{
|
|
||||||
INFO_LOG(SP1, "DMA Write: Address is 0x%x and size is 0x%x", addr, size);
|
|
||||||
mWriteBuffer.write(size, Memory::GetPointer(addr));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ERROR_LOG(SP1, "Unhandled BBA DMA write: %i, %08x", size, addr);
|
if (transfer.region == transfer.MX &&
|
||||||
|
transfer.direction == transfer.WRITE &&
|
||||||
|
transfer.address == BBA_WRTXFIFOD)
|
||||||
|
{
|
||||||
|
DirectFIFOWrite(Memory::GetPointer(addr), size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
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)
|
void CEXIETHERNET::DMARead(u32 addr, u32 size)
|
||||||
{
|
{
|
||||||
if (mReadP != INVALID_P)
|
DEBUG_LOG(SP1, "dma r: %08x %x", addr, size);
|
||||||
{
|
|
||||||
if (mReadP + size > BBA_MEM_SIZE)
|
|
||||||
{
|
|
||||||
INFO_LOG(SP1, "Read error: mReadP + size = %04x + %i", mReadP, size);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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);
|
memcpy(Memory::GetPointer(addr), &mBbaMem[transfer.address], size);
|
||||||
|
|
||||||
|
transfer.address += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEXIETHERNET::DoState(PointerWrap &p)
|
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(mBbaMem);
|
||||||
p.Do(mExpectVariableLengthImmWrite);
|
// TODO ... the rest...
|
||||||
p.Do(mReadyToSend);
|
ERROR_LOG(SP1, "CEXIETHERNET::DoState not implemented!");
|
||||||
p.Do(RegisterBlock);
|
|
||||||
// TODO?
|
|
||||||
//mWriteBuffer.DoState(p);
|
|
||||||
//mCbw.DoState(p);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//#pragma optimize("",on)
|
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)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
{
|
||||||
|
switch (transfer.address)
|
||||||
|
{
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#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)
|
||||||
|
{
|
||||||
|
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");
|
||||||
|
|
||||||
|
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)))
|
||||||
|
{
|
||||||
|
// 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];
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXIETHERNET::SendFromDirectFIFO()
|
||||||
|
{
|
||||||
|
SendFrame(tx_fifo, *(u16 *)&mBbaMem[BBA_TXFIFOCNT]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXIETHERNET::SendFromPacketBuffer()
|
||||||
|
{
|
||||||
|
ERROR_LOG(SP1, "tx packet buffer not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXIETHERNET::SendComplete()
|
||||||
|
{
|
||||||
|
mBbaMem[BBA_NCRA] &= ~(NCRA_ST0 | NCRA_ST1);
|
||||||
|
*(u16 *)&mBbaMem[BBA_TXFIFOCNT] = 0;
|
||||||
|
|
||||||
|
if (mBbaMem[BBA_IMR] & INT_T)
|
||||||
|
{
|
||||||
|
mBbaMem[BBA_IR] |= INT_T;
|
||||||
|
|
||||||
|
exi_status.interrupt |= exi_status.TRANSFER;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
u8 cur_byte = dest_eth_addr[byte_num];
|
||||||
|
for (size_t bit = 0; bit < 8; ++bit)
|
||||||
|
{
|
||||||
|
u8 carry = ((crc >> 31) & 1) ^ (cur_byte & 1);
|
||||||
|
crc <<= 1;
|
||||||
|
cur_byte >>= 1;
|
||||||
|
if (carry)
|
||||||
|
crc = (crc ^ 0x4c11db6) | carry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return bits used as index
|
||||||
|
return crc >> 26;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline 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
|
||||||
|
{
|
||||||
|
// Lookup the dest eth address in the hashmap
|
||||||
|
u16 index = HashIndex(mRecvBuffer);
|
||||||
|
return !!(mBbaMem[BBA_NAFR_MAR0 + index / 8] & (1 << (index % 8)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void CEXIETHERNET::inc_rwp()
|
||||||
|
{
|
||||||
|
u16 *rwp = (u16 *)&mBbaMem[BBA_RWP];
|
||||||
|
|
||||||
|
if (*rwp + 1 == page_ptr(BBA_RHBP))
|
||||||
|
*rwp = page_ptr(BBA_BP);
|
||||||
|
else
|
||||||
|
(*rwp)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
*write_ptr++ = mRecvBuffer[i];
|
||||||
|
|
||||||
|
if (off == 0xff)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -20,107 +20,16 @@
|
||||||
|
|
||||||
#include "Thread.h"
|
#include "Thread.h"
|
||||||
|
|
||||||
inline u8 makemaskb(int start, int end) {
|
// Network Control Register A
|
||||||
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
|
|
||||||
enum NCRA
|
enum NCRA
|
||||||
{
|
{
|
||||||
NCRA_RESET = 0x01, // RESET
|
NCRA_RESET = 0x01, // RESET
|
||||||
NCRA_ST0 = 0x02, // Start transmit command/status
|
NCRA_ST0 = 0x02, // Start transmit command/status
|
||||||
NCRA_ST1 = 0x04, // "
|
NCRA_ST1 = 0x04, // "
|
||||||
NCRA_SR = 0x08, // Start Receive
|
NCRA_SR = 0x08 // Start Receive
|
||||||
};
|
};
|
||||||
|
|
||||||
// Network Control Register B, RW
|
// Network Control Register B
|
||||||
enum NCRB
|
enum NCRB
|
||||||
{
|
{
|
||||||
NCRB_PR = 0x01, // Promiscuous Mode
|
NCRB_PR = 0x01, // Promiscuous Mode
|
||||||
|
@ -129,11 +38,11 @@ enum NCRB
|
||||||
NCRB_PB = 0x08, // Pass Bad Frame
|
NCRB_PB = 0x08, // Pass Bad Frame
|
||||||
NCRB_AB = 0x10, // Accept Broadcast
|
NCRB_AB = 0x10, // Accept Broadcast
|
||||||
NCRB_HBD = 0x20, // reserved
|
NCRB_HBD = 0x20, // reserved
|
||||||
NCRB_RXINTC = 0xC0, // Receive Interrupt Counter (mask)
|
NCRB_RXINTC = 0xC0 // Receive Interrupt Counter (mask)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Interrupt Mask Register, RW, 00h
|
// Interrupt Mask Register
|
||||||
// Interrupt Register, RW, 00h
|
// Interrupt Register
|
||||||
enum Interrupts
|
enum Interrupts
|
||||||
{
|
{
|
||||||
INT_FRAG = 0x01, // Fragment Counter
|
INT_FRAG = 0x01, // Fragment Counter
|
||||||
|
@ -143,17 +52,20 @@ enum Interrupts
|
||||||
INT_T_ERR = 0x10, // Transmit Error
|
INT_T_ERR = 0x10, // Transmit Error
|
||||||
INT_FIFO_ERR = 0x20, // FIFO Error
|
INT_FIFO_ERR = 0x20, // FIFO Error
|
||||||
INT_BUS_ERR = 0x40, // BUS 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
|
enum NWAYC
|
||||||
{
|
{
|
||||||
NWAYC_FD = 0x01, // Full Duplex Mode
|
NWAYC_FD = 0x01, // Full Duplex Mode
|
||||||
NWAYC_PS100 = 0x02, // Port Select 100/10
|
NWAYC_PS100_10 = 0x02, // Port Select 100/10
|
||||||
NWAYC_ANE = 0x03, // Autonegotiation Enable
|
NWAYC_ANE = 0x04, // Autonegotiate enable
|
||||||
NWAYC_ANS_RA = 0x04, // Restart Autonegotiation
|
|
||||||
NWAYC_LTE = 0x08, // Link Test Enable
|
// Autonegotiation status bits...
|
||||||
|
|
||||||
|
NWAYC_NTTEST = 0x40, // Reserved
|
||||||
|
NWAYC_LTE = 0x80 // Link Test Enable
|
||||||
};
|
};
|
||||||
|
|
||||||
enum NWAYS
|
enum NWAYS
|
||||||
|
@ -168,6 +80,30 @@ enum NWAYS
|
||||||
NWAYS_10TXH = 0x80
|
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
|
enum
|
||||||
{
|
{
|
||||||
BBA_NCRA = 0x00,
|
BBA_NCRA = 0x00,
|
||||||
|
@ -182,6 +118,7 @@ enum
|
||||||
BBA_BP = 0x0a,
|
BBA_BP = 0x0a,
|
||||||
BBA_TLBP = 0x0c,
|
BBA_TLBP = 0x0c,
|
||||||
BBA_TWP = 0x0e,
|
BBA_TWP = 0x0e,
|
||||||
|
BBA_IOB = 0x10,
|
||||||
BBA_TRP = 0x12,
|
BBA_TRP = 0x12,
|
||||||
BBA_RWP = 0x16,
|
BBA_RWP = 0x16,
|
||||||
BBA_RRP = 0x18,
|
BBA_RRP = 0x18,
|
||||||
|
@ -195,6 +132,14 @@ enum
|
||||||
BBA_NAFR_PAR3 = 0x23,
|
BBA_NAFR_PAR3 = 0x23,
|
||||||
BBA_NAFR_PAR4 = 0x24,
|
BBA_NAFR_PAR4 = 0x24,
|
||||||
BBA_NAFR_PAR5 = 0x25,
|
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_NWAYC = 0x30,
|
||||||
BBA_NWAYS = 0x31,
|
BBA_NWAYS = 0x31,
|
||||||
|
@ -210,37 +155,51 @@ enum
|
||||||
|
|
||||||
BBA_SI_ACTRL = 0x5c,
|
BBA_SI_ACTRL = 0x5c,
|
||||||
BBA_SI_STATUS = 0x5d,
|
BBA_SI_STATUS = 0x5d,
|
||||||
BBA_SI_ACTRL2 = 0x60,
|
BBA_SI_ACTRL2 = 0x60
|
||||||
};
|
};
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
BBA_RECV_SIZE = 0x800,
|
BBA_NUM_PAGES = 0x10,
|
||||||
BBA_MEM_SIZE = 0x1000,
|
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,
|
|
||||||
|
|
||||||
EXI_DEVTYPE_ETHER = 0x04020200,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum
|
enum { EXI_DEVTYPE_ETHER = 0x04020200 };
|
||||||
|
|
||||||
|
enum SendStatus
|
||||||
{
|
{
|
||||||
EXPECT_NONE = 0,
|
DESC_CC0 = 0x01,
|
||||||
EXPECT_ID
|
DESC_CC1 = 0x02,
|
||||||
|
DESC_CC2 = 0x04,
|
||||||
|
DESC_CC3 = 0x08,
|
||||||
|
DESC_CRSLOST= 0x10,
|
||||||
|
DESC_UF = 0x20,
|
||||||
|
DESC_OWC = 0x40,
|
||||||
|
DESC_OWN = 0x80
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum RecvStatus
|
||||||
|
{
|
||||||
|
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
|
class CEXIETHERNET : public IEXIDevice
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CEXIETHERNET(const std::string& mac_addr);
|
CEXIETHERNET();
|
||||||
~CEXIETHERNET();
|
virtual ~CEXIETHERNET();
|
||||||
void SetMAC(u8 *new_addr);
|
void SetCS(int cs);
|
||||||
void SetCS(int _iCS);
|
|
||||||
bool IsPresent();
|
bool IsPresent();
|
||||||
void Update();
|
|
||||||
bool IsInterruptSet();
|
bool IsInterruptSet();
|
||||||
void ImmWrite(u32 data, u32 size);
|
void ImmWrite(u32 data, u32 size);
|
||||||
u32 ImmRead(u32 size);
|
u32 ImmRead(u32 size);
|
||||||
|
@ -249,61 +208,133 @@ public:
|
||||||
void DoState(PointerWrap &p);
|
void DoState(PointerWrap &p);
|
||||||
|
|
||||||
//private:
|
//private:
|
||||||
// STATE_TO_SAVE
|
struct
|
||||||
u32 m_uPosition;
|
{
|
||||||
u32 m_uCommand;
|
enum
|
||||||
|
{
|
||||||
|
READ,
|
||||||
|
WRITE
|
||||||
|
} direction;
|
||||||
|
|
||||||
bool m_bInterruptSet;
|
enum
|
||||||
u16 mWriteP, mReadP;
|
{
|
||||||
|
EXI,
|
||||||
|
MX
|
||||||
|
} region;
|
||||||
|
|
||||||
bool mExpectSpecialImmRead; //reset to false on deselect
|
u16 address;
|
||||||
u32 mSpecialImmData;
|
bool valid;
|
||||||
bool Activated;
|
} transfer;
|
||||||
|
|
||||||
u16 mRBRPP; //RRP - Receive Buffer Read Page Pointer
|
|
||||||
bool mRBEmpty;
|
|
||||||
|
|
||||||
u8 mBbaMem[BBA_MEM_SIZE];
|
|
||||||
|
|
||||||
WriteBuffer mWriteBuffer;
|
|
||||||
CyclicBufferWriter mCbw;
|
|
||||||
|
|
||||||
bool mExpectVariableLengthImmWrite;
|
|
||||||
bool mReadyToSend;
|
|
||||||
u8 RegisterBlock[0x1000];
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
CMD_ID = 0x00,
|
EXI_ID,
|
||||||
CMD_READ_REG = 0x01,
|
REVISION_ID,
|
||||||
|
INTERRUPT_MASK,
|
||||||
|
INTERRUPT,
|
||||||
|
DEVICE_ID,
|
||||||
|
ACSTART,
|
||||||
|
HASH_READ = 8,
|
||||||
|
HASH_WRITE,
|
||||||
|
HASH_STATUS = 0xb,
|
||||||
|
RESET = 0xf
|
||||||
};
|
};
|
||||||
|
|
||||||
void recordSendComplete();
|
// exi regs
|
||||||
bool sendPacket(u8 *etherpckt, int size);
|
struct EXIStatus
|
||||||
bool checkRecvBuffer();
|
{
|
||||||
bool handleRecvdPacket();
|
enum
|
||||||
|
{
|
||||||
|
TRANSFER = 0x80
|
||||||
|
};
|
||||||
|
|
||||||
//TAP interface
|
u8 revision_id;
|
||||||
bool activate();
|
u8 interrupt_mask;
|
||||||
bool CheckRecieved();
|
u8 interrupt;
|
||||||
bool deactivate();
|
u16 device_id;
|
||||||
bool isActivated();
|
u8 acstart;
|
||||||
bool resume();
|
u32 hash_challenge;
|
||||||
bool startRecv();
|
u32 hash_response;
|
||||||
bool cbwriteDescriptor(u32 size);
|
u8 hash_status;
|
||||||
|
|
||||||
|
EXIStatus()
|
||||||
|
{
|
||||||
|
device_id = 0xd107;
|
||||||
|
revision_id = 0;//0xf0;
|
||||||
|
acstart = 0x4e;
|
||||||
|
|
||||||
volatile bool mWaiting;
|
interrupt_mask = 0;
|
||||||
u8 mac_address[6];
|
interrupt = 0;
|
||||||
u8 mRecvBuffer[BBA_RECV_SIZE];
|
hash_challenge = 0;
|
||||||
#ifdef _WIN32
|
hash_response = 0;
|
||||||
|
hash_status = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} exi_status;
|
||||||
|
|
||||||
|
struct Descriptor
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
HANDLE mHAdapter, mHRecvEvent, mHReadWait;
|
||||||
DWORD mMtu;
|
DWORD mMtu;
|
||||||
OVERLAPPED mReadOverlapped;
|
OVERLAPPED mReadOverlapped;
|
||||||
DWORD mRecvBufferLength;
|
|
||||||
static VOID CALLBACK ReadWaitCallback(PVOID lpParameter, BOOLEAN TimerFired);
|
static VOID CALLBACK ReadWaitCallback(PVOID lpParameter, BOOLEAN TimerFired);
|
||||||
#else
|
#elif defined(__linux__)
|
||||||
u32 mRecvBufferLength;
|
int fd;
|
||||||
|
std::thread readThread;
|
||||||
|
volatile bool readEnabled;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue