// --------------------------------------------------------------------------------------- // nicx.cpp // // Copyright (C) Microsoft Corporation // --------------------------------------------------------------------------------------- #include "xnp.h" #include "xnver.h" #ifdef XNET_FEATURE_XBOX // --------------------------------------------------------------------------------------- // Globals // --------------------------------------------------------------------------------------- KINTERRUPT CXnNic::s_InterruptObject; // --------------------------------------------------------------------------------------- // Definitions // --------------------------------------------------------------------------------------- #define HWADDR0(_hwaddr) ((const DWORD *)((BYTE *)(_hwaddr)))[0] #define HWADDR1(_hwaddr) ((const WORD *)((BYTE *)(_hwaddr)))[2] DefineTag(nicStats, TAG_ENABLE); // Trace Nic stats DefineTag(nicStatsAll, 0); // Trace all Nic stats // --------------------------------------------------------------------------------------- // Internal // --------------------------------------------------------------------------------------- void CXnNic::NicStartXmitRecv() { ICHECK(NIC, USER|UDPC|SDPC); PNICCSR pCsr = PNicCsr(); pCsr->rx_poll = RXPOLL_EN | _dwRxPollFreq; pCsr->tx_en = TXEN_ENABLE; pCsr->rx_en = RXEN_ENABLE; pCsr->mode = MODE_TXDM|MODE_RXDM; } void CXnNic::NicStopXmitRecv() { ICHECK(NIC, USER|UDPC|SDPC); PNICCSR pCsr = PNicCsr(); int timeout; // Turn off the transmitter and receiver pCsr->rx_poll = 0; pCsr->rx_en = 0; pCsr->tx_en = 0; // Wait for a max of 5msec until both the transmitter and receiver are idle for (timeout=500; timeout--; ) { if ( !(pCsr->rx_sta & RXSTA_BUSY) && !(pCsr->tx_sta & TXSTA_BUSY)) break; KeStallExecutionProcessor(10); } // Ensure there is no active DMA transfer in progress pCsr->mode = MODE_DISABLE_DMA; for (timeout=500; timeout--; ) { if (pCsr->mode & MODE_DMA_IDLE) break; KeStallExecutionProcessor(10); } pCsr->mode = 0; } BOOLEAN CXnNic::NicIsr(PKINTERRUPT, PVOID pvContext) { return(((CXnNic *)pvContext)->NicProcessIsr()); } BOOLEAN CXnNic::NicProcessIsr() { Assert(!TestInitFlag(INITF_NIC_STOP)); // Disable interrupts PNicCsr()->intr_mk = 0; // Queue the dpc to handle the interrupt KeInsertQueueDpc(&_dpc, NULL, NULL); #ifdef XNET_FEATURE_STATS _NicStats.isrCount += 1; #endif return(TRUE); } void CXnNic::NicDpc(PKDPC dpc, void * pvContext, void * pvArg1, void * pvArg2) { ((CXnNic *)pvContext)->NicProcessDpc(); } void CXnNic::NicProcessDpc() { ICHECK(NIC, SDPC); PNICCSR pCsr = PNicCsr(); STATINC(dpcCount); while (1) { DWORD dwMintr = pCsr->mintr; DWORD dwIntr = pCsr->intr; if (dwIntr == 0) break; // Process the MII interrupt before acknowledging it to prevent the auto-poll // of the PHY interfering with reading of the current link state if (dwIntr & INTR_MINT) { NicMiiInterrupt(dwMintr, FALSE); } // Acknowledge interrupts (MII interrupts first) pCsr->mintr = dwMintr; pCsr->intr = dwIntr; // We always call these two interrupt handlers no matter what the interrupt // bits say. They will do nothing if there isn't anything to receive or transmit. NicRecvInterrupt(); NicXmitInterrupt(); // If a receive frame was missed, tell the Nic to restart RX polling if (dwIntr & INTR_MISS) { STATINC(rxMissedFrames); pCsr->mode = MODE_RXDM; } } // Re-enable interrupts pCsr->intr_mk = INTR_ALL; } void CXnNic::NicMiiInterrupt(DWORD dwMintr, BOOL fInit) { ICHECK(NIC, USER|UDPC|SDPC); PNICCSR pCsr = PNicCsr(); DWORD dwMiics = pCsr->mii_cs; DWORD dwLinkState = PhyGetLinkState(!fInit); if (fInit || (dwLinkState != _dwLinkState)) { TraceSz3(Warning, "+Ethernet link status: %s %dMbps %s-duplex", (dwLinkState & XNET_ETHERNET_LINK_ACTIVE) ? "up" : "down", (dwLinkState & XNET_ETHERNET_LINK_100MBPS) ? 100 : (dwLinkState & XNET_ETHERNET_LINK_10MBPS) ? 10 : 0, (dwLinkState & XNET_ETHERNET_LINK_FULL_DUPLEX) ? "full" : (dwLinkState & XNET_ETHERNET_LINK_HALF_DUPLEX) ? "half" : "?"); // NOTE: When the link was up before, we need to stop both Tx and Rx and then set // Rx polling frequency and Tx duplex mode according to the link status. if (!fInit) { NicStopXmitRecv(); } _dwRxPollFreq = (dwLinkState & XNET_ETHERNET_LINK_10MBPS) ? RXPOLL_FREQ_10MPS : RXPOLL_FREQ_100MPS; if (dwLinkState & XNET_ETHERNET_LINK_FULL_DUPLEX) pCsr->tx_cntl &= ~TXCNTL_HDEN; else pCsr->tx_cntl |= TXCNTL_HDEN; if (!fInit) { NicStartXmitRecv(); } if (fInit && (dwLinkState & XNET_ETHERNET_LINK_ACTIVE)) { SetInitFlag(INITF_CONNECTED_BOOT); } _dwLinkState = dwLinkState; } } void CXnNic::NicRecvInterrupt() { ICHECK(NIC, UDPC|SDPC); CPacket pkt; void * pv; UINT cb; CEnetHdr * pEnetHdr; UINT uiFlags; UINT uiType; RecvDesc * prd; DWORD dwFlagsCount; prd = _prdPtr; while (1) { dwFlagsCount = prd->_dwFlagsCount; if (dwFlagsCount & RXDESC_OWN) break; if ((dwFlagsCount & RXDESC_REND) == 0) { STATINC(rxEndOfFrameErrors); goto nextframe; } if (dwFlagsCount & RXDESC_ERR) { STATINC_(dwFlagsCount & RXDESC_OFOL, rxOverFlowErrors); STATINC_(dwFlagsCount & RXDESC_CRC, rxCrcErrors); STATINC_(dwFlagsCount & RXDESC_LFER, rxLengthErrors); STATINC_(dwFlagsCount & RXDESC_MAX, rxMaxFrameErrors); STATINC_(dwFlagsCount & RXDESC_LCOL, rxLateCollisions); STATINC_(dwFlagsCount & RXDESC_RUNT, rxRunts); STATINC_(dwFlagsCount & RXDESC_FRAM, rxFramingErrors); // Accept an error frame if RXDESC_FRAM is the only error bit turned on. // For all other errors we discard the frame. if ((dwFlagsCount & (RXDESC_OFOL|RXDESC_CRC|RXDESC_LFER|RXDESC_MAX|RXDESC_LCOL|RXDESC_RUNT|RXDESC_FRAM)) != RXDESC_FRAM) goto nextframe; } // Until NicStart is called we drop all incoming frames because the upper // layers of the stack are still initializing. if (!TestInitFlag(INITF_NIC_2)) goto nextframe; pv = VirAddr(prd->_dwPhyAddr); cb = (dwFlagsCount & 0xFFFF); pEnetHdr = (CEnetHdr *)pv; uiFlags = PKTF_TYPE_ENET; uiType = pEnetHdr->_wType; PushPktRecvTags(pEnetHdr->_eaDst.IsBroadcast()); if ((dwFlagsCount & (RXDESC_ERR|RXDESC_FRAM|RXDESC_EXTRA)) == (RXDESC_ERR|RXDESC_FRAM|RXDESC_EXTRA)) { STATINC(rxExtraByteErrors); if (cb > 0) { cb -= 1; } } if (cb < ENET_DATA_MINSIZE) { STATINC(rxMinSizeErrors); goto nextframepop; } STATINC(rxGoodFrames); if (NTOHS((WORD)uiType) <= ENET_DATA_MAXSIZE) { CIeeeHdr * pIeeeHdr = (CIeeeHdr *)pEnetHdr; BYTE abEnetHdr[sizeof(CEnetAddr) * 2]; if (!pIeeeHdr->IsEnetFrame()) { TraceSz(pktRecv, "[DISCARD] IEEE frame type not supported"); goto nextframepop; } // Convert the link header into a standard CEnetHdr uiType = pIeeeHdr->_wTypeIeee; pv = (BYTE *)pv + (sizeof(CIeeeHdr) - sizeof(CEnetHdr)); cb -= (sizeof(CIeeeHdr) - sizeof(CEnetHdr)); memcpy(abEnetHdr, pIeeeHdr, sizeof(abEnetHdr)); memcpy(pv, abEnetHdr, sizeof(abEnetHdr)); pEnetHdr = (CEnetHdr *)pv; pEnetHdr->_wType = (WORD)uiType; } pv = (BYTE *)pv + sizeof(CEnetHdr); cb -= sizeof(CEnetHdr); TraceSz4(pktRecv, "[ENET %s %s %04X][%d]", pEnetHdr->_eaDst.Str(), pEnetHdr->_eaSrc.Str(), NTOHS((WORD)uiType), cb); if (pEnetHdr->_eaDst.IsBroadcast()) { uiFlags |= PKTF_RECV_BROADCAST; } else if (!pEnetHdr->_eaDst.IsEqual(_ea)) { #ifdef XNET_FEATURE_XBDM_SERVER if (!pEnetHdr->_eaDst.IsEqual(_eaClient)) #endif { TraceSz(pktWarn, "[DISCARD] Frame should not have been accepted by Nic hardware"); goto nextframepop; } } #ifdef XNET_FEATURE_XBDM_SERVER if ((uiFlags & PKTF_RECV_BROADCAST) || (pEnetHdr->_eaDst.IsEqual(_eaClient))) { if (_pXbdmClient) { _pXbdmClient->EnetRecv(uiFlags, pv, cb, uiType); } else if ((uiFlags & PKTF_RECV_BROADCAST) == 0) { TraceSz(pktRecv, "[DISCARD] Title stack not attached"); } if ((uiFlags & PKTF_RECV_BROADCAST) == 0) goto nextframepop; } #endif pkt.Init(uiFlags, pv, cb, NULL); EnetRecv(&pkt, uiType); nextframepop: PopPktRecvTags(); nextframe: prd->_dwFlagsCount = RXDESC_OWN | (NIC_FRAME_SIZE - NIC_FRAME_ALIGNMENT - 1); prd = PrdNext(prd); } _prdPtr = prd; } void CXnNic::NicXmitInterrupt() { ICHECK(NIC, UDPC|SDPC); XmitDesc * pxd; CPacket * ppkt; DWORD dwFlagsCount; while (1) { ppkt = _pqXmit.GetHead(); if (ppkt == NULL) break; pxd = _pxdBusy; if (ppkt->TestFlags(PKTF_XMIT_DUALPAGE)) { Assert((pxd->_dwFlagsCount & TXDESC_TEND) == 0); pxd = PxdNext(pxd); } dwFlagsCount = pxd->_dwFlagsCount; Assert(dwFlagsCount & TXDESC_TEND); if (dwFlagsCount & TXDESC_OWN) break; _pqXmit.RemoveHead(); _pxdBusy = PxdNext(pxd); _cxdBusy -= (1 + !!ppkt->TestFlags(PKTF_XMIT_DUALPAGE)); MmLockUnlockBufferPages(ppkt->GetEnetHdr(), sizeof(CEnetHdr) + ppkt->GetCb(), TRUE); if (dwFlagsCount & TXDESC_ERR) { STATINC_(dwFlagsCount & TXDESC_UFLO, txUnderflowErrors); STATINC_(dwFlagsCount & TXDESC_LCOL, txLateCollisions); STATINC_(dwFlagsCount & TXDESC_LCAR, txLostCarriers); STATINC_(dwFlagsCount & TXDESC_DEF, txDefers); STATINC_(dwFlagsCount & TXDESC_EXDEF, txExcessiveDefers); STATINC_(dwFlagsCount & TXDESC_RTRY, txRetryErrors); } else { STATINC(txGoodFrames); } #ifdef XNET_FEATURE_XBDM_SERVER if (ppkt->TestFlags(PKTF_XMIT_XBDMCLIENT)) { CXbdmPacket * ppktXbdm = (CXbdmPacket *)ppkt; if (_pXbdmClient) _pXbdmClient->XmitComplete(ppktXbdm->_pvPkt); else TraceSz(Warning, "XBDM client detached with active packets in the Xmit queue"); _pqClient.InsertTail(ppktXbdm); continue; } #endif ppkt->ClearFlags(PKTF_XMIT_DUALPAGE); ppkt->Complete(this); } if (_fXmitFull && _cxdBusy < _cxdPool) { _fXmitFull = FALSE; #ifdef XNET_FEATURE_XBDM_SERVER if (_pXbdmClient) _pXbdmClient->EnetPush(); #endif EnetPush(); } } BOOL CXnNic::NicXmitReady() { ICHECK(NIC, UDPC|SDPC); #ifdef XNET_FEATURE_XBDM_CLIENT if (_pXbdmServer) { return(_pXbdmServer->XmitReady()); } #endif BOOL fReady = (_cxdBusy < _cxdPool); if (!fReady) { _fXmitFull = TRUE; } return(fReady); } void CXnNic::NicXmit(CPacket * ppkt) { ICHECK(NIC, UDPC|SDPC); #ifdef XNET_FEATURE_XBDM_CLIENT if (_pXbdmServer) { _pXbdmServer->Xmit(ppkt, ppkt->GetEnetHdr(), ppkt->GetCb() + sizeof(CEnetHdr)); } else #endif { Assert(_cxdBusy < _cxdPool); Assert(ppkt->GetCb() <= ENET_DATA_MAXSIZE); void * pv = ppkt->GetEnetHdr(); UINT cb = ppkt->GetCb() + sizeof(CEnetHdr); XmitDesc * pxd = _pxdFree; XmitDesc * pxd0; UINT cb0; TraceSz4(pktXmit, "[ENET dst=%s src=%s %04X][%d]", ((CEnetHdr *)pv)->_eaDst.Str(), ((CEnetHdr *)pv)->_eaSrc.Str(), NTOHS(((CEnetHdr *)pv)->_wType), ppkt->GetCb()); _pqXmit.InsertTail(ppkt); MmLockUnlockBufferPages(pv, cb, FALSE); pxd->_dwPhyAddr = MmGetPhysicalAddress(pv); cb0 = PAGE_SIZE - (pxd->_dwPhyAddr & (PAGE_SIZE - 1)); if (cb <= cb0) { // Entire frame is in a single physical page. Only need one transmit destriptor. // Notice that the length in the _dwFlagsCount is the actual length minus one. // That is an odd requirement of the nVidia hardware. ppkt->ClearFlags(PKTF_XMIT_DUALPAGE); pxd->_dwFlagsCount = TXDESC_OWN | TXDESC_TEND | (cb - 1); } else { // The frame straddles a page boundary. Use two transmit descriptors. We set // up the second descriptor before the first one to prevent the Nic from trying // to transmit the frame before we are done setting up the two descriptors. ppkt->SetFlags(PKTF_XMIT_DUALPAGE); pxd0 = pxd; pxd = PxdNext(pxd0); _cxdBusy += 1; pxd->_dwPhyAddr = MmGetPhysicalAddress((BYTE *)pv + cb0); pxd->_dwFlagsCount = TXDESC_OWN | TXDESC_TEND | (cb - cb0 - 1); pxd0->_dwFlagsCount = TXDESC_OWN | (cb0 - 1); } _pxdFree = PxdNext(pxd); _cxdBusy += 1; if (!TestInitFlag(INITF_NIC_STOP)) { // Tell the Nic to check the transmit ring PNICCSR pCsr = PNicCsr(); pCsr->mode = MODE_TXDM; } } } void CXnNic::NicTimer() { ICHECK(NIC, SDPC); #ifdef XNET_FEATURE_XBDM_CLIENT if (_pXbdmServer) return; #endif // This function gets called every 200ms from the main timer. Occasionally, the // transmitter will stall with frames on the queue. If there are any frames queued, // we set the MODE_TXDM bit on the Nic hardware to tell it to check the queue. if (_cxdBusy > 0) { PNICCSR pCsr = PNicCsr(); pCsr->mode = MODE_TXDM; } // In case we miss a receive interrupt, process any received frames now if ((_prdPtr->_dwFlagsCount & RXDESC_OWN) == 0) { NicRecvInterrupt(); } } NTSTATUS CXnNic::NicInit(XNetInitParams * pxnip) { TCHECK(USER); NTSTATUS status = BaseInit(pxnip); if (!NT_SUCCESS(status)) return(status); #ifdef XNET_FEATURE_XBDM_CLIENT _pXbdmServer = (CXbdmServer *)KeGetCurrentPrcb()->DmEnetFunc; if (_pXbdmServer) { if (!_pXbdmServer->IsValidServer()) { TraceSz(Warning, "XBDM.DLL is incompatible with this version of XNET."); return(NETERR_SYSCALL); } BOOL fLinkIsUp = FALSE; status = _pXbdmServer->InitClient(cfgEnetReceiveQueueLength, cfgEnetTransmitQueueLength, &_ea, &fLinkIsUp); if (!NT_SUCCESS(status)) return(status); SetInitFlag(INITF_NIC); if (fLinkIsUp) { SetInitFlag(INITF_CONNECTED_BOOT); } return(NETERR_OK); } #elif !defined(XNET_FEATURE_XBDM_SERVER) // For xnets.lib (secure library), we cannot have XBDM owning the network stack. // If we find that it is running, we tell it to release the NIC hardware now. // Note that this code will execute on the retail box, but will have no effect // because pXbdmServer will be NULL always. This code sequence makes it possible // to run an XBE linked with xnets.lib on a devkit. CXbdmServer * pXbdmServer = (CXbdmServer *)KeGetCurrentPrcb()->DmEnetFunc; if (pXbdmServer && pXbdmServer->IsValidServer()) pXbdmServer->NicStop(); #endif SetInitFlag(INITF_NIC); StatInit(); PNICCSR pCsr = PNicCsr(); _ulIntrVector = HalGetInterruptVector(XPCICFG_NIC_IRQ, &_irqlIntr); KeInitializeDpc(&_dpc, NicDpc, this); KeInitializeInterrupt(&s_InterruptObject, NicIsr, this, _ulIntrVector, _irqlIntr, LevelSensitive, TRUE); NicStopXmitRecv(); // Reset buffer management pCsr->mode = MODE_RESET_BUFFERS; KeStallExecutionProcessor(10); pCsr->mode = 0; KeStallExecutionProcessor(10); pCsr->mintr_mk = 0; pCsr->intr_mk = 0; pCsr->pm_cntl = 0; pCsr->swtr_cntl = 0; pCsr->tx_poll = 0; pCsr->rx_poll = 0; pCsr->tx_sta = pCsr->tx_sta; pCsr->rx_sta = pCsr->rx_sta; pCsr->mintr = pCsr->mintr; pCsr->intr = pCsr->intr; _crdPool = cfgEnetReceiveQueueLength; if (_crdPool > (PAGE_SIZE/2)/sizeof(RecvDesc)) _crdPool = (PAGE_SIZE/2)/sizeof(RecvDesc); _cxdPool = cfgEnetTransmitQueueLength + 1; if (_cxdPool > (PAGE_SIZE/2)/sizeof(XmitDesc)) _cxdPool = (PAGE_SIZE/2)/sizeof(XmitDesc); XmitDesc * pxd = NULL; UINT cbDma = PAGE_SIZE + (_crdPool * NIC_FRAME_SIZE); #ifdef XNET_FEATURE_XBDM_SERVER // Try allocating the DMA memory in the first 64K if possible under XBDM. pxd = (XmitDesc *)HalDmaAlloc(cbDma, 64 * 1024); #endif if (pxd == NULL) pxd = (XmitDesc *)HalDmaAlloc(cbDma); if (pxd == NULL) { TraceSz(Warning, "Out of memory allocating DMA receive buffers"); return(NETERR_MEMORY); } // Clear the entire first page where the transmit and receive descriptors go memset(pxd, 0, PAGE_SIZE); // Compute the offset between virtual and physical memory for these descriptors _dwPhyOff = MmGetPhysicalAddress(pxd) - (DWORD_PTR)pxd; // Fill in the transmit descriptor pointers _pxdFirst = pxd; _pxdBusy = pxd; _pxdFree = pxd; _pxdLast = pxd + _cxdPool - 1; // Decrement _cxdPool so that we can assume that two transmit descriptors are // available whenever _cxdBusy < _cxdPool. This makes for an easier check later. _cxdPool -= 1; RecvDesc * prd = (RecvDesc *)((BYTE *)pxd + (PAGE_SIZE/2)); _prdFirst = prd; _prdPtr = prd; _prdLast = prd + _crdPool - 1; // Fill in the RecvDesc to point to the frame buffers. Notice that we bias the // frame pointer by NIC_FRAME_ALIGNMENT (2 bytes). This is because the CEnetHdr // is 14 bytes long, and we'd like the start of the IP packet to be on a four-byte // boundary. DWORD dwPhyAddr = (DWORD_PTR)pxd + _dwPhyOff + PAGE_SIZE + NIC_FRAME_ALIGNMENT; for (; prd <= _prdLast; ++prd, dwPhyAddr += NIC_FRAME_SIZE) { prd->_dwPhyAddr = dwPhyAddr; prd->_dwFlagsCount = RXDESC_OWN | (NIC_FRAME_SIZE - NIC_FRAME_ALIGNMENT - 1); } #ifdef XNET_FEATURE_XBDM_SERVER // Allocate and enqueue enough CXbdmPacket structures to satisfy the worst case // where the client has completely filled the transmit queue. CXbdmPacket * ppktXbdm = (CXbdmPacket *)SysAllocZ((_cxdPool + 1) * sizeof(CXbdmPacket), PTAG_CXbdmPacket); if (ppktXbdm == NULL) { TraceSz(Warning, "Out of memory allocating CXbdmPacket"); return(NETERR_MEMORY); } _ppktXbdm = ppktXbdm; for (int cPkt = _cxdPool + 1; cPkt > 0; --cPkt, ++ppktXbdm) { _pqClient.InsertTail(ppktXbdm); } #endif ULONG ulType, ulSize; status = ExQueryNonVolatileSetting(XC_FACTORY_ETHERNET_ADDR, &ulType, (BYTE *)&_ea, sizeof(CEnetAddr), &ulSize); if (!NT_SUCCESS(status) || ulSize != sizeof(CEnetAddr)) { // If we failed to read Ethernet address from non-volatile memory, // pick a random even address among the first 64 addresses of // the 00-50-f2 address block. This is so that we can at least boot // on the manufacturing line and start communicating with the test server. TraceSz(Warning, "**************************************************************************"); TraceSz2(Warning, "Unable to read Ethernet address from EEPROM (status=%08X,ulSize=%d).", status, ulSize); TraceSz(Warning, "Run the recovery CD to repair. Continuing with a random Ethernet address."); TraceSz(Warning, "**************************************************************************"); BYTE bAddr; Rand(&bAddr, sizeof(bAddr)); _ea._ab[1] = 0x50; _ea._ab[2] = 0xF2; _ea._ab[5] = (BYTE)(bAddr & 0x3E); } _dwRxPollFreq = RXPOLL_FREQ_100MPS; #ifdef XNET_FEATURE_XBDM_SERVER if (_ea._ab[5] & 1) { TraceSz(Warning, "***************************************************************************"); TraceSz1(Warning, "Ethernet address of this Development Kit is incorrect (%s).", _ea.Str()); TraceSz(Warning, "The last bit of the address should not be set. Clearing last bit and"); TraceSz(Warning, "continuing. There may be conflicts with other network devices as a result."); TraceSz(Warning, "***************************************************************************"); _ea._ab[5] &= ~1; } _eaClient = _ea; // Title always gets EEPROM address _ea._ab[5] |= 1; // Debug always gets next address // Enable unicast reception for the Ethernet address of the title stack pCsr->uni0 = HWADDR0(_eaClient._ab); pCsr->uni1 = HWADDR1(_eaClient._ab); // Enable multicast reception for the Ethernet address of the debug stack pCsr->mult_mk0 = 0xFFFFFFFF; pCsr->mult_mk1 = 0xFFFF; pCsr->mult0 = HWADDR0(_ea._ab); pCsr->mult1 = HWADDR1(_ea._ab); #else // Enable unicast reception for the Ethernet address of the title stack pCsr->uni0 = HWADDR0(_ea._ab); pCsr->uni1 = HWADDR1(_ea._ab); // Disable multicast reception pCsr->mult_mk0 = 0xFFFFFFFF; pCsr->mult_mk1 = 0xFFFF; pCsr->mult0 = 0xFFFFFFFF; pCsr->mult1 = 0xFFFF; #endif // Setup transmitter and receiver // NOTE: nVidia NIC somehow expects the maximum // receive buffer size is 1518 instead of 1514. Assert(NIC_FRAME_SIZE - NIC_FRAME_ALIGNMENT > 1518); pCsr->rx_cntl_1 = 1518; pCsr->rx_cntl_0 = RXCNTL_DEFAULT; pCsr->tx_cntl = TXCNTL_DEFAULT; // Randomly generate a backoff control timeout (single byte), but don't let it // be zero because that means no seed (aggressive retry). BYTE bBackoff; Rand(&bBackoff, sizeof(bBackoff)); bBackoff += (bBackoff == 0); pCsr->bkoff_cntl = (BKOFFCNTL_DEFAULT & 0xFFFFFF00) | bBackoff; pCsr->tx_def = TXDEF_DEFAULT; pCsr->rx_def = RXDEF_DEFAULT; pCsr->tx_dadr = PhyAddr(_pxdFirst); pCsr->rx_dadr = PhyAddr(_prdFirst); pCsr->dlen = ((_crdPool-1) << 16) | (_cxdPool); // _cxdPool already decremented earlier pCsr->rx_fifo_wm = RXFIFOWM_DEFAULT; pCsr->tx_fifo_wm = TXFIFOWM_DEFAULT; // Enable MII auto-polling interrupt (delay auto-poll enable until after PhyInitialize) pCsr->mii_cs = MIICS_DEFAULT & ~MIICS_APEN; pCsr->mii_tm = MIITM_DEFAULT; KeStallExecutionProcessor(50); // Initialize the PHY status = PhyInitialize(FALSE, NULL); if (!NT_SUCCESS(status)) return(status); pCsr->mii_cs |= MIICS_APEN; KeStallExecutionProcessor(50); NicMiiInterrupt(0, TRUE); NicStartXmitRecv(); pCsr->mintr = pCsr->mintr; pCsr->intr = pCsr->intr; pCsr->mintr_mk = MINTR_MAPI; pCsr->intr_mk = INTR_ALL; if (!KeConnectInterrupt(&s_InterruptObject)) { TraceSz(Warning, "Failed to connect NIC interrupt"); return(STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT); } _HalShutdownReg.NotificationRoutine = (PHAL_SHUTDOWN_NOTIFICATION)HalShutdownNotification; HalRegisterShutdownNotification(&_HalShutdownReg, TRUE); SetInitFlag(INITF_NIC_1); #ifdef XNET_FEATURE_XBDM_SERVER KeGetCurrentPrcb()->DmEnetFunc = &_XbdmServer; #endif return(NETERR_OK); } void CXnNic::NicStart() { ICHECK(NIC, USER); BaseStart(); Assert(!TestInitFlag(INITF_NIC_STOP)); #ifdef XNET_FEATURE_XBDM_CLIENT if (_pXbdmServer) { _pXbdmServer->AttachClient(&_XbdmClient); return; } #endif SetInitFlag(INITF_NIC_2); } void CXnNic::NicFlush() { TCHECK(UDPC|SDPC); #ifdef XNET_FEATURE_XBDM_CLIENT if (_pXbdmServer) return; #endif if (!TestInitFlag(INITF_NIC) || TestInitFlag(INITF_NIC_STOP)) return; // Wait up to half a second for packets queued for transmit to go EnetPush(); PNICCSR pCsr = PNicCsr(); for (UINT cTimeout = 10000; cTimeout > 0; --cTimeout) { pCsr->mode = MODE_TXDM; NicXmitInterrupt(); if (_pqXmit.IsEmpty()) break; KeStallExecutionProcessor(50); } } void CXnNic::NicStop() { TCHECK(UDPC|SDPC); if (TestInitFlag(INITF_NIC) && !TestInitFlag(INITF_NIC_STOP)) { if (TestInitFlag(INITF_NIC_1)) { HalRegisterShutdownNotification(&_HalShutdownReg, FALSE); } #ifdef XNET_FEATURE_XBDM_CLIENT if (_pXbdmServer) { _pXbdmServer->DetachClient(); } else #endif { if (s_InterruptObject.Connected) { KeDisconnectInterrupt(&s_InterruptObject); } KeRemoveQueueDpc(&_dpc); } SetInitFlag(INITF_NIC_STOP); } BaseStop(); } void CXnNic::NicTerm() { TCHECK(UDPC); NicStop(); SetInitFlag(INITF_NIC_TERM); if (TestInitFlag(INITF_NIC)) { StatTerm(); if (!_pqXmit.IsEmpty()) { TraceSz1(Warning, "Nic shutdown with %d packet(s) queued for transmit", _pqXmit.Count()); } while (!_pqXmit.IsEmpty()) { CPacket * ppkt = _pqXmit.RemoveHead(); MmLockUnlockBufferPages(ppkt->GetEnetHdr(), sizeof(CEnetHdr) + ppkt->GetCb(), TRUE); Assert(!ppkt->TestFlags(PKTF_XMIT_XBDMCLIENT)); PacketFree(ppkt); } if (_pxdFirst) { HalDmaFree(_pxdFirst); } #ifdef XNET_FEATURE_XBDM_SERVER if (_ppktXbdm) { SysFree(_ppktXbdm); } KeGetCurrentPrcb()->DmEnetFunc = NULL; Assert(_pXbdmClient == NULL); #endif Assert(!s_InterruptObject.Connected); } BaseTerm(); } void CXnNic::HalShutdownNotification(HAL_SHUTDOWN_REGISTRATION * pHalShutdownReg) { KIRQL kirql = ::KeRaiseIrqlToDpcLevel(); CXnNic * pXnNic = (CXnNic *)((BYTE *)pHalShutdownReg - offsetof(CXnNic, _HalShutdownReg)); ((CXnIp *)pXnNic)->SecRegShutdown(FALSE); pXnNic->NicFlush(); pXnNic->NicStop(); ::KeLowerIrql(kirql); } // --------------------------------------------------------------------------------------- // Nic Statistics // --------------------------------------------------------------------------------------- #ifdef XNET_FEATURE_STATS void CXnNic::StatInit() { _timerStats.Init((PFNTIMER)StatTimer); } void CXnNic::StatTerm() { TimerSet(&_timerStats, TIMER_INFINITE); } void CXnNic::StatInc(UINT iStat) { ICHECK(NIC, UDPC|SDPC); ULONG * pul = (ULONG *)((BYTE *)&_NicStats + iStat); *pul += 1; #ifdef XNET_FEATURE_TRACE if ( Tag(nicStats) && ( Tag(nicStatsAll) || (iStat > offsetof(NICSTATS, rxGoodFrames)))) { if (!_timerStats.IsActive()) { TimerSetRelative(&_timerStats, 5 * TICKS_PER_SECOND); } } #endif } void CXnNic::StatTimer(CTimer * pt) { ICHECK(NIC, UDPC|SDPC); #define DUMPSTAT(n) if (_NicStatsLast.n != _NicStats.n) { TraceSz3(nicStats, "%5d [%5d] %s", _NicStats.n - _NicStatsLast.n, _NicStats.n, #n); } TraceSz7(nicStats, "[tx %d] [txq %d/%d] [rx %d] [rxq %d] [isr %d] [dpc %d]", _NicStats.txGoodFrames, _cxdBusy, _cxdPool, _NicStats.rxGoodFrames, _crdPool, _NicStats.isrCount, _NicStats.dpcCount); DUMPSTAT(txUnderflowErrors); DUMPSTAT(txLateCollisions); DUMPSTAT(txLostCarriers); DUMPSTAT(txDefers); DUMPSTAT(txExcessiveDefers); DUMPSTAT(txRetryErrors); DUMPSTAT(rxMinSizeErrors); DUMPSTAT(rxFramingErrors); DUMPSTAT(rxOverFlowErrors); DUMPSTAT(rxCrcErrors); DUMPSTAT(rxLengthErrors); DUMPSTAT(rxMaxFrameErrors); DUMPSTAT(rxLateCollisions); DUMPSTAT(rxRunts); DUMPSTAT(rxExtraByteErrors); DUMPSTAT(rxMissedFrames); DUMPSTAT(rxEndOfFrameErrors); _NicStatsLast = _NicStats; } #endif // --------------------------------------------------------------------------------------- // CXbdmClient // --------------------------------------------------------------------------------------- #ifdef XNET_FEATURE_XBDM_CLIENT INLINE CXnNic * CXnNic::CXbdmClient::GetXnNic() { return((CXnNic *)((BYTE *)this - offsetof(CXnNic, _XbdmClient))); } void CXnNic::CXbdmClient::XmitComplete(void * pvPkt) { ((CPacket *)pvPkt)->Complete(GetXnNic()); } void CXnNic::CXbdmClient::EnetRecv(UINT uiFlags, void * pv, UINT cb, UINT uiType) { CPacket pkt; pkt.Init(uiFlags, pv, cb, NULL); GetXnNic()->PushPktRecvTags(pkt.GetEnetHdr()->_eaDst.IsBroadcast()); GetXnNic()->EnetRecv(&pkt, uiType); GetXnNic()->PopPktRecvTags(); } void CXnNic::CXbdmClient::EnetPush() { GetXnNic()->EnetPush(); } DWORD CXnNic::CXbdmClient::GetXnAddr(XNADDRXBDM * pxnaXbdm) { XNADDR xnaddr; DWORD dwFlags = ((CXnIp *)GetXnNic())->IpGetXnAddr(&xnaddr); memcpy(pxnaXbdm->abEnet, xnaddr.abEnet, sizeof(pxnaXbdm->abEnet)); pxnaXbdm->ina = xnaddr.ina; return(dwFlags); } DWORD CXnNic::NicGetOtherXnAddr(XNADDR * pxna) { XNADDRXBDM xnaddrXbdm = { 0 }; DWORD dwFlags = _pXbdmServer ? _pXbdmServer->GetXnAddr(&xnaddrXbdm) : XNET_GET_XNADDR_NONE; memset(pxna, 0, sizeof(*pxna)); memcpy(pxna->abEnet, xnaddrXbdm.abEnet, sizeof(pxna->abEnet)); pxna->ina = xnaddrXbdm.ina; return(dwFlags); } #endif // --------------------------------------------------------------------------------------- // CXbdmServer // --------------------------------------------------------------------------------------- #ifdef XNET_FEATURE_XBDM_SERVER INLINE CXnNic * CXnNic::CXbdmServer::GetXnNic() { return((CXnNic *)((BYTE *)this - offsetof(CXnNic, _XbdmServer))); } void CXnNic::CXbdmServer::NicStop() { Assert(GetXnNic()->_pXbdmClient == NULL); KIRQL irql = ::KeRaiseIrqlToDpcLevel(); GetXnNic()->NicStop(); KeGetCurrentPrcb()->DmEnetFunc = NULL; ::KeLowerIrql(irql); } NTSTATUS CXnNic::CXbdmServer::InitClient(UINT cfgRecvQ, UINT cfgXmitQ, CEnetAddr * pea, BOOL * pfLinkIsUp) { Assert(GetXnNic()->_pXbdmClient == NULL); *pea = GetXnNic()->_eaClient; *pfLinkIsUp = GetXnNic()->TestInitFlag(INITF_CONNECTED_BOOT); return(NETERR_OK); } void CXnNic::CXbdmServer::AttachClient(CXbdmClient * pXbdmClient) { Assert(GetXnNic()->_pXbdmClient == NULL); KIRQL irql = ::KeRaiseIrqlToDpcLevel(); GetXnNic()->_pXbdmClient = pXbdmClient; ::KeLowerIrql(irql); } void CXnNic::CXbdmServer::DetachClient() { Assert(KeGetCurrentIrql() == DISPATCH_LEVEL); CXnNic * pXnNic = GetXnNic(); PNICCSR pCsr = PNicCsr(); UINT cTimeout = 0; while (!pXnNic->_pqXmit.IsEmpty()) { pCsr->mode = MODE_TXDM; pXnNic->NicXmitInterrupt(); KeStallExecutionProcessor(50); cTimeout++; AssertSz(cTimeout != 20000, "Taking too long to flush transmit queue"); } pXnNic->_pXbdmClient = NULL; } void CXnNic::CXbdmServer::Xmit(void * pvPkt, void * pv, UINT cb) { CXnNic * pXnNic = GetXnNic(); Assert(pXnNic->_pXbdmClient != NULL); Assert(!pXnNic->_pqClient.IsEmpty()); CXbdmPacket * ppktXbdm = (CXbdmPacket *)pXnNic->_pqClient.RemoveHead(); ppktXbdm->Init(PKTF_XMIT_XBDMCLIENT, (BYTE *)pv + sizeof(CEnetHdr), cb - sizeof(CEnetHdr), NULL); ppktXbdm->_pvPkt = pvPkt; pXnNic->NicXmit(ppktXbdm); } BOOL CXnNic::CXbdmServer::XmitReady() { Assert(GetXnNic()->_pXbdmClient != NULL); BOOL fReady = GetXnNic()->NicXmitReady(); return(fReady); } DWORD CXnNic::CXbdmServer::GetXnAddr(XNADDRXBDM * pxnaXbdm) { XNADDR xnaddr; DWORD dwFlags = ((CXnIp *)GetXnNic())->IpGetXnAddr(&xnaddr); memcpy(pxnaXbdm->abEnet, xnaddr.abEnet, sizeof(pxnaXbdm->abEnet)); pxnaXbdm->ina = xnaddr.ina; return(dwFlags); } DWORD CXnNic::NicGetOtherXnAddr(XNADDR * pxna) { XNADDRXBDM xnaddrXbdm = { 0 }; DWORD dwFlags = _pXbdmClient ? _pXbdmClient->GetXnAddr(&xnaddrXbdm) : XNET_GET_XNADDR_NONE; memset(pxna, 0, sizeof(*pxna)); memcpy(pxna->abEnet, xnaddrXbdm.abEnet, sizeof(pxna->abEnet)); pxna->ina = xnaddrXbdm.ina; return(dwFlags); } #endif // --------------------------------------------------------------------------------------- #endif