/*++ Copyright (c) 2000 Microsoft Corporation Module Name: nic.c Abstract: XBox network controller "driver" code Revision History: 07/25/2000 davidx Created it. --*/ #include "precomp.h" #ifndef SILVER #include // // NIC driver global variables // UINT NicXmitDescCount; BYTE* NicPktPoolBase; UINT_PTR NicPktPoolPhyAddrOffset; KINTERRUPT NicIntrObject; BOOL NicDontCopyReceivedPacket; PRIVATE VOID NicInterruptDpc(PKDPC, PVOID, PVOID, PVOID); // BUGBUG: temporary workaround for nv2a snooping bug #ifdef DVTSNOOPBUG BYTE NicTempRecvBuffer[DMAPKT_SIZE]; PRIVATE VOID NicTempRecvBufferPktCompletion(Packet* pkt, NTSTATUS status) {} #endif // // NIC statistics // typedef struct _XNICSTATS { ULONG isrCount; ULONG dpcCount; ULONG txGoodFrames; ULONG rxGoodFrames; ULONG txStuckXmits; ULONG txUnderflowErrors; ULONG txLateCollisions; ULONG txLostCarriers; ULONG txDefers; ULONG txExcessiveDefers; ULONG txRetryErrors; ULONG rxFramingErrors; ULONG rxOverFlowErrors; ULONG rxCrcErrors; ULONG rxLengthErrors; ULONG rxMaxFrameErrors; ULONG rxLateCollisions; ULONG rxRunts; ULONG rxExtraByteErrors; ULONG rxMissedFrames; ULONG rxEndOfFrameErrors; } XNICSTATS; XNICSTATS NicStats; // // Lock and unlock the physical pages containing packet data // INLINE VOID NicLockPacketPages(Packet* pkt) { MmLockUnlockBufferPages(pkt->data, pkt->datalen, FALSE); } INLINE VOID NicUnlockPacketPages(Packet* pkt) { MmLockUnlockBufferPages(pkt->data, pkt->datalen, TRUE); } // // Where the received Ethernet frame data starts // #define GetPktFrameData(pkt) (&(pkt)->buf[RECVPKT_OVERHEAD]) // // Receive packet completion routine // PRIVATE VOID NicRecvBufferPktCompletion(Packet* pkt, NTSTATUS status) { pkt->nextpkt = NULL; pkt->data = GetPktFrameData(pkt); pkt->pktflags = PKTFLAG_DMA; } PRIVATE VOID NicStopXmitRecv( IfEnet* nic, INT handleIntr ) /*++ Routine Description: Stop the controller from receiving and transmitting Arguments: nic - Points to the NIC data structure handleIntr - Whether to handle the pending interrupts > 0 : disable and then handle interrupts = 0 : disable interrupts < 0 : leave interrupts alone Return Value: NONE --*/ { PNIC_CSR csr = nic->CSR; INT timeout; // Turn off the transmitter and receiver csr->rx_poll = 0; csr->rx_en = 0; csr->tx_en = 0; // Wait for a max of 5msec until both // the transmitter and receiver are idle for (timeout=500; timeout--; ) { if (!(csr->rx_sta & RXSTA_BUSY) && !(csr->tx_sta & TXSTA_BUSY)) break; KeStallExecutionProcessor(10); } // Ensure there is no active DMA transfer in progress csr->mode = MODE_DISABLE_DMA; for (timeout=500; timeout--; ) { if (csr->mode & MODE_DMA_IDLE) break; KeStallExecutionProcessor(10); } csr->mode = 0; if (handleIntr >= 0) { // Disable interrupts and // handle any pending interrupts if requested NicDisableInterrupt(); if (handleIntr > 0) { NicInterruptDpc(&nic->dpc, nic, nic, NULL); } } } // // Start the transmitter and receiver // INLINE VOID NicStartXmitRecv(PNIC_CSR csr, DWORD rxpoll_freq) { csr->rx_poll = RXPOLL_EN | rxpoll_freq; csr->tx_en = TXEN_ENABLE; csr->rx_en = RXEN_ENABLE; csr->mode = MODE_TXDM; } PRIVATE VOID NicProcessRecvInterrupt( IfEnet* nic ) /*++ Routine Description: Process receive interrupts Arguments: nic - Points to the NIC data structure Return Value: NONE --*/ { NicBufDesc* bufdesc = nic->cmdbuf.rxptr; Packet* pkt; DWORD flags_count; // Looping until we ran into a receive descriptor // that's still owned by the controller while (!((flags_count = bufdesc->flags_count) & RXDESC_OWN)) { pkt = PktQRemoveHead(&nic->recvq); if (flags_count & RXDESC_REND) { // // NOTE: Workaround for hardware bug // Framing errors are ignored. // if (!(flags_count & RXDESC_ERR) || (flags_count & RXDESC_FRAM)) { // // A packet was received successfully. // Pass it upstream for processing. // // Note: we expect the processing to be done // synchronously here and the upstream component // we'll call CompletePacket on our packet // inside the following call. // NicStats.rxGoodFrames++; pkt->datalen = flags_count & 0xffff; // NOTE: Workaround for hardware bug // If extra byte flag is set, decrement the frame length by 1 if (flags_count & RXDESC_FRAM) { NicStats.rxFramingErrors++; if (flags_count & RXDESC_EXTRA) { NicStats.rxExtraByteErrors++; pkt->datalen--; } } // BUGBUG: workaround for nv2a hardware bug #ifndef DVTSNOOPBUG EnetReceiveFrame(nic, pkt); #else // DVTSNOOPBUG { UINT len = pkt->datalen; Packet* p = (Packet*) NicTempRecvBuffer; p->nextpkt = NULL; p->pktflags = p->iphdrOffset = 0; p->data = p->buf; p->datalen = len; p->recvifp = (IfInfo*) nic; p->completionCallback = NicTempRecvBufferPktCompletion; memcpy(p->data, pkt->data, len); NicRecvBufferPktCompletion(pkt, NETERR_OK); EnetReceiveFrame(nic, p); } #endif // DVTSNOOPBUG } else { if (flags_count & RXDESC_OFOL) NicStats.rxOverFlowErrors++; if (flags_count & RXDESC_CRC ) NicStats.rxCrcErrors++; if (flags_count & RXDESC_LFER) NicStats.rxLengthErrors++; if (flags_count & RXDESC_MAX ) NicStats.rxMaxFrameErrors++; if (flags_count & RXDESC_LCOL) NicStats.rxLateCollisions++; if (flags_count & RXDESC_RUNT) NicStats.rxRunts++; } } else { NicStats.rxEndOfFrameErrors++; } // Give the ownership of the receive descriptor back to the NIC // And tell the receiver to check the receive descriptor ring ASSERT(bufdesc->phyaddr == MmGetPhysicalAddress(pkt->data)); PktQInsertTail(&nic->recvq, pkt); bufdesc->flags_count = RXDESC_OWN | (DMAPKT_MAXDATA - 1); // Move on to the next receive descriptor if (++bufdesc == nic->cmdbuf.rxend) bufdesc = nic->cmdbuf.rxstart; } // Update the next receive descriptor pointer nic->cmdbuf.rxptr = bufdesc; } PRIVATE VOID NicCheckMiiStatus( IfEnet* nic, DWORD mintr, BOOL init ) /*++ Routine Description: Check the PHY status Arguments: nic - Points to the NIC data structure mintr - MII interrupt bits init - Whether this is the first call after reboot (Tx and Rx are currently stopped) Return Value: NONE --*/ { PNIC_CSR csr = nic->CSR; DWORD miics = csr->mii_cs; DWORD delta = nic->linkStatus ^ miics; if (init || ((mintr & MINTR_MAPI) && (delta & MIICS_LS) && (miics & MIICS_LS))) { DWORD linkState = PhyGetLinkState(!init); // BUGBUG: always dump out Ethernet link status for now #if DBG DbgPrint("Ethernet link status: %s %dMbps %s-duplex\n", (linkState & XNET_LINK_IS_UP) ? "up" : "down", (linkState & XNET_LINK_100MBPS) ? 100 : (linkState & XNET_LINK_10MBPS) ? 10 : 0, (linkState & XNET_LINK_FULL_DUPLEX) ? "full" : (linkState & XNET_LINK_HALF_DUPLEX) ? "half" : "?"); #endif // 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 (!init) { NicStopXmitRecv(nic, -1); } nic->rxpollFreq = (linkState & XNET_LINK_10MBPS) ? RXPOLL_FREQ_10MPS : RXPOLL_FREQ_100MPS; if (linkState & XNET_LINK_FULL_DUPLEX) csr->tx_cntl &= ~TXCNTL_HDEN; else csr->tx_cntl |= TXCNTL_HDEN; if (!init) { NicStartXmitRecv(csr, nic->rxpollFreq); } if (init && (linkState & XNET_LINK_IS_UP)) nic->flags |= IFFLAG_CONNECTED_BOOT; } nic->linkStatus = miics; } // // Check if transmit descriptor td2 is the last one used // to transmit an Ethernet frame that started at td1. // INLINE BOOL IsEndXmitDesc(IfEnet* nic, NicBufDesc* td1, NicBufDesc* td2) { if (td1 == td2) return TRUE; if (td1->flags_count & TXDESC_TEND) return FALSE; if (++td1 == nic->cmdbuf.txend) td1 = nic->cmdbuf.txstart; return (td1 == td2); } PRIVATE BOOL NicProcessXmitInterrupt( IfEnet* nic ) /*++ Routine Description: Process transmit interrupts Arguments: nic - Points to the NIC data structure Return Value: TRUE if we processed any transmit completion interrupts FALSE otherwise --*/ { NicBufDesc* bufdesc; Packet* pkt; DWORD flags_count; NTSTATUS status; BOOL processed = FALSE; // Loop through uncompleted transmission queue while (pkt = nic->cmdq.head) { bufdesc = (NicBufDesc*) pkt->ifdata; if ((flags_count = bufdesc->flags_count) & TXDESC_OWN) break; processed = TRUE; PktQRemoveHead(&nic->cmdq); nic->cmdqCount--; nic->cmdqWatchdog = 0; NicUnlockPacketPages(pkt); ASSERT(flags_count & TXDESC_TEND); if (!(flags_count & TXDESC_ERR)) { NicStats.txGoodFrames++; status = NETERR_OK; } else { if (flags_count & TXDESC_UFLO) NicStats.txUnderflowErrors++; if (flags_count & TXDESC_LCOL) NicStats.txLateCollisions++; if (flags_count & TXDESC_LCAR) NicStats.txLostCarriers++; if (flags_count & TXDESC_DEF) NicStats.txDefers++; if (flags_count & TXDESC_EXDEF) NicStats.txExcessiveDefers++; if (flags_count & TXDESC_RTRY) NicStats.txRetryErrors++; status = NETERR_HARDWARE; } COMPLETEPACKET(pkt, status); // Update the uncompleted transmit descriptor pointer ASSERT(IsEndXmitDesc(nic, nic->cmdbuf.txtail, bufdesc)); ASSERT((nic->cmdbuf.txtail->flags_count & TXDESC_OWN) == 0); bufdesc++; nic->cmdbuf.txtail = (bufdesc == nic->cmdbuf.txend) ? nic->cmdbuf.txstart : bufdesc; } return processed; } VOID NicWaitForXmitQEmpty( IfEnet* nic ) /*++ Routine Description: Wait until the transmit queue is empty This is only called by the debug monitor when the regular net stack is unloaded. Arguments: nic - Points to the NIC data structure Return Value: NONE --*/ { UINT timeout = 0; while (!PktQIsEmpty(&nic->cmdq)) { NicProcessXmitInterrupt(nic); KeStallExecutionProcessor(50); timeout++; ASSERT(timeout < 20000); } } PRIVATE VOID NicInterruptDpc( PKDPC dpc, PVOID deferredContext, PVOID noReenable, PVOID arg2 ) /*++ Routine Description: Ethernet interface interrupt service routine (runs at DISPATCH_LEVEL) Arguments: dpc - Pointer to the DPC object deferredContext - Points to the NIC data structure noReenable - Leave interrupts disabled arg2 - Unused arguments Return Value: NONE --*/ { IfEnet* nic = (IfEnet*) deferredContext; PNIC_CSR csr = nic->CSR; NicStats.dpcCount++; while (TRUE) { DWORD mintr = csr->mintr; DWORD intr = csr->intr; // Acknowledge all pending interrupts // note that we need to acknowledge MII interrupts first if (intr == 0) break; csr->mintr = mintr; csr->intr = intr; if (intr & INTR_MINT) { // Process MII interrupt NicCheckMiiStatus(nic, mintr, FALSE); } if (intr & (INTR_MISS|INTR_RCINT|INTR_REINT)) { // Process any received packets NicProcessRecvInterrupt(nic); if (intr & INTR_MISS) { csr->mode = MODE_RXDM; NicStats.rxMissedFrames++; } } if (intr & (INTR_TCINT|INTR_TEINT)) { // Process any completed transmissions NicProcessXmitInterrupt(nic); // If there is more room now in the command queue, we can // move some packets from the send queue to the command queue. if (!EnetIsSendQEmpty(nic) && nic->cmdqCount < cfgXmitQLength) { EnetStartOutput(nic); } } } // Reenable interrupts if (!noReenable) { NicEnableInterrupt(); } } PRIVATE BOOLEAN NicIsr( PKINTERRUPT interrupt, PVOID serviceContext ) /*++ Routine Description: Ethernet interrupt service routine (runs at DIRQL) Arguments: interrupt - Interrupt object serviceContext - Points to the NIC data structure Return Value: TRUE if the interrupt was handled FALSE if the interrupt wasn't generated by our device --*/ { IfEnet* nic = (IfEnet*) serviceContext; NicStats.isrCount++; if ((nic->CSR->intr & nic->CSR->intr_mk) == 0) return FALSE; // Yep, this is ours: // schedule the DPC routine for execution // and disable further interrupts KeInsertQueueDpc(&nic->dpc, NULL, NULL); NicDisableInterrupt(); return TRUE; } VOID NicTimerProc( IfEnet* nic ) /*++ Routine Description: NIC timer procedure Arguments: nic - Points to the NIC data structure Return Value: NONE --*/ { // If the transmitter is stuck, start it if (!PktQIsEmpty(&nic->cmdq) && nic->cmdqWatchdog++ > 2) { WARNING_("Transmitter is stuck."); nic->cmdqWatchdog = 0; NicStats.txStuckXmits++; NicProcessXmitInterrupt(nic); nic->CSR->mode = MODE_TXDM; } } VOID NicTransmitFrame( IfEnet* nic, Packet* pkt ) /*++ Routine Description: Transmit an Ethernet frame to the NIC Arguments: nic - Points to the NIC data structure pkt - Points to the frame to be transmitted We assume the Ethernet frame has been completed filled out. Return Value: NONE --*/ { UINT_PTR phyaddr0; UINT len, len0; NicBufDesc* bufdesc; // Make sure we have at least 2 transmit descriptors available len = (nic->cmdbuf.txtail <= nic->cmdbuf.txhead) ? (NicXmitDescCount - (nic->cmdbuf.txhead - nic->cmdbuf.txtail)) : (nic->cmdbuf.txtail - nic->cmdbuf.txhead); if (len <= 2) { WARNING_("Out of transmit descriptors."); COMPLETEPACKET(pkt, NETERR_MEMORY); return; } // Check the packet data size ASSERT(pkt->datalen != 0); if (pkt->datalen > ENETHDRLEN+ENET_MAXDATASIZE) { COMPLETEPACKET(pkt, NETERR_MSGSIZE); return; } NicLockPacketPages(pkt); // Since the total buffer size is <= 1500+14 bytes, // it can at most span two physical pages. len = pkt->datalen; phyaddr0 = MmGetPhysicalAddress(pkt->data); len0 = PAGE_SIZE - (phyaddr0 & (PAGE_SIZE-1)); if (len <= len0) { // The buffer is in a single physical page. // We only need one transmit descriptor here. // // Notice the funny len-1 business below. // This is an odd requirement of the NIC. bufdesc = nic->cmdbuf.txhead; bufdesc->phyaddr = phyaddr0; bufdesc->flags_count = TXDESC_OWN | TXDESC_TEND | (len-1); } else { // The buffer straddles page boundary. // So we need two transmit descriptors. NicBufDesc* bufdesc0 = nic->cmdbuf.txhead; bufdesc = bufdesc0+1; if (bufdesc == nic->cmdbuf.txend) bufdesc = nic->cmdbuf.txstart; // NOTE: We're setting up the second transmit descriptor // before the first one. Otherwise, the NIC might use up // the first descriptor before we have the second one ready. bufdesc->phyaddr = MmGetPhysicalAddress(pkt->data + len0); bufdesc->flags_count = TXDESC_OWN | TXDESC_TEND | (len-len0-1); bufdesc0->phyaddr = phyaddr0; bufdesc0->flags_count = TXDESC_OWN | (len0-1); } pkt->ifdata = (UINT_PTR) bufdesc; PktQInsertTail(&nic->cmdq, pkt); nic->cmdqCount++; // Tell the transmitter to check the transmit descriptor ring nic->CSR->mode = MODE_TXDM; bufdesc++; nic->cmdbuf.txhead = (bufdesc == nic->cmdbuf.txend) ? nic->cmdbuf.txstart : bufdesc; } // // Tell the NIC to not to receive any multicast frames // INLINE VOID NicRecvMcastNone(PNIC_CSR csr) { csr->mult_mk0 = 0; csr->mult_mk1 = 0; csr->mult0 = 1; csr->mult1 = 0; } NTSTATUS NicSetMcastAddrs( IfEnet* nic, const BYTE* addrs, UINT count ) /*++ Routine Description: Send multicast addresses down to the NIC. Arguments: nic - Points to the NIC data structure addrs - Points to an array of multicast addresses count - Specifies the number of multicast addresses Return Value: Status code --*/ // Converting Ethernet address from a sequence of bytes // to two DWORDs that can be set into UNIn and MULTn registers #define HWADDR0(_hwaddr) ((const DWORD*) (_hwaddr))[0] #define HWADDR1(_hwaddr) ((const WORD*) (_hwaddr))[2] { PNIC_CSR csr = nic->CSR; // Stop the controller from transmitting and receiving NicStopXmitRecv(nic, 1); if (count == 0) { // Don't receive any multicast frames NicRecvMcastNone(csr); } else { // Compute the multicast address filter values. // Notice that the filtering here is not perfect. // Exactly filtering is done inside IP receive function. DWORD andMask0 = -1; DWORD orMask0 = 0; WORD andMask1 = -1; WORD orMask1 = 0; while (count--) { andMask0 &= HWADDR0(addrs); orMask0 |= HWADDR0(addrs); andMask1 &= HWADDR1(addrs); orMask1 |= HWADDR1(addrs); addrs += ENETADDRLEN; } orMask0 = andMask0 | ~orMask0; orMask1 = andMask1 | ~orMask1; csr->mult0 = andMask0; csr->mult1 = andMask1; csr->mult_mk0 = orMask0; csr->mult_mk1 = orMask1; } // Restart transmitter and receiver NicStartXmitRecv(csr, nic->rxpollFreq); NicEnableInterrupt(); return NETERR_OK; } VOID NicReset( IfEnet* nic, BOOL disconnectIntr ) /*++ Routine Description: Reset the network controller: disable transmit and receive disable all interrupts clear any pending interrupt bits Arguments: nic - Points to the NIC data structure disconnectIntr - Whether to disconnect interrupts Return Value: NONE --*/ { PNIC_CSR csr = nic->CSR; // Stop transmitter and receiver NicStopXmitRecv(nic, 0); // Reset buffer management csr->mode = MODE_RESET_BUFFERS; KeStallExecutionProcessor(10); csr->mode = 0; KeStallExecutionProcessor(10); csr->intr_mk = 0; csr->mintr_mk = 0; csr->pm_cntl = 0; csr->swtr_cntl = 0; csr->tx_poll = 0; csr->rx_poll = 0; csr->tx_sta = csr->tx_sta; csr->rx_sta = csr->rx_sta; csr->intr = csr->intr; csr->mintr = csr->mintr; if (disconnectIntr) { NicDisconnectInterrupt(nic); } } PRIVATE NTSTATUS NicInitBuffers( IfEnet* nic ) /*++ Routine Description: Initialize transmit and receive buffer descriptors Arguments: nic - Points to the NIC data structure Return Value: Status code --*/ { BYTE* buf; NicBufDesc* bufdesc; UINT index; Packet* pkt; NTSTATUS status; status = NicPktPoolInit(cfgRecvQLength); if (!NT_SUCCESS(status)) return status; // Allocate one contiguous physical page buf = (BYTE*) NicAllocSharedMem(PAGE_SIZE); if (!buf) return NETERR_MEMORY; ZeroMem(buf, PAGE_SIZE); nic->cmdbuf.phyaddrOffset = MmGetPhysicalAddress(buf) - (UINT_PTR) buf; // First half page is used for transmit buffer descriptors nic->cmdbuf.txstart = (NicBufDesc*) buf; nic->cmdbuf.txend = nic->cmdbuf.txstart + NicXmitDescCount; nic->cmdbuf.txhead = nic->cmdbuf.txtail = nic->cmdbuf.txstart; // Second half page is used for receive buffer descriptors nic->cmdbuf.rxstart = (NicBufDesc*) (buf + PAGE_SIZE / 2); nic->cmdbuf.rxend = nic->cmdbuf.rxstart + cfgRecvQLength; nic->cmdbuf.rxptr = nic->cmdbuf.rxstart; // Allocate receive buffers bufdesc = nic->cmdbuf.rxstart; for (index=0; index < cfgRecvQLength; index++) { pkt = NicPktAlloc(index); pkt->data = GetPktFrameData(pkt); pkt->recvifp = (IfInfo*) nic; XnetSetPacketCompletion(pkt, NicRecvBufferPktCompletion); PktQInsertTail(&nic->recvq, pkt); bufdesc->phyaddr = NicPktGetPhyAddr(pkt->data); bufdesc->flags_count = RXDESC_OWN | (DMAPKT_MAXDATA - 1); bufdesc++; } return NETERR_OK; } PRIVATE NTSTATUS NicReadEnetAddr( IfEnet* nic ) /*++ Routine Description: Read permanent Ethernet address Arguments: nic - Points to the NIC data structure Return Value: Status code --*/ { DWORD err; ULONG type, size, tick0; err = XQueryValue(XC_FACTORY_ETHERNET_ADDR, &type, nic->hwaddr, ENETADDRLEN, &size); if (err != ERROR_SUCCESS || size != ENETADDRLEN) { // NOTE: // If we failed to read Ethernet address from non-volatile memory, // pick a random address among the first 32 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. #ifdef DEVKIT DbgPrint("########## Invalid Ethernet address:\n"); DbgPrint(" You must run the Recovery CD.\n"); DbgPrint(" Defaulting to hardcoded Ethernet address...\n"); #endif __asm { rdtsc mov tick0, eax } nic->hwaddr[0] = 0x00; nic->hwaddr[1] = 0x50; nic->hwaddr[2] = 0xf2; nic->hwaddr[3] = 0x00; nic->hwaddr[4] = 0x00; nic->hwaddr[5] = (BYTE) (tick0 & 0x1f); } nic->hwaddrlen = ENETADDRLEN; return NETERR_OK; } VOID NicCleanup( IfEnet* nic ) /*++ Routine Description: Cleanup the NIC interface Arguments: nic - Points to the NIC data structure Return Value: NONE --*/ { // Clean up the command queue and the shared command data buffer while (!PktQIsEmpty(&nic->cmdq)) { Packet* pkt = PktQRemoveHead(&nic->cmdq); NicUnlockPacketPages(pkt); COMPLETEPACKET(pkt, NETERR_CANCELLED); } // Clean up the receive buffer queue NicPktPoolCleanup(); PktQInit(&nic->recvq); if (nic->cmdbuf.txstart) { NicFreeSharedMem(nic->cmdbuf.txstart); nic->cmdbuf.txstart = NULL; } NicDisconnectInterrupt(nic); } NTSTATUS NicInitialize( IfEnet* nic ) /*++ Routine Description: Initialize the NIC "driver" code Arguments: nic - Points to the NIC data structure Return Value: Status code --*/ { PNIC_CSR csr = NicCsr; NTSTATUS status = NETERR_HARDWARE; // NicXmitDescCount must be > 2*cfgXmitQLength and < PAGE_SIZE/(2*8) NicXmitDescCount = 2*cfgXmitQLength+1; if (NicXmitDescCount < 64) NicXmitDescCount = 64; ASSERT(NicXmitDescCount < PAGE_SIZE/(2*sizeof(NicBufDesc))); // Locate the NIC card and get the assigned resources nic->CSR = (PNIC_CSR) XPCICFG_NIC_MEMORY_REGISTER_BASE_0; nic->csrSize = XPCICFG_NIC_MEMORY_REGISTER_LENGTH_0; nic->intrVector = HalGetInterruptVector(XPCICFG_NIC_IRQ, &nic->intrIrql); KeInitializeDpc(&nic->dpc, NicInterruptDpc, nic); KeInitializeInterrupt( &NicIntrObject, NicIsr, nic, nic->intrVector, nic->intrIrql, LevelSensitive, TRUE); // Reset the NIC NicReset(nic, FALSE); // Initialize transmit and receive buffers status = NicInitBuffers(nic); if (!NT_SUCCESS(status)) goto err; // Read permanent Ethernet address status = NicReadEnetAddr(nic); if (!NT_SUCCESS(status)) goto err; nic->rxpollFreq = RXPOLL_FREQ_100MPS; csr->uni0 = HWADDR0(nic->hwaddr); csr->uni1 = HWADDR1(nic->hwaddr); // Disable multicast frame reception by default NicRecvMcastNone(csr); // Setup transmitter and receiver // NOTE: nVidia NIC somehow expects the maximum // receive buffer size is 1518 instead of 1514. ASSERT(DMAPKT_MAXDATA > 1518); csr->rx_cntl_1 = 1518; csr->rx_cntl_0 = RXCNTL_DEFAULT; csr->tx_cntl = TXCNTL_DEFAULT; csr->bkoff_cntl = BKOFFCNTL_DEFAULT; csr->tx_def = TXDEF_DEFAULT; csr->rx_def = RXDEF_DEFAULT; csr->tx_dadr = NicBufPhyAddr(nic, nic->cmdbuf.txhead); csr->rx_dadr = NicBufPhyAddr(nic, nic->cmdbuf.rxptr); csr->dlen = ((cfgRecvQLength-1) << 16) | (NicXmitDescCount-1); csr->rx_fifo_wm = RXFIFOWM_DEFAULT; csr->tx_fifo_wm = TXFIFOWM_DEFAULT; // Enable MII auto-polling interrupt csr->mii_cs = MIICS_DEFAULT; csr->mii_tm = MIITM_DEFAULT; csr->mintr_mk = MINTR_MAPI; // Initialize the PHY csr->mii_cs &= ~MIICS_APEN; status = PhyInitialize(FALSE, NULL); csr->mii_cs |= MIICS_APEN; if (!NT_SUCCESS(status)) goto err; NicCheckMiiStatus(nic, 0, TRUE); // Enable transmit and receive NicStartXmitRecv(csr, nic->rxpollFreq); // Connect the NIC interrupt if (KeConnectInterrupt(&NicIntrObject)) return NETERR_OK; status = STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT; err: NicReset(nic, FALSE); NicCleanup(nic); return status; } #endif // !SILVER