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..fed72e8712 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,165 @@ // 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 -#include -#include + +#ifdef __linux__ #include -#include -#include -#ifdef __linux__ #include -#else -#include +#include +#include +#include +#include #endif -#include - -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); - 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(); + readEnabled = false; + + INFO_LOG(SP1, "BBA initialized with associated tap %s", ifr.ifr_name); return true; +#else + NOTIMPLEMENTED("Activate"); + return false; +#endif +} + +void CEXIETHERNET::Deactivate() +{ +#ifdef __linux__ + close(fd); + fd = -1; + + // TODO: find a way to interrupt the read(2) in the readThread. Kill the + // thread maybe? + readEnabled = false; + if (readThread.joinable()) + readThread.join(); +#else + NOTIMPLEMENTED("Deactivate"); +#endif +} + +bool CEXIETHERNET::IsActivated() +{ +#ifdef __linux__ + return fd != -1 ? true : false; #else return false; #endif } -bool CEXIETHERNET::CheckRecieved() +bool CEXIETHERNET::SendFrame(u8* frame, u32 size) { - if(!isActivated()) +#ifdef __linux__ + WARN_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; - 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; +#endif } -bool CEXIETHERNET::resume() +void ReadThreadHandler(CEXIETHERNET* self) { - 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; -} - -void CpuThread(CEXIETHERNET *self) -{ - while(1) + while (true) { - 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(); + if (self->fd < 0) 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()) - 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) - { - 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 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)) + 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) + { + // HACK: This usleep is there to avoid BBA buffer overflow. Has to + // be replaced by a better throttling support at some point. + usleep(1000); + WARN_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 { - struct { u32 next_packet_ptr:12, packet_len:12, status:8; }; - u32 word; -}; - -bool CEXIETHERNET::cbwriteDescriptor(u32 size) +bool CEXIETHERNET::RecvInit() { - 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__ + readThread = std::thread(ReadThreadHandler, this); 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 } diff --git a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp index d4351d35ab..c04aab219e 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp +++ b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp @@ -54,10 +54,12 @@ CEXIETHERNET::CEXIETHERNET(const std::string& mac_addr) // hax .. fully established 100BASE-T link mBbaMem[BBA_NWAYS] = NWAYS_LS100 | NWAYS_LPNWAY | NWAYS_100TXF | NWAYS_ANCLPT; -#ifdef _WIN32 +#if defined(_WIN32) mHAdapter = INVALID_HANDLE_VALUE; mHRecvEvent = INVALID_HANDLE_VALUE; mHReadWait = INVALID_HANDLE_VALUE; +#elif defined(__linux__) + fd = -1; #endif mRecvBufferLength = 0; @@ -484,6 +486,11 @@ void CEXIETHERNET::inc_rwp() bool CEXIETHERNET::RecvHandlePacket() { + u8 *write_ptr; + u8 *end_ptr; + u8 *read_ptr; + Descriptor *descriptor; + if (!RecvMACFilter()) goto wait_for_next; @@ -498,11 +505,11 @@ bool CEXIETHERNET::RecvHandlePacket() PAGE_PTR(BBA_RWP), PAGE_PTR(BBA_RHBP)); - u8 *write_ptr = PTR_FROM_PAGE_PTR(BBA_RWP); - u8 *end_ptr = PTR_FROM_PAGE_PTR(BBA_RHBP); - u8 *read_ptr = PTR_FROM_PAGE_PTR(BBA_RRP); + 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 = (Descriptor *)write_ptr; + descriptor = (Descriptor *)write_ptr; //u8 *descriptor = write_ptr; write_ptr += 4; diff --git a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h index 5489131b08..adee2d39d5 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h +++ b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h @@ -303,15 +303,18 @@ public: u8 *mRecvBuffer; -#ifdef _WIN32 +#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 + + u32 mRecvBufferLength; }; #endif