xbox-kernel/private/ntos/xnet/enet/xnic.c
2020-09-30 17:17:25 +02:00

1128 lines
27 KiB
C

/*++
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 <xboxp.h>
//
// 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