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;