1229 lines
34 KiB
C++
1229 lines
34 KiB
C++
// ---------------------------------------------------------------------------------------
|
|
// 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
|
|
|