From 3cb6e4a8647e62a6074caeeff18392096e619e47 Mon Sep 17 00:00:00 2001 From: sktsqrl Date: Fri, 29 Jun 2012 17:33:56 -0700 Subject: [PATCH 01/10] Redo BBA once again. Now it works on windows, if crappily. To use, install OpenVPN's TAP device driver. Then create a network bridge between the TAP and your device connected to the internet. TODO: proper overlapped read - can look at qemu impl non-windows impl --- Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp | 290 ++----- .../Core/Core/Src/HW/EXI_DeviceEthernet.cpp | 778 ++++++++++-------- Source/Core/Core/Src/HW/EXI_DeviceEthernet.h | 318 +++---- 3 files changed, 686 insertions(+), 700 deletions(-) diff --git a/Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp b/Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp index eee496ace6..d56cc9363e 100644 --- a/Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp +++ b/Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp @@ -187,27 +187,9 @@ bool OpenTAP(HANDLE& adapter, const std::string device_guid) } // namespace Win32TAPHelper -bool CEXIETHERNET::deactivate() +bool CEXIETHERNET::Activate() { - INFO_LOG(SP1, "Deactivating BBA..."); - if (!isActivated()) - return true; - CloseHandle(mHRecvEvent); - mHRecvEvent = INVALID_HANDLE_VALUE; - CloseHandle(mHAdapter); - mHAdapter = INVALID_HANDLE_VALUE; - INFO_LOG(SP1, "Success!"); - return true; -} - -bool CEXIETHERNET::isActivated() -{ - return mHAdapter != INVALID_HANDLE_VALUE; -} - -bool CEXIETHERNET::activate() -{ - if (isActivated()) + if (IsActivated()) return true; INFO_LOG(SP1, "Activating BBA..."); @@ -237,16 +219,15 @@ bool CEXIETHERNET::activate() /* get driver version info */ ULONG info[3]; - if (DeviceIoControl (mHAdapter, TAP_IOCTL_GET_VERSION, - &info, sizeof (info), &info, sizeof (info), &len, NULL)) + if (DeviceIoControl(mHAdapter, TAP_IOCTL_GET_VERSION, + &info, sizeof(info), &info, sizeof(info), &len, NULL)) { INFO_LOG(SP1, "TAP-Win32 Driver Version %d.%d %s", - info[0], info[1], (info[2] ? "(DEBUG)" : "")); + info[0], info[1], info[2] ? "(DEBUG)" : ""); } - if ( !(info[0] > TAP_WIN32_MIN_MAJOR - || (info[0] == TAP_WIN32_MIN_MAJOR && info[1] >= TAP_WIN32_MIN_MINOR)) ) + if (!(info[0] > TAP_WIN32_MIN_MAJOR || (info[0] == TAP_WIN32_MIN_MAJOR && info[1] >= TAP_WIN32_MIN_MINOR))) { - PanicAlertT("ERROR: This version of Dolphin requires a TAP-Win32 driver" + PanicAlertT("ERROR: This version of Dolphin requires a TAP-Win32 driver" " that is at least version %d.%d -- If you recently upgraded your Dolphin" " distribution, a reboot is probably required at this point to get" " Windows to see the new driver.", @@ -254,46 +235,42 @@ bool CEXIETHERNET::activate() return false; } - /* get driver MTU */ - if (!DeviceIoControl(mHAdapter, TAP_IOCTL_GET_MTU, - &mMtu, sizeof (mMtu), &mMtu, sizeof (mMtu), &len, NULL)) - { - INFO_LOG(SP1, "Couldn't get device MTU"); - return false; - } - INFO_LOG(SP1, "TAP-Win32 MTU=%d (ignored)", mMtu); - /* set driver media status to 'connected' */ ULONG status = TRUE; if (!DeviceIoControl(mHAdapter, TAP_IOCTL_SET_MEDIA_STATUS, - &status, sizeof (status), &status, sizeof (status), &len, NULL)) + &status, sizeof(status), &status, sizeof(status), &len, NULL)) { INFO_LOG(SP1, "WARNING: The TAP-Win32 driver rejected a" "TAP_IOCTL_SET_MEDIA_STATUS DeviceIoControl call."); return false; } - //set up recv event - if ((mHRecvEvent = CreateEvent(NULL, false, false, NULL)) == NULL) - { - INFO_LOG(SP1, "Failed to create recv event:%x", GetLastError()); - return false; - } - ZeroMemory(&mReadOverlapped, sizeof(mReadOverlapped)); - resume(); + //RecvInit(); INFO_LOG(SP1, "Success!"); return true; } -bool CEXIETHERNET::CheckRecieved() +void CEXIETHERNET::Deactivate() { - if (!isActivated()) - return false; + if (!IsActivated()) + return; - return false; + RecvStop(); + + CloseHandle(mHRecvEvent); + CloseHandle(mHAdapter); + + mHRecvEvent = INVALID_HANDLE_VALUE; + mHAdapter = INVALID_HANDLE_VALUE; } +bool CEXIETHERNET::IsActivated() +{ + return mHAdapter != INVALID_HANDLE_VALUE; +} + +// TODO check if still needed // Required for lwip...not sure why void fixup_ip_checksum(u16 *dataptr, u16 len) { @@ -313,220 +290,97 @@ void fixup_ip_checksum(u16 *dataptr, u16 len) *chksum = Common::swap16(~acc); } -bool CEXIETHERNET::sendPacket(u8 *etherpckt, int size) +bool CEXIETHERNET::SendFrame(u8 *frame, u32 size) { - fixup_ip_checksum((u16*)etherpckt + 7, 20); - INFO_LOG(SP1, "Packet (%zu):\n%s", size, ArrayToString(etherpckt, size, 0, 16).c_str()); + //fixup_ip_checksum((u16*)frame + 7, 20); + + DEBUG_LOG(SP1, "SendFrame %x\n%s", size, ArrayToString(frame, size, 0x10).c_str()); DWORD numBytesWrit; OVERLAPPED overlap; ZeroMemory(&overlap, sizeof(overlap)); //overlap.hEvent = mHRecvEvent; - if (!WriteFile(mHAdapter, etherpckt, size, &numBytesWrit, &overlap)) + + if (!WriteFile(mHAdapter, frame, size, &numBytesWrit, &overlap)) { // Fail Boat DWORD res = GetLastError(); - INFO_LOG(SP1, "Failed to send packet with error 0x%X", res); + WARN_LOG(SP1, "Failed to send packet with error 0x%X", res); } + if (numBytesWrit != size) { - INFO_LOG(SP1, "BBA sendPacket %i only got %i bytes sent!", size, numBytesWrit); + WARN_LOG(SP1, "BBA SendFrame %i only got %i bytes sent!", size, numBytesWrit); return false; } - recordSendComplete(); - 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 - 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); + SendComplete(); - 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() +VOID CALLBACK CEXIETHERNET::ReadWaitCallback(PVOID lpParameter, BOOLEAN TimerFired) { - if (!isActivated()) - return true; + CEXIETHERNET* self = (CEXIETHERNET*)lpParameter; + + GetOverlappedResult(self->mHAdapter, &self->mReadOverlapped, + &self->mRecvBufferLength, false); - INFO_LOG(SP1, "BBA resume"); - //mStop = false; + self->RecvHandlePacket(); +} + +bool CEXIETHERNET::RecvInit() +{ + // 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)); RegisterWaitForSingleObject(&mHReadWait, mHRecvEvent, ReadWaitCallback, this, INFINITE, WT_EXECUTEDEFAULT);//WT_EXECUTEINWAITTHREAD mReadOverlapped.hEvent = mHRecvEvent; - if (mBbaMem[BBA_NCRA] & NCRA_SR) - startRecv(); - - INFO_LOG(SP1, "BBA resume complete"); return true; } -bool CEXIETHERNET::startRecv() +bool CEXIETHERNET::RecvStart() { - if (!isActivated()) - return false;// Should actually be an assert + if (!IsActivated()) + return false; - INFO_LOG(SP1, "startRecv... "); - - if (mWaiting) - { - INFO_LOG(SP1, "already waiting"); - return true; - } + if (mHRecvEvent == INVALID_HANDLE_VALUE) + RecvInit(); DWORD res = ReadFile(mHAdapter, mRecvBuffer, BBA_RECV_SIZE, &mRecvBufferLength, &mReadOverlapped); + if (!res && (GetLastError() != ERROR_IO_PENDING)) + { + // error occurred + return false; + } + 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; - } + ERROR_LOG(SP1, "RECV COMPLETED IMMEDIATELY"); + RecvHandlePacket(); } return true; } -VOID CALLBACK CEXIETHERNET::ReadWaitCallback(PVOID lpParameter, BOOLEAN TimerFired) +void CEXIETHERNET::RecvStop() { - static int sNumber = 0; - int cNumber = sNumber++; - INFO_LOG(SP1, "WaitCallback %i", cNumber); - __assume(!TimerFired); - CEXIETHERNET* self = (CEXIETHERNET*)lpParameter; - __assume(self->mHAdapter != INVALID_HANDLE_VALUE); - GetOverlappedResult(self->mHAdapter, &self->mReadOverlapped, - &self->mRecvBufferLength, false); - self->mWaiting = false; - self->handleRecvdPacket(); - INFO_LOG(SP1, "WaitCallback %i done", cNumber); + if (!IsActivated()) + return; + + CancelIo(mHAdapter); } -union bba_descr -{ - struct { u32 next_packet_ptr:12, packet_len:12, status:8; }; - u32 word; -}; - -bool CEXIETHERNET::cbwriteDescriptor(u32 size) -{ - if (size < SIZEOF_ETH_HEADER) - { - INFO_LOG(SP1, "Packet too small: %i bytes", size); - return false; - } - - // The descriptor supposed to include the size of itself - size += SIZEOF_RECV_DESCRIPTOR; - - //We should probably not implement wraparound here, - //since neither tmbinc, riptool.dol, or libogc does... - if (mCbw.p_write() + SIZEOF_RECV_DESCRIPTOR >= CB_SIZE) - { - INFO_LOG(SP1, "The descriptor won't fit"); - return false; - } - if (size >= CB_SIZE) - { - INFO_LOG(SP1, "Packet too big: %i bytes", size); - return false; - } - - bba_descr descr; - descr.word = 0; - descr.packet_len = size; - descr.status = 0; - u32 npp; - if (mCbw.p_write() + size < CB_SIZE) - { - npp = (u32)(mCbw.p_write() + size + CB_OFFSET); - } - else - { - npp = (u32)(mCbw.p_write() + size + CB_OFFSET - CB_SIZE); - } - - npp = (npp + 0xff) & ~0xff; - - if (npp >= CB_SIZE + CB_OFFSET) - npp -= CB_SIZE; - - descr.next_packet_ptr = npp >> 8; - //DWORD swapped = swapw(descr.word); - //next_packet_ptr:12, packet_len:12, status:8; - INFO_LOG(SP1, "Writing descriptor 0x%08X @ 0x%04X: next 0x%03X len 0x%03X status 0x%02X", - descr.word, mCbw.p_write() + CB_OFFSET, descr.next_packet_ptr, - descr.packet_len, descr.status); - - mCbw.write(&descr.word, SIZEOF_RECV_DESCRIPTOR); - - return true; -} //#pragma optimize("",on) diff --git a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp index 6572233b20..d4351d35ab 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp +++ b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp @@ -19,19 +19,18 @@ //#pragma optimize("",off) #include "EXI_Device.h" #include "EXI_DeviceEthernet.h" +#include "StringUtil.h" -#define MAKE(type, arg) (*(type *)&(arg)) - -CEXIETHERNET::CEXIETHERNET(const std::string& mac_addr) : - m_uPosition(0), - m_uCommand(0), - mWriteBuffer(2048), - mCbw(mBbaMem + CB_OFFSET, CB_SIZE) +CEXIETHERNET::CEXIETHERNET(const std::string& mac_addr) { + tx_fifo = new u8[1518]; + mBbaMem = new u8[BBA_MEM_SIZE]; + mRecvBuffer = new u8 [BBA_RECV_SIZE]; + + MXHardReset(); + const u8 mac_address_default[6] = { 0x00, 0x09, 0xbf, 0x01, 0x00, 0xc1 }; - - memset(mBbaMem, 0, BBA_MEM_SIZE); int x = 0; u8 new_addr[6] = { 0 }; @@ -46,21 +45,14 @@ CEXIETHERNET::CEXIETHERNET(const std::string& mac_addr) : new_addr[x / 2] |= (c - 'a' + 10) << ((x & 1) ? 0 : 4); x++; } } + if (x / 2 == 6) - memcpy(mac_address, new_addr, 6); + memcpy(&mBbaMem[BBA_NAFR_PAR0], new_addr, 6); else - memcpy(mac_address, mac_address_default, 6); + memcpy(&mBbaMem[BBA_NAFR_PAR0], 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]); - - mWriteP = INVALID_P; - mReadP = INVALID_P; - mWaiting = false; - mReadyToSend = false; - Activated = false; - m_bInterruptSet = false; + // hax .. fully established 100BASE-T link + mBbaMem[BBA_NWAYS] = NWAYS_LS100 | NWAYS_LPNWAY | NWAYS_100TXF | NWAYS_ANCLPT; #ifdef _WIN32 mHAdapter = INVALID_HANDLE_VALUE; @@ -69,34 +61,25 @@ CEXIETHERNET::CEXIETHERNET(const std::string& mac_addr) : #endif mRecvBufferLength = 0; - - mExpectSpecialImmRead = false; - mExpectVariableLengthImmWrite = false; } CEXIETHERNET::~CEXIETHERNET() { - /*/ crashy crashy - if (isActivated()) - deactivate(); - //*/ + Deactivate(); + + delete tx_fifo; + delete mBbaMem; + delete mRecvBuffer; } void CEXIETHERNET::SetCS(int cs) { - INFO_LOG(SP1, "chip select: %s%s", cs ? "true" : "false", - mExpectVariableLengthImmWrite ? ", expecting variable write" : ""); - if (!cs) + DEBUG_LOG(SP1, "chip select: %s", cs ? "true" : "false"); + + if (cs) { - if (mExpectVariableLengthImmWrite) - { - INFO_LOG(SP1, "Variable write complete. Final size: %i bytes", - mWriteBuffer.size()); - mExpectVariableLengthImmWrite = false; - mReadyToSend = true; - } - mExpectSpecialImmRead = false; - mWriteP = mReadP = INVALID_P; + // Invalidate the previous transfer + transfer.valid = false; } } @@ -105,336 +88,477 @@ bool CEXIETHERNET::IsPresent() return true; } -void CEXIETHERNET::Update() -{ - return; -} - bool CEXIETHERNET::IsInterruptSet() { - return m_bInterruptSet; + return !!(exi_status.interrupt & exi_status.interrupt_mask); } -void CEXIETHERNET::recordSendComplete() +void CEXIETHERNET::ImmWrite(u32 data, u32 size) { - mBbaMem[BBA_NCRA] &= ~(NCRA_ST0 | NCRA_ST1); - if (mBbaMem[BBA_IMR] & INT_T) + data >>= (4 - size) * 8; + + if (!transfer.valid) { - mBbaMem[BBA_IR] |= INT_T; - INFO_LOG(SP1, "\t\tBBA Send interrupt raised"); - m_bInterruptSet = true; - } - // TODO why did sonic put this here? - //startRecv(); -} - -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"); - } + transfer.valid = true; + transfer.region = IsMXCommand(data) ? transfer.MX : transfer.EXI; + if (transfer.region == transfer.EXI) + transfer.address = ((data & ~0xc000) >> 8) & 0xff; else - { - INFO_LOG(SP1, "\t\twritep set to %0*x", size * 2, mWriteP); - } + transfer.address = (data >> 8) & 0xffff; + transfer.direction = IsWriteCommand(data) ? transfer.WRITE : transfer.READ; + + WARN_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) + exit(0); + + // transfer has been setup 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. + + WARN_LOG(SP1, "%s write %0*x", + transfer.region == transfer.MX ? "mx " : "exi", size * 2, data); + + if (transfer.region == transfer.EXI) { - // Read from BBA Register - 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) + switch (transfer.address) { - 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]); + case INTERRUPT: + exi_status.interrupt &= data ^ 0xff; 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); + case INTERRUPT_MASK: + exi_status.interrupt_mask = data; break; } - return; } - - ERROR_LOG(SP1, "\tNot expecting imm write of size %d", size); - ERROR_LOG(SP1, "\t\t SKIPPING!"); + else + { + MXCommandHandler(data, 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); - mExpectSpecialImmRead = false; - return mSpecialImmData; - } - - if (mReadP != INVALID_P) - { - if (mReadP + size > BBA_MEM_SIZE) + switch (transfer.address) { - ERROR_LOG(SP1, "\tRead error: readp + size = %04x + %i", mReadP, size); - return 0; + case EXI_ID: + 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); - - 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; + transfer.address += size; } else { - ERROR_LOG(SP1, "Unhandled imm read size %d", size); - return 0; + for (int i = size - 1; i >= 0; i--) + ret |= mBbaMem[transfer.address++] << (i * 8); } + + WARN_LOG(SP1, "imm r%i: %0*x", size, size * 2, ret); + + ret <<= (4 - size) * 8; + + return ret; } void CEXIETHERNET::DMAWrite(u32 addr, u32 size) { - 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; - } + WARN_LOG(SP1, "dma w: %08x %x", addr, size); - 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) { - if (mReadP != INVALID_P) - { - 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, "dma r: %08x %x", addr, size); - 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) { - 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); + // TODO ... the rest... +} + +bool CEXIETHERNET::IsMXCommand(u32 const data) +{ + return !!(data & (1 << 31)); +} + +bool CEXIETHERNET::IsWriteCommand(u32 const data) +{ + return IsMXCommand(data) ? !!(data & (1 << 30)) : !!(data & (1 << 14)); +} + +char const * const CEXIETHERNET::GetRegisterName() const +{ +#define STR_RETURN(x) case x: return #x; + + if (transfer.region == transfer.EXI) + { + switch (transfer.address) + { + 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) + { + WARN_LOG(SP1, "software reset"); + //MXSoftReset(); + Activate(); + } + + if ((mBbaMem[BBA_NCRA] & NCRA_SR) ^ (data & NCRA_SR)) + { + WARN_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))) + { + // TODO Might have to check TXDMA status as well? + + if (data & NCRA_ST0) + { + WARN_LOG(SP1, "start tx - local dma"); + SendFromPacketBuffer(); + } + else if (data & NCRA_ST1) + { + WARN_LOG(SP1, "start tx - direct fifo"); + SendFromDirectFIFO(); + // Kind of a hack: send completes instantly, so we don't + // actually write the "send in status" bit to the register + data &= ~NCRA_ST1; + } + } + goto write_to_register; + + case BBA_WRTXFIFOD: + if (size == 2) + data = Common::swap16(data & 0xffff); + else if (size == 4) + data = Common::swap32(data); + DirectFIFOWrite((u8 *)&data, size); + // Do not increment address + return; + + case BBA_IR: + data &= (data & 0xff) ^ 0xff; + goto write_to_register; + +write_to_register: + default: + { + for (int i = size - 1; i >= 0; i--) + { + mBbaMem[transfer.address++] = (data >> (i * 8)) & 0xff; + } + } + } +} + +void CEXIETHERNET::DirectFIFOWrite(u8 *data, u32 size) +{ + // In direct mode, the hardware handles creating the state required by the + // GMAC instead of finagling with packet descriptors and such + + u16 *tx_fifo_count = (u16 *)&mBbaMem[BBA_TXFIFOCNT]; + + memcpy(tx_fifo + *tx_fifo_count, data, size); + + *tx_fifo_count += size; + *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; +} + +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; +} + +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))); + } +} + +#define PAGE_PTR(x) ((mBbaMem[x + 1] << 8) | mBbaMem[x]) + +void CEXIETHERNET::inc_rwp() +{ + u16 *rwp = (u16 *)&mBbaMem[BBA_RWP]; + + if (*rwp + 1 == PAGE_PTR(BBA_RHBP)) + // TODO check if BP is used as well + *rwp = PAGE_PTR(BBA_BP); + else + (*rwp)++; +} + +bool CEXIETHERNET::RecvHandlePacket() +{ + if (!RecvMACFilter()) + goto wait_for_next; + + WARN_LOG(SP1, "RecvHandlePacket %x\n%s", mRecvBufferLength, + ArrayToString(mRecvBuffer, mRecvBufferLength, 0x100).c_str()); + +#define PTR_FROM_PAGE_PTR(x) &mBbaMem[PAGE_PTR(x) << 8] + + ERROR_LOG(SP1, "%x %x %x %x", + PAGE_PTR(BBA_BP), + PAGE_PTR(BBA_RRP), + PAGE_PTR(BBA_RWP), + PAGE_PTR(BBA_RHBP)); + + u8 *write_ptr = PTR_FROM_PAGE_PTR(BBA_RWP); + u8 *end_ptr = PTR_FROM_PAGE_PTR(BBA_RHBP); + u8 *read_ptr = PTR_FROM_PAGE_PTR(BBA_RRP); + + Descriptor *descriptor = (Descriptor *)write_ptr; + //u8 *descriptor = write_ptr; + write_ptr += 4; + + for (u32 i = 0, off = 4; i < mRecvBufferLength; ++i, ++off) + { + *write_ptr++ = mRecvBuffer[i]; + + if (off == 0xff) + { + off = 0; + inc_rwp(); + } + + if (write_ptr == end_ptr) + // TODO check if BP is used as well + write_ptr = PTR_FROM_PAGE_PTR(BBA_BP); + + if (write_ptr == read_ptr) + { + ERROR_LOG(SP1, "recv buffer full error - not implemented"); + } + } + + // Align up to next page + if ((mRecvBufferLength + 4) % 256) + inc_rwp(); + + ERROR_LOG(SP1, "%x %x %x %x", + PAGE_PTR(BBA_BP), + PAGE_PTR(BBA_RRP), + PAGE_PTR(BBA_RWP), + PAGE_PTR(BBA_RHBP)); + + // Update descriptor + descriptor->set(*(u16 *)&mBbaMem[BBA_RWP], 4 + mRecvBufferLength, 0); + + mBbaMem[BBA_LRPS] = descriptor->get_status(); + + // Raise interrupt + if (mBbaMem[BBA_IMR] & INT_R) + { + mBbaMem[BBA_IR] |= INT_R; + + exi_status.interrupt |= exi_status.TRANSFER; + } + else + { + ERROR_LOG(SP1, "NOT raising recv interrupt"); + } + +wait_for_next: + if (mBbaMem[BBA_NCRA] & NCRA_SR) + RecvStart(); + +#undef PTR_FROM_PAGE_PTR + return true; } //#pragma optimize("",on) diff --git a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h index 3fa3a42d34..5489131b08 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h +++ b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h @@ -20,107 +20,16 @@ #include "Thread.h" -inline u8 makemaskb(int start, int end) { - return (u8)_rotl((2 << (end - start)) - 1, 7 - end); -} -inline u32 makemaskh(int start, int end) { - return (u32)_rotl((2 << (end - start)) - 1, 15 - end); -} -inline u32 makemaskw(int start, int end) { - return _rotl((2 << (end - start)) - 1, 31 - end); -} -inline u8 getbitsb(u8 byte, int start, int end) { - return (byte & makemaskb(start, end)) >> u8(7 - end); -} -inline u32 getbitsh(u32 hword, int start, int end) { - return (hword & makemaskh(start, end)) >> u32(15 - end); -} -inline u32 getbitsw(u32 dword, int start, int end) { - return (dword & makemaskw(start, end)) >> (31 - end); -} - -class WriteBuffer -{ -public: - WriteBuffer(u32 s) : _size(0) - { - _buffer = (u8*)malloc(s*sizeof(u8)); - ucapacity = s; - } - ~WriteBuffer() { free(_buffer); } - u32 size() const { return _size; } - u32 capacity() const { return ucapacity; } - void write(u32 s, const void *src) - { - if (_size + s >= ucapacity) - { - INFO_LOG(SP1, "Write too large!"); - exit(0); - } - - memcpy(_buffer + _size, src, s); - _size = _size + s; - } - void clear() { _size = 0; } - u8* p() { return _buffer; } - -private: - u8* _buffer; - u32 ucapacity; - u32 _size; -}; - -// Doesn't contain error checks for wraparound writes -class CyclicBufferWriter -{ -public: - CyclicBufferWriter(u8 *buffer, size_t cap) { - _buffer = buffer; _capacity = cap; _write = 0; - } - - size_t p_write() const { return _write; } - void reset() { _write = 0; } - - void write(void *src, size_t size) { - _dbg_assert_(SP1, size < _capacity); - u8* bsrc = (u8*) src; - if (_write + size >= _capacity) - { - // wraparound - memcpy(_buffer + _write, src, _capacity - _write); - memcpy(_buffer, bsrc + (_capacity - _write), size - (_capacity - _write)); - _write = size - (_capacity - _write); - } else { - memcpy(_buffer + _write, src, size); - _write += size; - } - //DEBUG_LOG(SP1, "CBWrote %i bytes", size); - } - // Aligns the write pointer to steps of 0x100, like the real BBA - void align() { - _write = (_write + 0xff) & ~0xff; - if(_write >= _capacity) - _write -= _capacity; - } - -private: - size_t _write; - size_t _capacity; - u8 *_buffer; -}; - -#define INVALID_P 0xFFFF - -// Network Control Register A, RW +// Network Control Register A enum NCRA { NCRA_RESET = 0x01, // RESET NCRA_ST0 = 0x02, // Start transmit command/status NCRA_ST1 = 0x04, // " - NCRA_SR = 0x08, // Start Receive + NCRA_SR = 0x08 // Start Receive }; -// Network Control Register B, RW +// Network Control Register B enum NCRB { NCRB_PR = 0x01, // Promiscuous Mode @@ -129,11 +38,11 @@ enum NCRB NCRB_PB = 0x08, // Pass Bad Frame NCRB_AB = 0x10, // Accept Broadcast NCRB_HBD = 0x20, // reserved - NCRB_RXINTC = 0xC0, // Receive Interrupt Counter (mask) + NCRB_RXINTC = 0xC0 // Receive Interrupt Counter (mask) }; -// Interrupt Mask Register, RW, 00h -// Interrupt Register, RW, 00h +// Interrupt Mask Register +// Interrupt Register enum Interrupts { INT_FRAG = 0x01, // Fragment Counter @@ -143,17 +52,20 @@ enum Interrupts INT_T_ERR = 0x10, // Transmit Error INT_FIFO_ERR = 0x20, // FIFO Error INT_BUS_ERR = 0x40, // BUS Error - INT_RBF = 0x80, // RX Buffer Full + INT_RBF = 0x80 // RX Buffer Full }; -// NWAY Configuration Register, RW, 84h +// NWAY Configuration Register enum NWAYC { NWAYC_FD = 0x01, // Full Duplex Mode - NWAYC_PS100 = 0x02, // Port Select 100/10 - NWAYC_ANE = 0x03, // Autonegotiation Enable - NWAYC_ANS_RA = 0x04, // Restart Autonegotiation - NWAYC_LTE = 0x08, // Link Test Enable + NWAYC_PS100_10 = 0x02, // Port Select 100/10 + NWAYC_ANE = 0x04, // Autonegotiate enable + + // Autonegotiation status bits... + + NWAYC_NTTEST = 0x40, // Reserved + NWAYC_LTE = 0x80 // Link Test Enable }; enum NWAYS @@ -168,6 +80,18 @@ enum NWAYS NWAYS_10TXH = 0x80 }; +enum MISC1 +{ + MISC1_BURSTDMA = 0x01, + MISC1_DISLDMA = 0x02, + MISC1_TPF = 0x04, + MISC1_TPH = 0x08, + MISC1_TXF = 0x10, + MISC1_TXH = 0x20, + MISC1_TXFIFORST = 0x40, + MISC1_RXFIFORST = 0x80 +}; + enum { BBA_NCRA = 0x00, @@ -182,6 +106,7 @@ enum BBA_BP = 0x0a, BBA_TLBP = 0x0c, BBA_TWP = 0x0e, + BBA_IOB = 0x10, BBA_TRP = 0x12, BBA_RWP = 0x16, BBA_RRP = 0x18, @@ -195,6 +120,14 @@ enum BBA_NAFR_PAR3 = 0x23, BBA_NAFR_PAR4 = 0x24, BBA_NAFR_PAR5 = 0x25, + BBA_NAFR_MAR0 = 0x26, + BBA_NAFR_MAR1 = 0x27, + BBA_NAFR_MAR2 = 0x28, + BBA_NAFR_MAR3 = 0x29, + BBA_NAFR_MAR4 = 0x2a, + BBA_NAFR_MAR5 = 0x2b, + BBA_NAFR_MAR6 = 0x2c, + BBA_NAFR_MAR7 = 0x2d, BBA_NWAYC = 0x30, BBA_NWAYS = 0x31, @@ -210,37 +143,48 @@ enum BBA_SI_ACTRL = 0x5c, BBA_SI_STATUS = 0x5d, - BBA_SI_ACTRL2 = 0x60, + BBA_SI_ACTRL2 = 0x60 }; enum { BBA_RECV_SIZE = 0x800, - BBA_MEM_SIZE = 0x1000, - - CB_OFFSET = 0x100, - CB_SIZE = (BBA_MEM_SIZE - CB_OFFSET), - SIZEOF_ETH_HEADER = 0xe, - SIZEOF_RECV_DESCRIPTOR = 4, - - EXI_DEVTYPE_ETHER = 0x04020200, + BBA_MEM_SIZE = 0x1000 }; -enum +enum { EXI_DEVTYPE_ETHER = 0x04020200 }; + +enum SendStatus { - EXPECT_NONE = 0, - EXPECT_ID + 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 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 }; class CEXIETHERNET : public IEXIDevice { public: CEXIETHERNET(const std::string& mac_addr); - ~CEXIETHERNET(); - void SetMAC(u8 *new_addr); - void SetCS(int _iCS); + virtual ~CEXIETHERNET(); + void SetCS(int cs); bool IsPresent(); - void Update(); bool IsInterruptSet(); void ImmWrite(u32 data, u32 size); u32 ImmRead(u32 size); @@ -249,52 +193,116 @@ public: void DoState(PointerWrap &p); //private: - // STATE_TO_SAVE - u32 m_uPosition; - u32 m_uCommand; + struct + { + enum + { + READ, + WRITE + } direction; - bool m_bInterruptSet; - u16 mWriteP, mReadP; + enum + { + EXI, + MX + } region; - bool mExpectSpecialImmRead; //reset to false on deselect - u32 mSpecialImmData; - bool Activated; + u16 address; + bool valid; + } transfer; - u16 mRBRPP; //RRP - Receive Buffer Read Page Pointer - bool mRBEmpty; - - u8 mBbaMem[BBA_MEM_SIZE]; - - WriteBuffer mWriteBuffer; - CyclicBufferWriter mCbw; - - bool mExpectVariableLengthImmWrite; - bool mReadyToSend; - u8 RegisterBlock[0x1000]; enum { - CMD_ID = 0x00, - CMD_READ_REG = 0x01, + EXI_ID, + REVISION_ID, + INTERRUPT_MASK, + INTERRUPT, + DEVICE_ID, + ACSTART, + HASH_READ = 8, + HASH_WRITE, + HASH_STATUS = 0xb, + RESET = 0xf }; - void recordSendComplete(); - bool sendPacket(u8 *etherpckt, int size); - bool checkRecvBuffer(); - bool handleRecvdPacket(); + // exi regs + struct EXIStatus + { + enum + { + TRANSFER = 0x80 + }; - //TAP interface - bool activate(); - bool CheckRecieved(); - bool deactivate(); - bool isActivated(); - bool resume(); - bool startRecv(); - bool cbwriteDescriptor(u32 size); + 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; + } + + } exi_status; + + struct Descriptor + { + u32 word; + + 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; + } + + u8 get_status() const + { + return (word >> 24) & 0xff; + } + }; + + bool IsMXCommand(u32 const data); + bool IsWriteCommand(u32 const data); + char const * const GetRegisterName() const; + void MXHardReset(); + void MXCommandHandler(u32 data, u32 size); + void DirectFIFOWrite(u8 *data, u32 size); + void SendFromDirectFIFO(); + void SendFromPacketBuffer(); + void SendComplete(); + u8 HashIndex(u8 *dest_eth_addr); + bool RecvMACFilter(); + void inc_rwp(); + bool RecvHandlePacket(); + + u8 *tx_fifo; + u8 *mBbaMem; + + // TAP interface + bool Activate(); + void Deactivate(); + bool IsActivated(); + bool SendFrame(u8 *frame, u32 size); + bool RecvInit(); + bool RecvStart(); + void RecvStop(); + + u8 *mRecvBuffer; - volatile bool mWaiting; - u8 mac_address[6]; - u8 mRecvBuffer[BBA_RECV_SIZE]; #ifdef _WIN32 HANDLE mHAdapter, mHRecvEvent, mHReadWait; DWORD mMtu; From 9a3dd778cb2074564333a05b18894e0aa6945c93 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Wed, 4 Jul 2012 04:34:40 +0200 Subject: [PATCH 02/10] Preliminary BBA support for Linux. Assumes that a TAP interface named 'Dolphin' exists and is preconfigured. Contains some dirty hacks. --- Source/Core/Core/Src/HW/BBA-TAP/TAP_Unix.cpp | 370 ++++++------------ .../Core/Core/Src/HW/EXI_DeviceEthernet.cpp | 17 +- Source/Core/Core/Src/HW/EXI_DeviceEthernet.h | 11 +- 3 files changed, 134 insertions(+), 264 deletions(-) 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 From 9cff8316d2866895e5cffbdf302f26c36a93f620 Mon Sep 17 00:00:00 2001 From: sktsqrl Date: Wed, 4 Jul 2012 13:44:37 -0700 Subject: [PATCH 03/10] code cleanup for win32/tap/eth --- Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp | 54 ++--- .../Core/Core/Src/HW/EXI_DeviceEthernet.cpp | 198 +++++++++--------- Source/Core/Core/Src/HW/EXI_DeviceEthernet.h | 12 +- 3 files changed, 126 insertions(+), 138 deletions(-) diff --git a/Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp b/Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp index d56cc9363e..e5cd78eb0e 100644 --- a/Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp +++ b/Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp @@ -192,28 +192,26 @@ bool CEXIETHERNET::Activate() if (IsActivated()) return true; - INFO_LOG(SP1, "Activating BBA..."); - DWORD len; std::vector 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; } @@ -240,14 +238,11 @@ bool CEXIETHERNET::Activate() if (!DeviceIoControl(mHAdapter, TAP_IOCTL_SET_MEDIA_STATUS, &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; } - //RecvInit(); - - INFO_LOG(SP1, "Success!"); return true; } @@ -270,50 +265,27 @@ bool CEXIETHERNET::IsActivated() return mHAdapter != INVALID_HANDLE_VALUE; } -// TODO check if still needed -// Required for lwip...not sure why -void fixup_ip_checksum(u16 *dataptr, u16 len) -{ - 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::SendFrame(u8 *frame, u32 size) { - //fixup_ip_checksum((u16*)frame + 7, 20); - - DEBUG_LOG(SP1, "SendFrame %x\n%s", size, ArrayToString(frame, size, 0x10).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, frame, size, &numBytesWrit, &overlap)) { - // Fail Boat DWORD res = GetLastError(); - WARN_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) { - WARN_LOG(SP1, "BBA SendFrame %i only got %i bytes sent!", size, numBytesWrit); - return false; + ERROR_LOG(SP1, "BBA SendFrame %i only got %i bytes sent!", size, numBytesWrit); } + // Always report the packet as being sent successfully, even though it might be a lie SendComplete(); return true; @@ -324,7 +296,7 @@ VOID CALLBACK CEXIETHERNET::ReadWaitCallback(PVOID lpParameter, BOOLEAN TimerFir CEXIETHERNET* self = (CEXIETHERNET*)lpParameter; GetOverlappedResult(self->mHAdapter, &self->mReadOverlapped, - &self->mRecvBufferLength, false); + (LPDWORD)&self->mRecvBufferLength, false); self->RecvHandlePacket(); } @@ -335,7 +307,7 @@ bool CEXIETHERNET::RecvInit() if ((mHRecvEvent = CreateEvent(NULL, false, false, NULL)) == NULL) { - INFO_LOG(SP1, "Failed to create recv event:%x", GetLastError()); + ERROR_LOG(SP1, "Failed to create recv event:%x", GetLastError()); return false; } @@ -358,7 +330,7 @@ bool CEXIETHERNET::RecvStart() RecvInit(); DWORD res = ReadFile(mHAdapter, mRecvBuffer, BBA_RECV_SIZE, - &mRecvBufferLength, &mReadOverlapped); + (LPDWORD)&mRecvBufferLength, &mReadOverlapped); if (!res && (GetLastError() != ERROR_IO_PENDING)) { diff --git a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp index c04aab219e..66ecc26bc4 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp +++ b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp @@ -16,16 +16,21 @@ // http://code.google.com/p/dolphin-emu/ #include "Memmap.h" -//#pragma optimize("",off) #include "EXI_Device.h" #include "EXI_DeviceEthernet.h" #include "StringUtil.h" +// 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) { tx_fifo = new u8[1518]; mBbaMem = new u8[BBA_MEM_SIZE]; + mRecvBuffer = new u8 [BBA_RECV_SIZE]; + mRecvBufferLength = 0; MXHardReset(); @@ -34,7 +39,7 @@ CEXIETHERNET::CEXIETHERNET(const std::string& mac_addr) int x = 0; u8 new_addr[6] = { 0 }; - for (int i = 0; i < (int)mac_addr.size() && x < 12; i++) + for (size_t i = 0; i < mac_addr.size() && x < 12; i++) { char c = mac_addr.at(i); if (c >= '0' && c <= '9') { @@ -61,8 +66,6 @@ CEXIETHERNET::CEXIETHERNET(const std::string& mac_addr) #elif defined(__linux__) fd = -1; #endif - - mRecvBufferLength = 0; } CEXIETHERNET::~CEXIETHERNET() @@ -76,8 +79,6 @@ CEXIETHERNET::~CEXIETHERNET() void CEXIETHERNET::SetCS(int cs) { - DEBUG_LOG(SP1, "chip select: %s", cs ? "true" : "false"); - if (cs) { // Invalidate the previous transfer @@ -109,14 +110,17 @@ void CEXIETHERNET::ImmWrite(u32 data, u32 size) transfer.address = (data >> 8) & 0xffff; transfer.direction = IsWriteCommand(data) ? transfer.WRITE : transfer.READ; - WARN_LOG(SP1, "%s %s %s %x", + 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) + { + 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; @@ -124,7 +128,7 @@ void CEXIETHERNET::ImmWrite(u32 data, u32 size) // Reach here if we're actually writing data to the EXI or MX region. - WARN_LOG(SP1, "%s write %0*x", + DEBUG_LOG(SP1, "%s write %0*x", transfer.region == transfer.MX ? "mx " : "exi", size * 2, data); if (transfer.region == transfer.EXI) @@ -178,7 +182,7 @@ u32 CEXIETHERNET::ImmRead(u32 size) ret |= mBbaMem[transfer.address++] << (i * 8); } - WARN_LOG(SP1, "imm r%i: %0*x", size, size * 2, ret); + DEBUG_LOG(SP1, "imm r%i: %0*x", size, size * 2, ret); ret <<= (4 - size) * 8; @@ -187,7 +191,7 @@ u32 CEXIETHERNET::ImmRead(u32 size) void CEXIETHERNET::DMAWrite(u32 addr, u32 size) { - WARN_LOG(SP1, "dma w: %08x %x", addr, size); + DEBUG_LOG(SP1, "dma w: %08x %x", addr, size); if (transfer.region == transfer.MX && transfer.direction == transfer.WRITE && @@ -205,7 +209,7 @@ void CEXIETHERNET::DMAWrite(u32 addr, u32 size) void CEXIETHERNET::DMARead(u32 addr, u32 size) { - ERROR_LOG(SP1, "dma r: %08x %x", addr, size); + DEBUG_LOG(SP1, "dma r: %08x %x", addr, size); memcpy(Memory::GetPointer(addr), &mBbaMem[transfer.address], size); @@ -216,6 +220,7 @@ void CEXIETHERNET::DoState(PointerWrap &p) { p.Do(mBbaMem); // TODO ... the rest... + ERROR_LOG(SP1, "CEXIETHERNET::DoState not implemented!"); } bool CEXIETHERNET::IsMXCommand(u32 const data) @@ -236,16 +241,16 @@ char const * const CEXIETHERNET::GetRegisterName() const { 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) + 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"; } } @@ -253,45 +258,45 @@ char const * const CEXIETHERNET::GetRegisterName() const { 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) + 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) @@ -320,14 +325,14 @@ void CEXIETHERNET::MXCommandHandler(u32 data, u32 size) case BBA_NCRA: if (data & NCRA_RESET) { - WARN_LOG(SP1, "software reset"); + DEBUG_LOG(SP1, "software reset"); //MXSoftReset(); Activate(); } if ((mBbaMem[BBA_NCRA] & NCRA_SR) ^ (data & NCRA_SR)) { - WARN_LOG(SP1, "%s rx", (data & NCRA_SR) ? "start" : "stop"); + DEBUG_LOG(SP1, "%s rx", (data & NCRA_SR) ? "start" : "stop"); if (data & NCRA_SR) RecvStart(); @@ -338,7 +343,7 @@ void CEXIETHERNET::MXCommandHandler(u32 data, u32 size) // Only start transfer if there isn't one currently running if (!(mBbaMem[BBA_NCRA] & (NCRA_ST0 | NCRA_ST1))) { - // TODO Might have to check TXDMA status as well? + // Technically transfer dma status is kept in TXDMA - not implemented if (data & NCRA_ST0) { @@ -347,7 +352,7 @@ void CEXIETHERNET::MXCommandHandler(u32 data, u32 size) } else if (data & NCRA_ST1) { - WARN_LOG(SP1, "start tx - direct fifo"); + 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 @@ -371,12 +376,11 @@ void CEXIETHERNET::MXCommandHandler(u32 data, u32 size) write_to_register: default: + for (int i = size - 1; i >= 0; i--) { - for (int i = size - 1; i >= 0; i--) - { - mBbaMem[transfer.address++] = (data >> (i * 8)) & 0xff; - } + mBbaMem[transfer.address++] = (data >> (i * 8)) & 0xff; } + return; } } @@ -390,6 +394,9 @@ void CEXIETHERNET::DirectFIFOWrite(u8 *data, u32 size) 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; } @@ -418,7 +425,7 @@ void CEXIETHERNET::SendComplete() mBbaMem[BBA_LTPS] = 0; } -u8 CEXIETHERNET::HashIndex(u8 *dest_eth_addr) +inline u8 CEXIETHERNET::HashIndex(u8 *dest_eth_addr) { // Calculate crc u32 crc = 0xffffffff; @@ -440,7 +447,7 @@ u8 CEXIETHERNET::HashIndex(u8 *dest_eth_addr) return crc >> 26; } -bool CEXIETHERNET::RecvMACFilter() +inline bool CEXIETHERNET::RecvMACFilter() { static u8 const broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; @@ -471,19 +478,19 @@ bool CEXIETHERNET::RecvMACFilter() } } -#define PAGE_PTR(x) ((mBbaMem[x + 1] << 8) | mBbaMem[x]) - void CEXIETHERNET::inc_rwp() { u16 *rwp = (u16 *)&mBbaMem[BBA_RWP]; - if (*rwp + 1 == PAGE_PTR(BBA_RHBP)) + if (*rwp + 1 == page_ptr(BBA_RHBP)) // TODO check if BP is used as well - *rwp = PAGE_PTR(BBA_BP); + *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; @@ -493,24 +500,23 @@ bool CEXIETHERNET::RecvHandlePacket() if (!RecvMACFilter()) goto wait_for_next; - + +#ifdef BBA_TACK_PAGE_PTRS WARN_LOG(SP1, "RecvHandlePacket %x\n%s", mRecvBufferLength, ArrayToString(mRecvBuffer, mRecvBufferLength, 0x100).c_str()); -#define PTR_FROM_PAGE_PTR(x) &mBbaMem[PAGE_PTR(x) << 8] + WARN_LOG(SP1, "%x %x %x %x", + page_ptr(BBA_BP), + page_ptr(BBA_RRP), + page_ptr(BBA_RWP), + page_ptr(BBA_RHBP)); +#endif - ERROR_LOG(SP1, "%x %x %x %x", - PAGE_PTR(BBA_BP), - PAGE_PTR(BBA_RRP), - PAGE_PTR(BBA_RWP), - PAGE_PTR(BBA_RHBP)); - - write_ptr = PTR_FROM_PAGE_PTR(BBA_RWP); - end_ptr = PTR_FROM_PAGE_PTR(BBA_RHBP); - read_ptr = PTR_FROM_PAGE_PTR(BBA_RRP); + write_ptr = 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; - //u8 *descriptor = write_ptr; write_ptr += 4; for (u32 i = 0, off = 4; i < mRecvBufferLength; ++i, ++off) @@ -525,7 +531,7 @@ bool CEXIETHERNET::RecvHandlePacket() if (write_ptr == end_ptr) // TODO check if BP is used as well - write_ptr = PTR_FROM_PAGE_PTR(BBA_BP); + write_ptr = ptr_from_page_ptr(BBA_BP); if (write_ptr == read_ptr) { @@ -537,11 +543,13 @@ bool CEXIETHERNET::RecvHandlePacket() if ((mRecvBufferLength + 4) % 256) inc_rwp(); - ERROR_LOG(SP1, "%x %x %x %x", - PAGE_PTR(BBA_BP), - PAGE_PTR(BBA_RRP), - PAGE_PTR(BBA_RWP), - PAGE_PTR(BBA_RHBP)); +#ifdef BBA_TACK_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 // Update descriptor descriptor->set(*(u16 *)&mBbaMem[BBA_RWP], 4 + mRecvBufferLength, 0); @@ -557,15 +565,13 @@ bool CEXIETHERNET::RecvHandlePacket() } else { - ERROR_LOG(SP1, "NOT raising recv interrupt"); + // 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(); -#undef PTR_FROM_PAGE_PTR return true; } - -//#pragma optimize("",on) diff --git a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h index adee2d39d5..a7b2260d19 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h +++ b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h @@ -275,6 +275,16 @@ public: } }; + 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; @@ -302,6 +312,7 @@ public: void RecvStop(); u8 *mRecvBuffer; + u32 mRecvBufferLength; #if defined(_WIN32) HANDLE mHAdapter, mHRecvEvent, mHReadWait; @@ -314,7 +325,6 @@ public: volatile bool readEnabled; #endif - u32 mRecvBufferLength; }; #endif From 6cccbb91ec06a47a6369d6cce41f5d1cd1e07983 Mon Sep 17 00:00:00 2001 From: sktsqrl Date: Wed, 4 Jul 2012 16:00:42 -0700 Subject: [PATCH 04/10] bba: implement recv buffer full interrupt (windows) hopefully fix crashes from closing dolphin while recving --- Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp | 9 ++-- .../Core/Core/Src/HW/EXI_DeviceEthernet.cpp | 42 +++++++++++++++---- Source/Core/Core/Src/HW/EXI_DeviceEthernet.h | 42 ++++++++++++------- 3 files changed, 64 insertions(+), 29 deletions(-) diff --git a/Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp b/Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp index e5cd78eb0e..15d2e1f6bb 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" @@ -314,7 +313,7 @@ bool CEXIETHERNET::RecvInit() ZeroMemory(&mReadOverlapped, sizeof(mReadOverlapped)); RegisterWaitForSingleObject(&mHReadWait, mHRecvEvent, ReadWaitCallback, - this, INFINITE, WT_EXECUTEDEFAULT);//WT_EXECUTEINWAITTHREAD + this, INFINITE, WT_EXECUTEDEFAULT); mReadOverlapped.hEvent = mHRecvEvent; @@ -340,7 +339,7 @@ bool CEXIETHERNET::RecvStart() if (res) { - ERROR_LOG(SP1, "RECV COMPLETED IMMEDIATELY"); + // Completed immediately RecvHandlePacket(); } @@ -352,7 +351,5 @@ void CEXIETHERNET::RecvStop() if (!IsActivated()) return; - CancelIo(mHAdapter); + UnregisterWaitEx(mHReadWait, INVALID_HANDLE_VALUE); } - -//#pragma optimize("",on) diff --git a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp index 66ecc26bc4..40fc4f12c0 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp +++ b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp @@ -478,12 +478,11 @@ inline bool CEXIETHERNET::RecvMACFilter() } } -void CEXIETHERNET::inc_rwp() +inline void CEXIETHERNET::inc_rwp() { u16 *rwp = (u16 *)&mBbaMem[BBA_RWP]; if (*rwp + 1 == page_ptr(BBA_RHBP)) - // TODO check if BP is used as well *rwp = page_ptr(BBA_BP); else (*rwp)++; @@ -497,6 +496,8 @@ bool CEXIETHERNET::RecvHandlePacket() u8 *end_ptr; u8 *read_ptr; Descriptor *descriptor; + u32 status = 0; + u16 rwp_initial = page_ptr(BBA_RWP); if (!RecvMACFilter()) goto wait_for_next; @@ -530,12 +531,24 @@ bool CEXIETHERNET::RecvHandlePacket() } if (write_ptr == end_ptr) - // TODO check if BP is used as well write_ptr = ptr_from_page_ptr(BBA_BP); if (write_ptr == read_ptr) { - ERROR_LOG(SP1, "recv buffer full error - not implemented"); + /* + 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; } } @@ -551,10 +564,25 @@ bool CEXIETHERNET::RecvHandlePacket() page_ptr(BBA_RHBP)); #endif - // Update descriptor - descriptor->set(*(u16 *)&mBbaMem[BBA_RWP], 4 + mRecvBufferLength, 0); + // Is the current frame multicast? + if (mRecvBuffer[0] & 0x01) + status |= DESC_MF; - mBbaMem[BBA_LRPS] = descriptor->get_status(); + 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) diff --git a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h index a7b2260d19..453e8d5843 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h +++ b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h @@ -82,14 +82,26 @@ enum NWAYS 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 + 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 @@ -148,8 +160,9 @@ enum 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 }; enum { EXI_DEVTYPE_ETHER = 0x04020200 }; @@ -178,6 +191,8 @@ enum RecvStatus DESC_RERR = 0x80 }; +#define BBA_RECV_SIZE 0x800 + class CEXIETHERNET : public IEXIDevice { public: @@ -261,18 +276,13 @@ public: { u32 word; - void set(u32 const next_page, u32 const packet_length, u32 const status) + 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; } - - u8 get_status() const - { - return (word >> 24) & 0xff; - } }; inline u16 page_ptr(int const index) const From bb84043de0207db384f0a9074ca8ef50062506f5 Mon Sep 17 00:00:00 2001 From: sktsqrl Date: Wed, 4 Jul 2012 16:17:47 -0700 Subject: [PATCH 05/10] bba: (windows) fix games which stop and restart recving, such as logging off of PSO and loggin on again without stopping dolphin --- Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp b/Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp index 15d2e1f6bb..87ebd90344 100644 --- a/Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp +++ b/Source/Core/Core/Src/HW/BBA-TAP/TAP_Win32.cpp @@ -252,10 +252,7 @@ void CEXIETHERNET::Deactivate() RecvStop(); - CloseHandle(mHRecvEvent); CloseHandle(mHAdapter); - - mHRecvEvent = INVALID_HANDLE_VALUE; mHAdapter = INVALID_HANDLE_VALUE; } @@ -352,4 +349,7 @@ void CEXIETHERNET::RecvStop() return; UnregisterWaitEx(mHReadWait, INVALID_HANDLE_VALUE); + + CloseHandle(mHRecvEvent); + mHRecvEvent = INVALID_HANDLE_VALUE; } From 5db70f452be044cd5cf2543f758dd7cfbad622ef Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Thu, 5 Jul 2012 01:29:14 +0200 Subject: [PATCH 06/10] Remove the now unneeded recv sleep hack and fixes game exit by implementing a timeout on read(2) calls in the read thread. --- Source/Core/Core/Src/HW/BBA-TAP/TAP_Unix.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) 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 fed72e8712..78c9822d3e 100644 --- a/Source/Core/Core/Src/HW/BBA-TAP/TAP_Unix.cpp +++ b/Source/Core/Core/Src/HW/BBA-TAP/TAP_Unix.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #endif @@ -78,8 +79,6 @@ void CEXIETHERNET::Deactivate() 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(); @@ -127,6 +126,16 @@ void ReadThreadHandler(CEXIETHERNET* self) if (self->fd < 0) return; + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(self->fd, &rfds); + + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 50000; + if (select(self->fd + 1, &rfds, NULL, NULL, &timeout) <= 0) + continue; + int readBytes = read(self->fd, self->mRecvBuffer, BBA_RECV_SIZE); if (readBytes < 0) { @@ -134,9 +143,6 @@ void ReadThreadHandler(CEXIETHERNET* self) } 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(); From dd48b246b26ae400aa07ad89112637bda403bc0d Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Thu, 5 Jul 2012 01:30:37 +0200 Subject: [PATCH 07/10] Reduce logging verbosity in the Linux BBA code --- Source/Core/Core/Src/HW/BBA-TAP/TAP_Unix.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 78c9822d3e..97c73eeb40 100644 --- a/Source/Core/Core/Src/HW/BBA-TAP/TAP_Unix.cpp +++ b/Source/Core/Core/Src/HW/BBA-TAP/TAP_Unix.cpp @@ -99,7 +99,7 @@ bool CEXIETHERNET::IsActivated() bool CEXIETHERNET::SendFrame(u8* frame, u32 size) { #ifdef __linux__ - WARN_LOG(SP1, "SendFrame %x\n%s", size, ArrayToString(frame, size, 0x10).c_str()); + INFO_LOG(SP1, "SendFrame %x\n%s", size, ArrayToString(frame, size, 0x10).c_str()); int writtenBytes = write(fd, frame, size); if ((u32)writtenBytes != size) @@ -143,7 +143,7 @@ void ReadThreadHandler(CEXIETHERNET* self) } else if (self->readEnabled) { - WARN_LOG(SP1, "Read data: %s", ArrayToString(self->mRecvBuffer, readBytes, 0x10).c_str()); + INFO_LOG(SP1, "Read data: %s", ArrayToString(self->mRecvBuffer, readBytes, 0x10).c_str()); self->mRecvBufferLength = readBytes; self->RecvHandlePacket(); } From a02cbedb0f0bcb1a183e59ef90306fc079309f50 Mon Sep 17 00:00:00 2001 From: sktsqrl Date: Sat, 7 Jul 2012 13:44:26 -0700 Subject: [PATCH 08/10] bba: generate MAC if needed --- Source/Core/Core/Src/HW/EXI_Device.cpp | 2 +- .../Core/Core/Src/HW/EXI_DeviceEthernet.cpp | 97 +++++++++++++++---- Source/Core/Core/Src/HW/EXI_DeviceEthernet.h | 2 +- 3 files changed, 79 insertions(+), 22 deletions(-) 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 40fc4f12c0..921473f7df 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp +++ b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp @@ -19,12 +19,51 @@ #include "EXI_Device.h" #include "EXI_DeviceEthernet.h" #include "StringUtil.h" +#include "../ConfigManager.h" // 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) + +// TODO move this code into Common or something, for IOS to use + +enum MACConsumer +{ + BBA, + IOS +}; + +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] = + { + rand() & 0xff, + rand() & 0xff, + rand() & 0xff + }; + + memcpy(&mac[3], id, 3); +} + +CEXIETHERNET::CEXIETHERNET() { tx_fifo = new u8[1518]; mBbaMem = new u8[BBA_MEM_SIZE]; @@ -33,28 +72,46 @@ CEXIETHERNET::CEXIETHERNET(const std::string& mac_addr) mRecvBufferLength = 0; MXHardReset(); - - const u8 mac_address_default[6] = - { 0x00, 0x09, 0xbf, 0x01, 0x00, 0xc1 }; - - int x = 0; - u8 new_addr[6] = { 0 }; - for (size_t i = 0; i < mac_addr.size() && x < 12; i++) + + // 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()) { - 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++; + 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(&mBbaMem[BBA_NAFR_PAR0], new_addr, 6); - else - memcpy(&mBbaMem[BBA_NAFR_PAR0], mac_address_default, 6); + + if (!mac_addr_valid) + { + GenerateMAC(BBA, mac_addr); + + mac_addr_setting = ArrayToString(mac_addr, 6, 10, false); + SConfig::GetInstance().SaveSettings(); + + 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; diff --git a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h index 453e8d5843..6a3a552f38 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h +++ b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.h @@ -196,7 +196,7 @@ enum RecvStatus class CEXIETHERNET : public IEXIDevice { public: - CEXIETHERNET(const std::string& mac_addr); + CEXIETHERNET(); virtual ~CEXIETHERNET(); void SetCS(int cs); bool IsPresent(); From 9442d334d83d2fd2deb7d7b01b41738261d86771 Mon Sep 17 00:00:00 2001 From: sktsqrl Date: Sat, 21 Jul 2012 12:21:55 -0700 Subject: [PATCH 09/10] bba: stub the os x impl --- Source/Core/Core/Src/HW/BBA-TAP/TAP_Apple.cpp | 39 +++++++++++-------- .../Core/Core/Src/HW/EXI_DeviceEthernet.cpp | 6 +-- 2 files changed, 25 insertions(+), 20 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() { + +bool CEXIETHERNET::Activate() +{ return false; } -bool CEXIETHERNET::activate() { + +void CEXIETHERNET::Deactivate() +{ +} + +bool CEXIETHERNET::IsActivated() +{ return false; } -bool CEXIETHERNET::CheckRecieved() { + +bool CEXIETHERNET::SendFrame(u8 *, u32) +{ return false; } -bool CEXIETHERNET::resume() { + +bool CEXIETHERNET::RecvInit() +{ return false; } -bool CEXIETHERNET::startRecv() { + +bool CEXIETHERNET::RecvStart() +{ return false; } -bool CEXIETHERNET::sendPacket(u8 *etherpckt, int size) { - return false; -} -bool CEXIETHERNET::handleRecvdPacket() { - return false; -} -bool CEXIETHERNET::cbwriteDescriptor(u32 size) { - return false; + +void CEXIETHERNET::RecvStop() +{ } diff --git a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp index 921473f7df..5419f67b1b 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp +++ b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp @@ -55,9 +55,9 @@ void GenerateMAC(MACConsumer type, u8 (&mac)[6]) u8 id[3] = { - rand() & 0xff, - rand() & 0xff, - rand() & 0xff + (u8)rand(), + (u8)rand(), + (u8)rand() }; memcpy(&mac[3], id, 3); From 601b9fc03b2a23c711ad6ade57acb8d58f0383f1 Mon Sep 17 00:00:00 2001 From: sktsqrl Date: Sat, 21 Jul 2012 12:50:40 -0700 Subject: [PATCH 10/10] typo fix --- Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp index 5419f67b1b..4197d01350 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp +++ b/Source/Core/Core/Src/HW/EXI_DeviceEthernet.cpp @@ -559,7 +559,7 @@ bool CEXIETHERNET::RecvHandlePacket() if (!RecvMACFilter()) goto wait_for_next; -#ifdef BBA_TACK_PAGE_PTRS +#ifdef BBA_TRACK_PAGE_PTRS WARN_LOG(SP1, "RecvHandlePacket %x\n%s", mRecvBufferLength, ArrayToString(mRecvBuffer, mRecvBufferLength, 0x100).c_str()); @@ -613,7 +613,7 @@ bool CEXIETHERNET::RecvHandlePacket() if ((mRecvBufferLength + 4) % 256) inc_rwp(); -#ifdef BBA_TACK_PAGE_PTRS +#ifdef BBA_TRACK_PAGE_PTRS WARN_LOG(SP1, "%x %x %x %x", page_ptr(BBA_BP), page_ptr(BBA_RRP),