diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/tdi/nbf/dlc.c | 3270 |
1 files changed, 3270 insertions, 0 deletions
diff --git a/private/ntos/tdi/nbf/dlc.c b/private/ntos/tdi/nbf/dlc.c new file mode 100644 index 000000000..893bffd49 --- /dev/null +++ b/private/ntos/tdi/nbf/dlc.c @@ -0,0 +1,3270 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + dlc.c + +Abstract: + + This module contains code which implements the data link layer for the + transport provider. + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Macros + + +// +// These two functions are used by the loopback indicator. +// + +STATIC +VOID +NbfCopyFromPacketToBuffer( + IN PNDIS_PACKET Packet, + IN UINT Offset, + IN UINT BytesToCopy, + OUT PCHAR Buffer, + OUT PUINT BytesCopied + ); + + +VOID +NbfProcessSabme( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PDLC_U_FRAME Header, + IN PVOID MacHeader, + IN PHARDWARE_ADDRESS SourceAddress, + IN PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine processes a received SABME frame. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC U-type frame. + + MacHeader - Pointer to the MAC header of the packet. + + DeviceContext - The device context of this adapter. + +Return Value: + + none. + +--*/ + +{ + PUCHAR SourceRouting; + UINT SourceRoutingLength; + UCHAR TempSR[MAX_SOURCE_ROUTING]; + PUCHAR ResponseSR; + +#if DBG + UCHAR *s; +#endif + + Header; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessSabme: Entered.\n"); + } + + // + // Format must be: SABME-c/x, on a real link object. + // + + if (!Command || (Link == NULL)) { + return; + } + + // + // Build response routing information. We do this on the SABME, even + // though we already did in on the Name Query, because the client may + // choose a different route (if there were multiple routes) than we + // did. + // + + MacReturnSourceRouting( + &DeviceContext->MacInfo, + MacHeader, + &SourceRouting, + &SourceRoutingLength); + + if (SourceRouting != NULL) { + + RtlCopyMemory( + TempSR, + SourceRouting, + SourceRoutingLength); + + MacCreateNonBroadcastReplySR( + &DeviceContext->MacInfo, + TempSR, + SourceRoutingLength, + &ResponseSR); + + } else { + + ResponseSR = NULL; + + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + + MacConstructHeader ( + &DeviceContext->MacInfo, + Link->Header, + SourceAddress->Address, + DeviceContext->LocalAddress.Address, + 0, // PacketLength, filled in later + ResponseSR, + SourceRoutingLength, + (PUINT)&(Link->HeaderLength)); + + // + // We optimize for fourteen-byte headers by putting + // the correct Dsap/Ssap at the end, so we can fill + // in new packets as one 16-byte move. + // + + if (Link->HeaderLength <= 14) { + Link->Header[Link->HeaderLength] = DSAP_NETBIOS_OVER_LLC; + Link->Header[Link->HeaderLength+1] = DSAP_NETBIOS_OVER_LLC; + } + + // + // Process the SABME. + // + + Link->LinkBusy = FALSE; // he's cleared his busy condition. + + switch (Link->State) { + + case LINK_STATE_ADM: + + // + // Remote station is initiating this link. Send UA and wait for + // his checkpoint before setting READY state. + // + + // Moving out of ADM, add special reference + NbfReferenceLinkSpecial("Waiting for Poll", Link, LREF_NOT_ADM); + + Link->State = LINK_STATE_W_POLL; // wait for RR-c/p. + + // Don't start T1, but prepare for timing the response + FakeStartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); + + NbfResetLink (Link); + NbfSendUa (Link, PollFinal); // releases lock + IF_NBFDBG (NBF_DEBUG_SETUP) { + NbfPrint4("ADM SABME on %lx %x-%x-%x\n", + Link, + Link->HardwareAddress.Address[3], + Link->HardwareAddress.Address[4], + Link->HardwareAddress.Address[5]); + } + StartTi (Link); +#if DBG + s = "ADM"; +#endif + break; + + case LINK_STATE_CONNECTING: + + // + // We've sent a SABME and are waiting for a UA. He's sent a + // SABME at the same time, so we tried to do it at the same time. + // The only balanced thing we can do at this time is to respond + // with UA and duplicate the effort. To not send anything would + // be bad because the link would never complete. + // + + // + // BUGBUG: What about timers here? + // + + Link->State = LINK_STATE_W_POLL; // wait for RR-c/p. + NbfSendUa (Link, PollFinal); // releases lock + StartTi (Link); +#if DBG + s = "CONNECTING"; +#endif + break; + + case LINK_STATE_W_POLL: + + // + // We're waiting for his initial poll on a RR-c/p. Instead, we + // got a SABME, so this is really a link reset. + // + // Unfortunately, if we happen to get two SABMEs + // and respond to the second one with another UA + // (when he has sent the RR-c/p and is expecting + // an RR-r/f), he will send a FRMR. So, we ignore + // this frame. + // + + // Link->State = LINK_STATE_W_POLL; // wait for RR-c/p. + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + StartTi(Link); +#if DBG + s = "W_POLL"; +#endif + break; + + case LINK_STATE_READY: + + // + // Link is already balanced. He's resetting the link. + // + + case LINK_STATE_W_FINAL: + + // + // We're waiting for a RR-r/f from the remote guy but instead + // he sent this SABME. We have to assume he wants to reset the link. + // + + case LINK_STATE_W_DISC_RSP: + + // + // We're waiting for a response from our DISC-c/p but instead of + // a UA-r/f, we got this SABME. He wants to initiate the link + // again because a transport connection has been initiated while + // we were taking the link down. + // + + Link->State = LINK_STATE_W_POLL; // wait for RR-c/p. + + // Don't start T1, but prepare for timing the response + FakeStartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); + + NbfResetLink (Link); // reset this connection. + NbfSendUa (Link, PollFinal); // releases lock. + StartTi(Link); +#if DBG + s = "READY/W_FINAL/W_DISC_RSP"; +#endif + break; + + default: + + ASSERT (FALSE); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "Unknown link state"; +#endif + + } /* switch */ + +#if DBG + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 (" NbfProcessSabme: Processed, State was %s.\n", s); + } +#endif + +} /* NbfProcessSabme */ + + +VOID +NbfProcessUa( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PDLC_U_FRAME Header + ) + +/*++ + +Routine Description: + + This routine processes a received UA frame. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC U-type frame. + +Return Value: + + none. + +--*/ + +{ +#if DBG + UCHAR *s; +#endif + + PollFinal, Header; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessUa: Entered.\n"); + } + + // + // Format must be: UA-r/x, on a real link object. + // + + if (Command || (Link == NULL)) { + return; + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + + Link->LinkBusy = FALSE; // he's cleared his busy condition. + + switch (Link->State) { + + case LINK_STATE_ADM: + + // + // Received an unnumbered acknowlegement while in ADM. Somehow + // the remote station is confused, so tell him we're disconnected. + // + + NbfSendDm (Link, FALSE); // indicate we're disconnected, release lock +#if DBG + s = "ADM"; +#endif + break; + + case LINK_STATE_CONNECTING: + + // + // We've sent a SABME and have just received the UA. + // + + UpdateBaseT1Timeout (Link); // got response to poll. + + Link->State = LINK_STATE_W_FINAL; // wait for RR-r/f. + Link->SendRetries = (UCHAR)Link->LlcRetries; + NbfSendRr (Link, TRUE, TRUE); // send RR-c/p, StartT1, release lock +#if DBG + s = "CONNECTING"; +#endif + break; + + case LINK_STATE_READY: + + // + // Link is already balanced. He's confused; throw it away. + // + + case LINK_STATE_W_POLL: + + // + // We're waiting for his initial poll on a RR-c/p. Instead, we + // got a UA, so he is confused. Throw it away. + // + + case LINK_STATE_W_FINAL: + + // + // We're waiting for a RR-r/f from the remote guy but instead + // he sent this UA. He is confused. Throw it away. + // + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "READY/W_POLL/W_FINAL"; +#endif + break; + + case LINK_STATE_W_DISC_RSP: + + // + // We've sent a DISC-c/p and have received the correct response. + // Disconnect this link. + // + + Link->State = LINK_STATE_ADM; // completed disconnection. + + // + // This is the normal "clean" disconnect path, so we stop + // all the timers here since we won't call NbfStopLink. + // + + StopT1 (Link); + StopT2 (Link); + StopTi (Link); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + // Moving to ADM, dereference link + NbfDereferenceLinkSpecial ("Got UA for DISC", Link, LREF_NOT_ADM); // decrement link's last ref. + +#if DBG + s = "W_DISC_RSP"; +#endif + break; + + default: + + ASSERT (FALSE); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "Unknown link state"; +#endif + + } /* switch */ + +} /* NbfProcessUa */ + + +VOID +NbfProcessDisc( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PDLC_U_FRAME Header + ) + +/*++ + +Routine Description: + + This routine processes a received DISC frame. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC U-type frame. + +Return Value: + + none. + +--*/ + +{ +#if DBG + UCHAR *s; +#endif + + Header; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessDisc: Entered.\n"); + } + + // + // Format must be: DISC-c/x, on a real link object. + // + + if (!Command || (Link == NULL)) { + return; + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + + Link->LinkBusy = FALSE; // he's cleared his busy condition. + + switch (Link->State) { + + case LINK_STATE_ADM: + + // + // Received a DISC while in ADM. Simply report disconnected mode. + // + +#if DBG + s = "ADM"; +#endif + NbfSendDm (Link, PollFinal); // indicate we're disconnected, release lock + break; + + case LINK_STATE_READY: + + // + // Link is balanced. Kill the link. + // + + Link->State = LINK_STATE_ADM; // we're reset now. + NbfSendUa (Link, PollFinal); // Send UA-r/x, release lock +#if DBG + if (NbfDisconnectDebug) { + NbfPrint0( "NbfProcessDisc calling NbfStopLink\n" ); + } +#endif + NbfStopLink (Link); // say goodnight, gracie + + // Moving to ADM, remove reference + NbfDereferenceLinkSpecial("Stopping link", Link, LREF_NOT_ADM); + +#if DBG + s = "READY"; +#endif + break; + + case LINK_STATE_CONNECTING: + + // + // We've sent a SABME and have just received a DISC. That means + // we have crossed a disconnection and reconnection. Throw away + // the disconnect. + // + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "CONNECTING"; +#endif + break; + + case LINK_STATE_W_POLL: + + // + // We're waiting for his initial poll on a RR-c/p. Instead, we + // got a DISC, so he wants to drop the link. + // + + case LINK_STATE_W_FINAL: + + // + // We're waiting for a RR-r/f from the remote guy but instead + // he sent DISC, so he wants to drop the link. + // + + case LINK_STATE_W_DISC_RSP: + + // + // We've sent a DISC-c/p and have received a DISC from him as well. + // Disconnect this link. + // + + Link->State = LINK_STATE_ADM; // we're reset now. + NbfSendUa (Link, PollFinal); // Send UA-r/x, release lock. + + NbfStopLink (Link); + + // moving to ADM, remove reference + NbfDereferenceLinkSpecial ("Got DISC on W_DIS_RSP", Link, LREF_NOT_ADM); // remove its "alive" ref. + +#if DBG + s = "W_POLL/W_FINAL/W_DISC_RSP"; +#endif + break; + + default: + + ASSERT (FALSE); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "Unknown link state"; +#endif + + } /* switch */ + +} /* NbfProcessDisc */ + + +VOID +NbfProcessDm( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PDLC_U_FRAME Header + ) + +/*++ + +Routine Description: + + This routine processes a received DM frame. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC U-type frame. + +Return Value: + + none. + +--*/ + +{ +#if DBG + UCHAR *s; +#endif + + Header; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessDm: Entered.\n"); + } + + // + // Format must be: DM-r/x, on a real link object. + // + + if (Command || (Link == NULL)) { + return; + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + + Link->LinkBusy = FALSE; // he's cleared his busy condition. + + switch (Link->State) { + + case LINK_STATE_ADM: + + // + // Received a DM while in ADM. Do nothing. + // + + case LINK_STATE_CONNECTING: + + // + // We've sent a SABME and have just received a DM. That means + // we have crossed a disconnection and reconnection. Throw away + // the disconnect mode indicator, we will reconnect in time. + // + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "ADM/CONNECTING"; +#endif + break; + + case LINK_STATE_READY: + + // + // Link is balanced and he is in ADM, so we have to shut down. + // + +#if DBG + if (NbfDisconnectDebug) { + NbfPrint0( "NbfProcessDm calling NbfStopLink\n" ); + } +#endif + + case LINK_STATE_W_POLL: + + // + // We're waiting for his initial poll on a RR-c/p. Instead, we + // got a DM, so he has dropped the link. + // + + case LINK_STATE_W_FINAL: + + // + // We're waiting for a RR-r/f from the remote guy but instead + // he sent DM, so he has already dropped the link. + // + + case LINK_STATE_W_DISC_RSP: + + // + // We've sent a DISC-c/p and have received a DM from him, indicating + // that he is now in ADM. While technically not what we expected, + // this protocol is commonly used in place of UA-r/f, so just treat + // as though we got a UA-r/f. Disconnect the link normally. + // + + Link->State = LINK_STATE_ADM; // we're reset now. + NbfSendDm (Link, FALSE); // indicate disconnected, release lock + + NbfStopLink (Link); + + // moving to ADM, remove reference + NbfDereferenceLinkSpecial ("Got DM in W_DISC_RSP", Link, LREF_NOT_ADM); // remove its "alive" ref. + +#if DBG + s = "READY/W_FINAL/W_POLL/W_DISC_RSP"; +#endif + break; + + default: + + ASSERT (FALSE); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "Unknown link state"; +#endif + + } /* switch */ + +} /* NbfProcessDm */ + + +VOID +NbfProcessFrmr( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PDLC_U_FRAME Header + ) + +/*++ + +Routine Description: + + This routine processes a received FRMR response frame. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC U-type frame. + +Return Value: + + none. + +--*/ + +{ +#if DBG + UCHAR *s; +#endif + ULONG DumpData[6]; + + PollFinal, Header; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessFrmr: Entered.\n"); + } + + // + // Log an error, this shouldn't happen. + // + + DumpData[0] = Link->State; + DumpData[1] = Link->Flags; + DumpData[2] = (Header->Information.FrmrInfo.Command << 8) + + (Header->Information.FrmrInfo.Ctrl); + DumpData[3] = (Header->Information.FrmrInfo.Vs << 16) + + (Header->Information.FrmrInfo.Vr << 8) + + (Header->Information.FrmrInfo.Reason); + DumpData[4] = (Link->SendState << 24) + + (Link->NextSend << 16) + + (Link->LastAckReceived << 8) + + (Link->SendWindowSize); + DumpData[5] = (Link->ReceiveState << 24) + + (Link->NextReceive << 16) + + (Link->LastAckSent << 8) + + (Link->ReceiveWindowSize); + + NbfWriteGeneralErrorLog( + Link->Provider, + EVENT_TRANSPORT_BAD_PROTOCOL, + 1, + STATUS_LINK_FAILED, + L"FRMR", + 6, + DumpData); + + + ++Link->Provider->FrmrReceived; + + // + // Format must be: FRMR-r/x, on a real link object. + // + + if (Command || (Link == NULL)) { + return; + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + + switch (Link->State) { + + case LINK_STATE_ADM: + + // + // Received a FRMR while in ADM. Report disconnected mode. + // + +#if DBG + s = "ADM"; +#endif + NbfSendDm (Link, FALSE); // indicate disconnected, release lock + break; + + case LINK_STATE_READY: + + // + // Link is balanced and he reported a protocol error. + // +#if 0 + // We want to reset the link, but not quite as fully + // as NbfResetLink. This code is the same as what + // is in there, except for: + // + // - resetting the send/receive numbers + // - removing packets from the WackQ + // + + StopT1 (Link); + StopT2 (Link); + Link->Flags &= LINK_FLAGS_DEFERRED_MASK; // keep deferred operations + + Link->SendWindowSize = 1; + Link->LinkBusy = FALSE; + + Link->ReceiveWindowSize = 1; + Link->WindowErrors = 0; + Link->BestWindowSize = 1; + Link->WorstWindowSize = Link->MaxWindowSize; + Link->Flags |= LINK_FLAGS_JUMP_START; + + Link->CurrentT1Timeout = Link->Provider->DefaultT1Timeout; + Link->BaseT1Timeout = Link->Provider->DefaultT1Timeout << DLC_TIMER_ACCURACY; + Link->CurrentPollRetransmits = 0; + Link->CurrentPollOutstanding = FALSE; + Link->T2Timeout = Link->Provider->DefaultT2Timeout; + Link->TiTimeout = Link->Provider->DefaultTiTimeout; + Link->LlcRetries = Link->Provider->LlcRetries; + Link->MaxWindowSize = Link->Provider->LlcMaxWindowSize; + + // + // The rest is similar to NbfActivateLink + // + + Link->State = LINK_STATE_CONNECTING; + Link->SendState = SEND_STATE_DOWN; + Link->ReceiveState = RECEIVE_STATE_DOWN; + Link->SendRetries = (UCHAR)Link->LlcRetries; + + NbfSendSabme (Link, TRUE); // send SABME/p, StartT1, release lock +#else + Link->State = LINK_STATE_ADM; // we're reset now. + NbfSendDm (Link, FALSE); // indicate disconnected, release lock + + NbfStopLink (Link); + + // moving to ADM, remove reference + NbfDereferenceLinkSpecial("Got DM in W_POLL", Link, LREF_NOT_ADM); +#endif + +#if DBG + NbfPrint1("Received FRMR on link %lx\n", Link); +#endif + +#if DBG + s = "READY"; +#endif + break; + + case LINK_STATE_CONNECTING: + + // + // We've sent a SABME and have just received a FRMR. + // + + case LINK_STATE_W_POLL: + + // + // We're waiting for his initial poll on a RR-c/p. Instead, we + // got a FRMR. + // + + case LINK_STATE_W_FINAL: + + // + // We're waiting for a RR-r/f from the remote guy but instead + // he sent FRMR. + // + + case LINK_STATE_W_DISC_RSP: + + // + // We've sent a DISC-c/p and have received a FRMR. + // + + Link->State = LINK_STATE_ADM; // we're reset now. + NbfSendDm (Link, FALSE); // indicate disconnected, release lock + + // moving to ADM, remove reference + NbfDereferenceLinkSpecial("Got DM in W_POLL", Link, LREF_NOT_ADM); + +#if DBG + s = "CONN/W_POLL/W_FINAL/W_DISC_RSP"; +#endif + break; + + default: + + ASSERT (FALSE); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "Unknown link state"; +#endif + + } /* switch */ + +} /* NbfProcessFrmr */ + + +VOID +NbfProcessTest( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PDLC_U_FRAME Header + ) + +/*++ + +Routine Description: + + This routine processes a received TEST frame. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC U-type frame. + +Return Value: + + none. + +--*/ + +{ + Header, PollFinal; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessTest: Entered.\n"); + } + + // + // Process only: TEST-c/x. + // + + // BUGBUG: respond to TEST on a link that is NULL. + + if (!Command || (Link == NULL)) { + return; + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + +#if DBG + PANIC ("NbfSendTest: Received Test Packet, not processing....\n"); +#endif + //NbfSendTest (Link, FALSE, PollFinal, Psdu); + +} /* NbfProcessTest */ + + +VOID +NbfProcessXid( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PDLC_U_FRAME Header + ) + +/*++ + +Routine Description: + + This routine processes a received XID frame. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC U-type frame. + +Return Value: + + none. + +--*/ + +{ + Header; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessXid: Entered.\n"); + } + + // + // Process only: XID-c/x. + // + + // BUGBUG: respond to XID with a link that is NULL. + + if (!Command || (Link == NULL)) { + return; + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + + NbfSendXid (Link, FALSE, PollFinal); // releases lock + +} /* NbfProcessXid */ + + +VOID +NbfProcessSFrame( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PDLC_S_FRAME Header, + IN UCHAR CommandByte + ) + +/*++ + +Routine Description: + + This routine processes a received RR, RNR, or REJ frame. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC S-type frame. + + CommandByte - The command byte of the frame (RR, RNR, or REJ). + +Return Value: + + none. + +--*/ + +{ +#if DBG + UCHAR *s; +#endif + BOOLEAN Resend; + BOOLEAN AckedPackets; + UCHAR AckSequenceNumber; + UCHAR OldLinkSendRetries; + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint2 (" NbfProcessSFrame %s: Entered, Link: %lx\n", Link, + Command == DLC_CMD_RR ? "RR" : (Command == DLC_CMD_RNR ? "RNR" : "REJ")); + } + + // + // Process any of: RR-x/x, RNR-x/x, REJ-x/x + // + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + + if (CommandByte == DLC_CMD_RNR) { + Link->LinkBusy = TRUE; // he's set a busy condition. + } else { + Link->LinkBusy = FALSE; // busy condition cleared. + } + + switch (Link->State) { + + case LINK_STATE_ADM: + + // + // We're disconnected. Tell him. + // + +#if DBG + s = "ADM"; +#endif + NbfSendDm (Link, PollFinal); // releases lock + break; + + case LINK_STATE_READY: + + // + // Link is balanced. Note that the sections below surrounded by + // if (Command && PollFinal) variants are all disjoint sets. + // That's the only reason the Spinlock stuff works right. DO NOT + // attempt to fiddle this unless you figure out the locking first! + // + + // + // If the AckSequenceNumber is not valid, ignore it. The + // number should be between the first packet on the WackQ + // and one more than the last packet. These correspond to + // Link->LastAckReceived and Link->NextSend. + // + + AckSequenceNumber = (UCHAR)(Header->RcvSeq >> 1); + + if (Link->NextSend >= Link->LastAckReceived) { + + // + // There is no 127 -> 0 wrap between the two... + // + + if ((AckSequenceNumber < Link->LastAckReceived) || + (AckSequenceNumber > Link->NextSend)) { + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + DbgPrint("NbfResendLlcPackets: %.2x-%.2x-%.2x-%.2x-%.2x-%.2x Unexpected N(R) %d, LastAck %d NextSend %d\n", + Link->HardwareAddress.Address[0], + Link->HardwareAddress.Address[1], + Link->HardwareAddress.Address[2], + Link->HardwareAddress.Address[3], + Link->HardwareAddress.Address[4], + Link->HardwareAddress.Address[5], + AckSequenceNumber, Link->LastAckReceived, Link->NextSend); +#endif + break; + + } + + } else { + + // + // There is a 127 -> 0 wrap between the two... + // + + if ((AckSequenceNumber < Link->LastAckReceived) && + (AckSequenceNumber > Link->NextSend)) { + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + DbgPrint("NbfResendLlcPackets: %.2x-%.2x-%.2x-%.2x-%.2x-%.2x Unexpected N(R) %d, LastAck %d NextSend %d\n", + Link->HardwareAddress.Address[0], + Link->HardwareAddress.Address[1], + Link->HardwareAddress.Address[2], + Link->HardwareAddress.Address[3], + Link->HardwareAddress.Address[4], + Link->HardwareAddress.Address[5], + AckSequenceNumber, Link->LastAckReceived, Link->NextSend); +#endif + break; + + } + + } + + + // + // We always resend on a REJ, and never on an RNR; + // for an RR we may change Resend to TRUE below. + // If we get a REJ on a WAN line (T1 is more than + // five seconds) then we pretend this was a final + // so we will resend even if a poll was outstanding. + // + + if (CommandByte == DLC_CMD_REJ) { + Resend = TRUE; + if (Link->CurrentT1Timeout >= ((5 * SECONDS) / SHORT_TIMER_DELTA)) { + PollFinal = TRUE; + } + OldLinkSendRetries = (UCHAR)Link->SendRetries; + } else { + Resend = FALSE; + } + + +#if 0 + // + // If we've got a request with no poll, must have fired T2 on + // the other side (or, if the other side is OS/2, he lost a + // packet and knows it or is telling me to lower the window size). + // In the T2 case, we've Acked current stuff, mark the window as + // needing adjustment at some future time. In the OS/2 cases, this + // is also the right thing to do. + // + + if ((!Command) && (!PollFinal)) { + ; + } +#endif + + if (PollFinal) { + + if (Command) { + + // + // If he is checkpointing, then we must respond with RR-r/f to + // update him on the status of our reception of his I-frames. + // + + StopT2 (Link); // we acked some I-frames. + NbfSendRr (Link, FALSE, PollFinal); // releases lock + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + } else { + + // + // If we were checkpointing, and he has sent an RR-r/f, we + // can clear the checkpoint condition. Any packets which + // are still waiting for acknowlegement at this point must + // now be resent. + // + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessRr: he's responded to our checkpoint.\n"); + } + if (Link->SendState != SEND_STATE_CHECKPOINTING) { + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessRr: BUGBUG: not ckpting, but final received.\n"); + } + } else if (CommandByte == DLC_CMD_RR) { + OldLinkSendRetries = (UCHAR)Link->SendRetries; + Resend = TRUE; + UpdateBaseT1Timeout (Link); // gor response to poll + } + StopT1 (Link); // checkpointing finished. + Link->SendRetries = (UCHAR)Link->LlcRetries; + Link->SendState = SEND_STATE_READY; + StartTi (Link); + } + } + + // + // NOTE: The link spinlock is held here. + // + + // + // The N(R) in this frame acknowleges some (or all) of our packets. + // We'll ack packets on our send queue if this is a final when we + // call Resend. This call must come after the checkpoint + // acknowlegement check so that an RR-r/f is always sent BEFORE + // any new I-frames. This allows us to always send I-frames as + // commands. + // + + if (Link->WackQ.Flink != &Link->WackQ) { + + // + // NOTE: ResendLlcPackets may release and reacquire + // the link spinlock. + // + + AckedPackets = ResendLlcPackets( + Link, + AckSequenceNumber, + Resend); + + if (Resend && (!AckedPackets) && (Link->State == LINK_STATE_READY)) { + + // + // To prevent stalling, pretend this RR wasn't + // received. + // + + if (OldLinkSendRetries == 1) { + + CancelT1Timeout (Link); // we are stopping a polling state + + Link->State = LINK_STATE_W_DISC_RSP; // we are awaiting a DISC/f. + Link->SendState = SEND_STATE_DOWN; + Link->ReceiveState = RECEIVE_STATE_DOWN; + Link->SendRetries = (UCHAR)Link->LlcRetries; + +#if DBG + DbgPrint ("NBF: No ack teardown of %lx\n", Link); +#endif + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + NbfStopLink (Link); + + StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); // retransmit timer. + NbfSendDisc (Link, TRUE); // send DISC-c/p. + + } else { + + StopTi (Link); + Link->SendRetries = OldLinkSendRetries-1; + + if (Link->SendState != SEND_STATE_CHECKPOINTING) { + Link->SendState = SEND_STATE_CHECKPOINTING; + NbfSendRr (Link, TRUE, TRUE);// send RR-c/p, StartT1, release lock + } else { + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + } + + } +#if DBG + s = "READY"; +#endif + break; // No need to RestartLinkTraffic + + } else if (AckedPackets) { + + Link->SendRetries = (UCHAR)Link->LlcRetries; + + } + + } + + + // + // If the link send state is READY, get the link going + // again. + // + // NOTE: RestartLinkTraffic releases the link spinlock. + // + + if (Link->SendState == SEND_STATE_READY) { + RestartLinkTraffic (Link); + } else { + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + } +#if DBG + s = "READY"; +#endif + break; + + case LINK_STATE_CONNECTING: + + // + // We've sent a SABME and are waiting for a UA. He's sent a + // RR too early, so just let the SABME timeout. + // + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "CONNECTING"; +#endif + break; + + case LINK_STATE_W_POLL: + + // + // We're waiting for his initial poll on a RR-c/p. If he just + // sends something without a poll, we'll let that get by. + // + + if (!Command) { + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "W_POLL"; +#endif + break; // don't allow this protocol. + } + Link->State = LINK_STATE_READY; // we're up! + + FakeUpdateBaseT1Timeout (Link); + NbfSendRr (Link, FALSE, PollFinal); // send RR-r/x, release lock + NbfCompleteLink (Link); // fire up the connections. + IF_NBFDBG (NBF_DEBUG_SETUP) { + NbfPrint4("W_POLL RR on %lx %x-%x-%x\n", + Link, + Link->HardwareAddress.Address[3], + Link->HardwareAddress.Address[4], + Link->HardwareAddress.Address[5]); + } + StartTi (Link); + +#if DBG + s = "W_POLL"; +#endif + break; + + case LINK_STATE_W_FINAL: + + // + // We're waiting for a RR-r/f from the remote guy. + // + + if (Command || !PollFinal) { // wait for final. + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "W_FINAL"; +#endif + break; // we sent RR-c/p. + } + Link->State = LINK_STATE_READY; // we're up. + StopT1 (Link); // poll was acknowleged. + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + NbfCompleteLink (Link); // fire up the connections. + StartTi (Link); +#if DBG + s = "W_FINAL"; +#endif + break; + + case LINK_STATE_W_DISC_RSP: + + // + // We're waiting for a response from our DISC-c/p but instead of + // a UA-r/f, we got this RR. Throw the packet away. + // + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "W_DISC_RSP"; +#endif + break; + + default: + + ASSERT (FALSE); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "Unknown link state"; +#endif + + } /* switch */ + +#if DBG + if (CommandByte == DLC_CMD_REJ) { + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 (" NbfProcessRej: (%s) REJ received.\n", s); + } + } +#endif + +} /* NbfProcessSFrame */ + + +VOID +NbfInsertInLoopbackQueue ( + IN PDEVICE_CONTEXT DeviceContext, + IN PNDIS_PACKET NdisPacket, + IN UCHAR LinkIndex + ) + +/*++ + +Routine Description: + + This routine places a packet on the loopback queue, and + queues a DPC to do the indication if needed. + +Arguments: + + DeviceContext - The device context in question. + + NdisPacket - The packet to place on the loopback queue. + + LinkIndex - The index of the loopback link to indicate to. + +Return Value: + + None: + +--*/ + +{ + PSEND_PACKET_TAG SendPacketTag; + KIRQL oldirql; + + SendPacketTag = (PSEND_PACKET_TAG)NdisPacket->ProtocolReserved; + SendPacketTag->OnLoopbackQueue = TRUE; + + SendPacketTag->LoopbackLinkIndex = LinkIndex; + + ACQUIRE_SPIN_LOCK(&DeviceContext->LoopbackSpinLock, &oldirql); + + InsertTailList(&DeviceContext->LoopbackQueue, &SendPacketTag->Linkage); + + if (!DeviceContext->LoopbackInProgress) { + + KeInsertQueueDpc(&DeviceContext->LoopbackDpc, NULL, NULL); + DeviceContext->LoopbackInProgress = TRUE; + + } + + RELEASE_SPIN_LOCK (&DeviceContext->LoopbackSpinLock, oldirql); + +} + + +VOID +NbfProcessLoopbackQueue ( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) + +/*++ + +Routine Description: + + This is the DPC routine which processes items on the + loopback queue. It processes a single request off the + queue (if there is one), then if there is another request + it requeues itself. + +Arguments: + + Dpc - The system DPC object. + + DeferredContext - A pointer to the device context. + + SystemArgument1, SystemArgument2 - Not used. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + PNDIS_PACKET NdisPacket; + PNDIS_BUFFER FirstBuffer; + PVOID LookaheadBuffer; + UINT LookaheadBufferSize; + UINT BytesCopied; + UINT PacketSize; + ULONG HeaderLength; + PTP_LINK Link; + PSEND_PACKET_TAG SendPacketTag; + PLIST_ENTRY p; + + + UNREFERENCED_PARAMETER(Dpc); + UNREFERENCED_PARAMETER(SystemArgument1); + UNREFERENCED_PARAMETER(SystemArgument2); + + + DeviceContext = (PDEVICE_CONTEXT)DeferredContext; + + ACQUIRE_DPC_SPIN_LOCK(&DeviceContext->LoopbackSpinLock); + + if (!IsListEmpty(&DeviceContext->LoopbackQueue)) { + + p = RemoveHeadList(&DeviceContext->LoopbackQueue); + + // + // BUGBUG: This depends on the fact that the Linkage field is + // the first one in ProtocolReserved. + // + + NdisPacket = CONTAINING_RECORD(p, NDIS_PACKET, ProtocolReserved[0]); + + SendPacketTag = (PSEND_PACKET_TAG)NdisPacket->ProtocolReserved; + SendPacketTag->OnLoopbackQueue = FALSE; + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LoopbackSpinLock); + + + // + // Determine the data needed to indicate. We don't guarantee + // that we will have the correct lookahead length, but since + // we know that any header we prepend is a single piece, + // and that's all we'll have to look at in an indicated packet, + // that's not a concern. + // + // Unfortunately that last paragraph is bogus since for + // indications to our client we need more data... + // + + NdisQueryPacket(NdisPacket, NULL, NULL, &FirstBuffer, &PacketSize); + + NdisQueryBuffer(FirstBuffer, &LookaheadBuffer, &LookaheadBufferSize); + + if ((LookaheadBufferSize != PacketSize) && + (LookaheadBufferSize < NBF_MAX_LOOPBACK_LOOKAHEAD)) { + + // + // There is not enough contiguous data in the + // packet's first buffer, so we merge it into + // DeviceContext->LookaheadContiguous. + // + + if (PacketSize > NBF_MAX_LOOPBACK_LOOKAHEAD) { + LookaheadBufferSize = NBF_MAX_LOOPBACK_LOOKAHEAD; + } else { + LookaheadBufferSize = PacketSize; + } + + NbfCopyFromPacketToBuffer( + NdisPacket, + 0, + LookaheadBufferSize, + DeviceContext->LookaheadContiguous, + &BytesCopied); + + ASSERT (BytesCopied == LookaheadBufferSize); + + LookaheadBuffer = DeviceContext->LookaheadContiguous; + + } + + + // + // Now determine which link to loop it back to; + // UI frames are not indicated on any link. + // + + SendPacketTag = (PSEND_PACKET_TAG)NdisPacket->ProtocolReserved; + + // + // Hold DeviceContext->LinkSpinLock until we get a + // reference. + // + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + switch (SendPacketTag->LoopbackLinkIndex) { + + case LOOPBACK_TO_CONNECTOR: + + Link = DeviceContext->LoopbackLinks[CONNECTOR_LINK]; + break; + + case LOOPBACK_TO_LISTENER: + + Link = DeviceContext->LoopbackLinks[LISTENER_LINK]; + break; + + case LOOPBACK_UI_FRAME: + default: + + Link = (PTP_LINK)NULL; + break; + + } + + // + // For non-null links, we have to reference them. + // We use LREF_TREE since that is what + // NbfGeneralReceiveHandler expects. + // + + if (Link != (PTP_LINK)NULL) { + NbfReferenceLink("loopback indication", Link, LREF_TREE); + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + MacReturnHeaderLength( + &DeviceContext->MacInfo, + LookaheadBuffer, + &HeaderLength); + + DeviceContext->LoopbackHeaderLength = HeaderLength; + + // + // Process the receive like any other. We don't have to + // worry about frame padding since we construct the + // frame ourselves. + // + + NbfGeneralReceiveHandler( + DeviceContext, + (NDIS_HANDLE)NdisPacket, + &DeviceContext->LocalAddress, // since it is loopback + Link, + LookaheadBuffer, // header + PacketSize - HeaderLength, // total packet size + (PDLC_FRAME)((PUCHAR)LookaheadBuffer + HeaderLength), // l/a data + LookaheadBufferSize - HeaderLength, // lookahead data length + TRUE + ); + + + // + // Now complete the send. + // + + NbfSendCompletionHandler( + DeviceContext->NdisBindingHandle, + NdisPacket, + NDIS_STATUS_SUCCESS + ); + + + ACQUIRE_DPC_SPIN_LOCK(&DeviceContext->LoopbackSpinLock); + + if (!IsListEmpty(&DeviceContext->LoopbackQueue)) { + + KeInsertQueueDpc(&DeviceContext->LoopbackDpc, NULL, NULL); + + // + // Remove these two lines if it is decided thet + // NbfReceiveComplete should be called after every + // loopback indication. + // + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LoopbackSpinLock); + return; + + } else { + + DeviceContext->LoopbackInProgress = FALSE; + + } + + } else { + + // + // This shouldn't happen! + // + + DeviceContext->LoopbackInProgress = FALSE; + +#if DBG + NbfPrint1("Loopback queue empty for device context %x\n", DeviceContext); +#endif + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LoopbackSpinLock); + + NbfReceiveComplete( + (NDIS_HANDLE)DeviceContext + ); + +} /* NbfProcessLoopbackQueue */ + + +NDIS_STATUS +NbfReceiveIndication ( + IN NDIS_HANDLE BindingContext, + IN NDIS_HANDLE ReceiveContext, + IN PVOID HeaderBuffer, + IN UINT HeaderBufferSize, + IN PVOID LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that a frame has been received on the physical link. + This routine is time critical, so we only allocate a + buffer and copy the packet into it. We also perform minimal + validation on this packet. It gets queued to the device context + to allow for processing later. + +Arguments: + + BindingContext - The Adapter Binding specified at initialization time. + + ReceiveContext - A magic cookie for the MAC. + + HeaderBuffer - pointer to a buffer containing the packet header. + + HeaderBufferSize - the size of the header. + + LookaheadBuffer - pointer to a buffer containing the negotiated minimum + amount of buffer I get to look at (not including header). + + LookaheadBufferSize - the size of the above. May be less than asked + for, if that's all there is. + + PacketSize - Overall size of the packet (not including header). + +Return Value: + + NDIS_STATUS - status of operation, one of: + + NDIS_STATUS_SUCCESS if packet accepted, + NDIS_STATUS_NOT_RECOGNIZED if not recognized by protocol, + NDIS_any_other_thing if I understand, but can't handle. + +--*/ +{ + PDEVICE_CONTEXT DeviceContext; + KIRQL oldirql; + PTP_LINK Link; + HARDWARE_ADDRESS SourceAddressBuffer; + PHARDWARE_ADDRESS SourceAddress; + UINT RealPacketSize; + PDLC_FRAME DlcHeader; + BOOLEAN Multicast; + + ENTER_NBF; + + IF_NBFDBG (NBF_DEBUG_NDIS) { + PUCHAR p; + SHORT i; + NbfPrint2 ("NbfReceiveIndication: Packet, Size: 0x0%lx LookaheadSize: 0x0%lx\n 00:", + PacketSize, LookaheadBufferSize); + p = (PUCHAR)LookaheadBuffer; + for (i=0;i<25;i++) { + NbfPrint1 (" %2x",p[i]); + } + NbfPrint0 ("\n"); + } + + DeviceContext = (PDEVICE_CONTEXT)BindingContext; + + RealPacketSize = 0; + + // + // Obtain the packet length; this may optionally adjust + // the lookahead buffer forward if the header we wish + // to remove spills over into what the MAC considers + // data. If it determines that the header is not + // valid, it keeps RealPacketSize at 0. + // + + MacReturnPacketLength( + &DeviceContext->MacInfo, + HeaderBuffer, + HeaderBufferSize, + PacketSize, + &RealPacketSize, + &LookaheadBuffer, + &LookaheadBufferSize + ); + + if (RealPacketSize < 2) { + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint1 ("NbfReceiveIndication: Discarding packet, bad length %lx\n", + HeaderBuffer); + } + return NDIS_STATUS_NOT_RECOGNIZED; + } + + // + // We've negotiated at least a contiguous DLC header passed back in the + // lookahead buffer. Check it to see if we want this packet. + // + + DlcHeader = (PDLC_FRAME)LookaheadBuffer; + + if (((*(USHORT UNALIGNED *)(&DlcHeader->Dsap)) & + (USHORT)((DLC_SSAP_MASK << 8) | DLC_DSAP_MASK)) != + (USHORT)((DSAP_NETBIOS_OVER_LLC << 8) | DSAP_NETBIOS_OVER_LLC)) { + + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint1 ("NbfReceiveIndication: Discarding lookahead data, not NetBIOS: %lx\n", + LookaheadBuffer); + } + LEAVE_NBF; + return NDIS_STATUS_NOT_RECOGNIZED; // packet was processed. + } + + + // + // Check that the packet is not too long. + // + + if (PacketSize > DeviceContext->MaxReceivePacketSize) { +#if DBG + NbfPrint2("NbfReceiveIndication: Ignoring packet length %d, max %d\n", + PacketSize, DeviceContext->MaxReceivePacketSize); +#endif + return NDIS_STATUS_NOT_RECOGNIZED; + } + + MacReturnSourceAddress( + &DeviceContext->MacInfo, + HeaderBuffer, + &SourceAddressBuffer, + &SourceAddress, + &Multicast + ); + + // + // Record how many multicast packets we get, to monitor + // general network activity. + // + + if (Multicast) { + ++DeviceContext->MulticastPacketCount; + } + + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + // + // Unless this is a UI frame find the Link this packet belongs to. + // If there is not a recognized link, pass the frame on to be handled + // by the receive complete code. + // + + if ((((PDLC_U_FRAME)LookaheadBuffer)->Command) != DLC_CMD_UI) { + + // This adds a link reference if it is found + + Link = NbfFindLink (DeviceContext, SourceAddress->Address); + + if (Link != NULL) { + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 ("NbfReceiveIndication: Found link, Link: %lx\n", Link); + } + + } + + } else { + + Link = NULL; + + } + + + NbfGeneralReceiveHandler( + DeviceContext, + ReceiveContext, + SourceAddress, + Link, + HeaderBuffer, // header + RealPacketSize, // total data length in packet + (PDLC_FRAME)LookaheadBuffer, // lookahead data + LookaheadBufferSize, // lookahead data length + FALSE // not loopback + ); + + KeLowerIrql (oldirql); + + return STATUS_SUCCESS; + +} /* NbfReceiveIndication */ + + +VOID +NbfGeneralReceiveHandler ( + IN PDEVICE_CONTEXT DeviceContext, + IN NDIS_HANDLE ReceiveContext, + IN PHARDWARE_ADDRESS SourceAddress, + IN PTP_LINK Link, + IN PVOID HeaderBuffer, + IN UINT PacketSize, + IN PDLC_FRAME DlcHeader, + IN UINT DlcSize, + IN BOOLEAN Loopback + ) + +/*++ + +Routine Description: + + This routine receives control from either NbfReceiveIndication + or NbfProcessLoopbackQueue. It continues the processing of + indicated data once the link has been determined. + + This routine is time critical, so we only allocate a + buffer and copy the packet into it. We also perform minimal + validation on this packet. It gets queued to the device context + to allow for processing later. + +Arguments: + + DeviceContext - The device context of this adapter. + + ReceiveContext - A magic cookie for the MAC. + + SourceAddress - The source address of the packet. + + Link - The link that this packet was received on, may be NULL + if the link was not found. If not NULL, Link will have + a reference of type LREF_TREE. + + HeaderBuffer - pointer to the packet header. + + PacketSize - Overall size of the packet (not including header). + + DlcHeader - Points to the DLC header of the packet. + + DlcSize - The length of the packet indicated, starting from DlcHeader. + + Loopback - TRUE if this was called by NbfProcessLoopbackQueue; + used to determine whether to call NdisTransferData or + NbfTransferLoopbackData. + +Return Value: + + None. + +--*/ +{ + + PNDIS_PACKET NdisPacket; + NTSTATUS Status; + PNDIS_BUFFER NdisBuffer; + NDIS_STATUS NdisStatus; + PSINGLE_LIST_ENTRY linkage; + UINT BytesTransferred; + BOOLEAN Command; + BOOLEAN PollFinal; + PRECEIVE_PACKET_TAG ReceiveTag; + PBUFFER_TAG BufferTag; + PUCHAR SourceRouting; + UINT SourceRoutingLength; + PDLC_I_FRAME IHeader; + PDLC_U_FRAME UHeader; + PDLC_S_FRAME SHeader; + PTP_ADDRESS DatagramAddress; + UINT NdisBufferLength; + PVOID BufferPointer; + + ENTER_NBF; + + + INCREMENT_COUNTER (DeviceContext, PacketsReceived); + + Command = (BOOLEAN)!(DlcHeader->Ssap & DLC_SSAP_RESPONSE); + + if (Link == (PTP_LINK)NULL) { + UHeader = (PDLC_U_FRAME)DlcHeader; + if (((UHeader->Command & ~DLC_U_PF) == DLC_CMD_UI) && Command) { + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfGeneralReceiveHandler: Processing packet as UI frame.\n"); + } + + MacReturnSourceRouting( + &DeviceContext->MacInfo, + HeaderBuffer, + &SourceRouting, + &SourceRoutingLength); + + if (SourceRoutingLength > MAX_SOURCE_ROUTING) { + Status = STATUS_ABANDONED; + } + else { + Status = NbfProcessUi ( + DeviceContext, + SourceAddress, + HeaderBuffer, + (PUCHAR)UHeader, + DlcSize, + SourceRouting, + SourceRoutingLength, + &DatagramAddress); + } + } else { + + // + // or drop on the floor. (BUGBUG: Note that state tables say that + // we'll always handle a DM with a DM response. This should change.) + // + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfReceiveIndication: it's not a UI frame!\n"); + } + Status = STATUS_SUCCESS; + } + + if (Status != STATUS_MORE_PROCESSING_REQUIRED) { + + LEAVE_NBF; + return; + + } else if (((PNBF_HDR_CONNECTIONLESS)((PUCHAR)UHeader + 3))->Command == + NBF_CMD_STATUS_RESPONSE) { + + (VOID)NbfProcessStatusResponse( + DeviceContext, + ReceiveContext, + (PNBF_HDR_CONNECTIONLESS)((PUCHAR)UHeader + 3), + SourceAddress, + SourceRouting, + SourceRoutingLength); + return; + + } else { + goto HandleAtComplete; // only datagrams will get through this + } + } + + + // + // At this point we have a link reference count of type LREF_TREE + // + + ++Link->PacketsReceived; + + // + // deal with I-frames first; they are what we expect the most of... + // + + if (!(DlcHeader->Byte1 & DLC_I_INDICATOR)) { + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 ("NbfReceiveIndication: I-frame encountered.\n"); + } + if (DlcSize >= 4 + sizeof (NBF_HDR_CONNECTION)) { + IHeader = (PDLC_I_FRAME)DlcHeader; + NbfProcessIIndicate ( + Command, + (BOOLEAN)(IHeader->RcvSeq & DLC_I_PF), + Link, + (PUCHAR)DlcHeader, + DlcSize, + PacketSize, + ReceiveContext, + Loopback); + } else { +#if DBG +// IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 ("NbfReceiveIndication: Runt I-frame, discarded!\n"); +// } +#endif + ; + } + + } else if (((DlcHeader->Byte1 & DLC_U_INDICATOR) == DLC_U_INDICATOR)) { + + // + // different case switches for S and U frames, because structures + // are different. + // + + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint0 ("NbfReceiveIndication: U-frame encountered.\n"); + } + + UHeader = (PDLC_U_FRAME)DlcHeader; + PollFinal = (BOOLEAN)(UHeader->Command & DLC_U_PF); + switch (UHeader->Command & ~DLC_U_PF) { + + case DLC_CMD_SABME: + NbfProcessSabme (Command, PollFinal, Link, UHeader, + HeaderBuffer, SourceAddress, DeviceContext); + break; + + case DLC_CMD_DISC: + NbfProcessDisc (Command, PollFinal, Link, UHeader); + break; + + case DLC_CMD_UA: + NbfProcessUa (Command, PollFinal, Link, UHeader); + break; + + case DLC_CMD_DM: + NbfProcessDm (Command, PollFinal, Link, UHeader); + break; + + case DLC_CMD_FRMR: + NbfProcessFrmr (Command, PollFinal, Link, UHeader); + break; + + case DLC_CMD_UI: + + ASSERT (FALSE); + break; + + case DLC_CMD_XID: + PANIC ("ReceiveIndication: XID!\n"); + NbfProcessXid (Command, PollFinal, Link, UHeader); + break; + + case DLC_CMD_TEST: + PANIC ("NbfReceiveIndication: TEST!\n"); + NbfProcessTest (Command, PollFinal, Link, UHeader); + break; + + default: + PANIC ("NbfReceiveIndication: bad U-frame, packet dropped.\n"); + + } /* switch */ + + } else { + + // + // We have an S-frame. + // + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 ("NbfReceiveIndication: S-frame encountered.\n"); + } + SHeader = (PDLC_S_FRAME)DlcHeader; + PollFinal = (BOOLEAN)(SHeader->RcvSeq & DLC_S_PF); + switch (SHeader->Command) { + + case DLC_CMD_RR: + case DLC_CMD_RNR: + case DLC_CMD_REJ: + NbfProcessSFrame (Command, PollFinal, Link, SHeader, SHeader->Command); + break; + + default: + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfReceiveIndication: bad S-frame.\n"); + } + + } /* switch */ + + } // if U-frame or S-frame + + // + // If we reach here, the packet has been processed. If it needs + // to be copied, we will jump to HandleAtComplete. + // + + NbfDereferenceLinkMacro ("Done with Indicate frame", Link, LREF_TREE); + LEAVE_NBF; + return; + +HandleAtComplete:; + + // + // At this point we DO NOT have any link references added in + // this function. + // + + linkage = ExInterlockedPopEntryList( + &DeviceContext->ReceivePacketPool, + &DeviceContext->Interlock); + + if (linkage != NULL) { + NdisPacket = CONTAINING_RECORD( linkage, NDIS_PACKET, ProtocolReserved[0] ); + } else { + // PANIC ("NbfReceiveIndicate: Discarding Packet, no receive packets.\n"); + DeviceContext->ReceivePacketExhausted++; + LEAVE_NBF; + return; + } + ReceiveTag = (PRECEIVE_PACKET_TAG)(NdisPacket->ProtocolReserved); + + linkage = ExInterlockedPopEntryList( + &DeviceContext->ReceiveBufferPool, + &DeviceContext->Interlock); + + if (linkage != NULL) { + BufferTag = CONTAINING_RECORD( linkage, BUFFER_TAG, Linkage); + } else { + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + &ReceiveTag->Linkage, + &DeviceContext->Interlock); + // PANIC ("NbfReceiveIndicate: Discarding Packet, no receive buffers.\n"); + DeviceContext->ReceiveBufferExhausted++; + LEAVE_NBF; + return; + } + + NdisAdjustBufferLength (BufferTag->NdisBuffer, PacketSize); + NdisChainBufferAtFront (NdisPacket, BufferTag->NdisBuffer); + + // + // DatagramAddress has a reference of type AREF_PROCESS_DATAGRAM, + // unless this is a datagram intended for RAS only, in which case + // it is NULL. + // + + BufferTag->Address = DatagramAddress; + + // + // set up async return status so we can tell when it has happened; + // can never get return of NDIS_STATUS_PENDING in synch completion routine + // for NdisTransferData, so we know it has completed when this status + // changes + // + + BufferTag->NdisStatus = NDIS_STATUS_PENDING; + ReceiveTag->PacketType = TYPE_AT_COMPLETE; + + ExInterlockedInsertTailList( + &DeviceContext->ReceiveInProgress, + &BufferTag->Linkage, + &DeviceContext->SpinLock); + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 ("NbfReceiveIndicate: Packet on Queue: %lx\n",NdisPacket); + } + + // + // receive packet is mapped at initalize + // + + // + // Determine how to handle the data transfer. + // + + if (Loopback) { + + NbfTransferLoopbackData( + &NdisStatus, + DeviceContext, + ReceiveContext, + DeviceContext->MacInfo.TransferDataOffset, + PacketSize, + NdisPacket, + &BytesTransferred + ); + + } else { + + NdisTransferData ( + &NdisStatus, + DeviceContext->NdisBindingHandle, + ReceiveContext, + DeviceContext->MacInfo.TransferDataOffset, + PacketSize, + NdisPacket, + &BytesTransferred); + + } + + // + // handle the various error codes + // + + switch (NdisStatus) { + case NDIS_STATUS_SUCCESS: // received packet + BufferTag->NdisStatus = NDIS_STATUS_SUCCESS; + + if (BytesTransferred == PacketSize) { // Did we get the entire packet? + ReceiveTag->PacketType = TYPE_AT_INDICATE; + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + &ReceiveTag->Linkage, + &DeviceContext->Interlock); + LEAVE_NBF; + return; + } + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint2 ("NbfReceiveIndicate: Discarding Packet, Partial transfer: 0x0%lx of 0x0%lx transferred\n", + BytesTransferred, PacketSize); + } + break; + + case NDIS_STATUS_PENDING: // waiting async complete from NdisTransferData + LEAVE_NBF; + return; + break; + + default: // something broke; certainly we'll never get NdisTransferData + // asynch completion with this error status... + break; + } + + // + // receive failed, for some reason; cleanup and fail return + // BUGBUG: Statistics go here + // + +#if DBG + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 ("NbfReceiveIndicate: Discarding Packet, transfer failed: %s\n", + NbfGetNdisStatus (NdisStatus)); + } +#endif + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + RemoveEntryList (&BufferTag->Linkage); + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + ReceiveTag->PacketType = TYPE_AT_INDICATE; + + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + &ReceiveTag->Linkage, + &DeviceContext->Interlock); + + NdisQueryBuffer (NdisBuffer, &BufferPointer, &NdisBufferLength); + BufferTag = CONTAINING_RECORD ( + BufferPointer, + BUFFER_TAG, + Buffer[0] + ); + NdisAdjustBufferLength (NdisBuffer, BufferTag->Length); // reset to good value + + ExInterlockedPushEntryList( + &DeviceContext->ReceiveBufferPool, + (PSINGLE_LIST_ENTRY)&BufferTag->Linkage, + &DeviceContext->Interlock); + + if (DatagramAddress) { + NbfDereferenceAddress ("DG TransferData failed", DatagramAddress, AREF_PROCESS_DATAGRAM); + } + + LEAVE_NBF; + return; + +} /* NbfGeneralReceiveHandler */ + + + +VOID +NbfTransferDataComplete ( + IN NDIS_HANDLE BindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus, + IN UINT BytesTransferred + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that an NdisTransferData has completed. We use this indication + to start stripping buffers from the receive queue. + +Arguments: + + BindingContext - The Adapter Binding specified at initialization time. + + NdisPacket/RequestHandle - An identifier for the request that completed. + + NdisStatus - The completion status for the request. + + BytesTransferred - Number of bytes actually transferred. + + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext = (PDEVICE_CONTEXT)BindingContext; + PRECEIVE_PACKET_TAG ReceiveTag; + PTP_CONNECTION Connection; + KIRQL oldirql1; + PTP_REQUEST Request; + PNDIS_BUFFER NdisBuffer; + UINT NdisBufferLength; + PVOID BufferPointer; + PBUFFER_TAG BufferTag; + + // + // Put the NDIS status into a place we can use in packet processing. + // Note that this complete indication may be occuring during the call + // to NdisTransferData in the receive indication. + // + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint2 (" NbfTransferDataComplete: Entered, Packet: %lx bytes transferred: 0x0%x\n", + NdisPacket, BytesTransferred); + } + ReceiveTag = (PRECEIVE_PACKET_TAG)(NdisPacket->ProtocolReserved); + + // + // note that the processing below depends on having only one packet + // transfer outstanding at a time. NDIS is supposed to guarentee this. + // + + switch (ReceiveTag->PacketType) { + + case TYPE_AT_COMPLETE: // datagrams + + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + NdisQueryBuffer (NdisBuffer, &BufferPointer, &NdisBufferLength); + BufferTag = CONTAINING_RECORD( BufferPointer, BUFFER_TAG, Buffer[0]); + BufferTag->NdisStatus = NdisStatus; + + ReceiveTag->PacketType = TYPE_AT_INDICATE; + + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + &ReceiveTag->Linkage, + &DeviceContext->Interlock); + + break; + + case TYPE_AT_INDICATE: // I-frames + + // + // The transfer for this packet is complete. Was it successful?? + // + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql1); + + Connection = ReceiveTag->Connection; + + // + // rip all of the NDIS_BUFFERs we've used off the chain and return them. + // + + if (ReceiveTag->AllocatedNdisBuffer) { + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + while (NdisBuffer != NULL) { + NdisFreeBuffer (NdisBuffer); + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + } + } else { + NdisReinitializePacket (NdisPacket); + } + + + if ((NdisStatus != NDIS_STATUS_SUCCESS) || + (!DeviceContext->MacInfo.SingleReceive)) { + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + ULONG DumpData[2]; + DumpData[0] = BytesTransferred; + DumpData[1] = ReceiveTag->BytesToTransfer; + + NbfWriteGeneralErrorLog( + DeviceContext, + EVENT_TRANSPORT_TRANSFER_DATA, + 603, + NdisStatus, + NULL, + 2, + DumpData); + + // Drop the packet. +#if DBG + NbfPrint1 ("NbfTransferDataComplete: status %s\n", + NbfGetNdisStatus (NdisStatus)); +#endif + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + Connection->Flags |= CONNECTION_FLAGS_TRANSFER_FAIL; + + } else { + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + } + + Connection->TransferBytesPending -= ReceiveTag->BytesToTransfer; + + if ((Connection->TransferBytesPending == 0) && + (Connection->Flags & CONNECTION_FLAGS_TRANSFER_FAIL)) { + + Connection->CurrentReceiveMdl = Connection->SavedCurrentReceiveMdl; + Connection->ReceiveByteOffset = Connection->SavedReceiveByteOffset; + Connection->MessageBytesReceived -= Connection->TotalTransferBytesPending; + Connection->Flags &= ~CONNECTION_FLAGS_TRANSFER_FAIL; + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + if ((Connection->Flags & CONNECTION_FLAGS_VERSION2) == 0) { + NbfSendNoReceive (Connection); + } + NbfSendReceiveOutstanding (Connection); + + ReceiveTag->CompleteReceive = FALSE; + + } else { + + // + // BUGBUG: If we have more data outstanding, this can't + // be the last piece; i.e. we can't handle having + // the last piece complete asynchronously before + // an earlier piece. + // +#if DBG + if (Connection->TransferBytesPending > 0) { + ASSERT (!ReceiveTag->CompleteReceive); + } +#endif + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } + + if (!Connection->CurrentReceiveSynchronous) { + NbfDereferenceReceiveIrp ("TransferData complete", IoGetCurrentIrpStackLocation(Connection->CurrentReceiveIrp), RREF_RECEIVE); + } + + + // + // dereference the connection to say we've done the I frame processing. + // This reference was done before calling NdisTransferData. + // + + if (ReceiveTag->TransferDataPended) { + NbfDereferenceConnection("TransferData done", Connection, CREF_TRANSFER_DATA); + } + + + } else { + + ASSERT (NdisStatus == STATUS_SUCCESS); + ASSERT (!ReceiveTag->TransferDataPended); + ASSERT (Connection->CurrentReceiveSynchronous); + + if (!Connection->SpecialReceiveIrp) { + Connection->CurrentReceiveIrp->IoStatus.Information += BytesTransferred; + } + + } + + + // + // see if we've completed the current receive. If so, move to the next one. + // + + if (ReceiveTag->CompleteReceive) { + CompleteReceive (Connection, ReceiveTag->EndOfMessage, (ULONG)BytesTransferred); + } + + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + &ReceiveTag->Linkage, + &DeviceContext->Interlock); + + KeLowerIrql (oldirql1); + + break; + + case TYPE_STATUS_RESPONSE: // response to remote adapter status + + // + // BUGBUG: Handle failure. + // + +#if DBG + if (NdisStatus != NDIS_STATUS_SUCCESS) { + DbgPrint ("NBF: STATUS_RESPONSE TransferData failed\n"); + } +#endif + + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + ASSERT (NdisBuffer); + NdisFreeBuffer (NdisBuffer); + + Request = (PTP_REQUEST)ReceiveTag->Connection; + + if (ReceiveTag->CompleteReceive) { + NbfCompleteRequest( + Request, + ReceiveTag->EndOfMessage ? STATUS_SUCCESS : STATUS_BUFFER_OVERFLOW, + Request->BytesWritten); + } + + NbfDereferenceRequest("Status xfer done", Request, RREF_STATUS); + + ReceiveTag->PacketType = TYPE_AT_INDICATE; + + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + &ReceiveTag->Linkage, + &DeviceContext->Interlock); + + break; + + default: +#if DBG + NbfPrint1 ("NbfTransferDataComplete: Bang! Packet Transfer failed, unknown packet type: %ld\n", + ReceiveTag->PacketType); + DbgBreakPoint (); +#endif + break; + } + + // + // BUGBUG: Statistics need to be kept here. + // + + return; + +} // NbfTransferDataComplete + + + +VOID +NbfReceiveComplete ( + IN NDIS_HANDLE BindingContext + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that a connection(less) frame has been received on the + physical link. We dispatch to the correct packet handler here. + +Arguments: + + BindingContext - The Adapter Binding specified at initialization time. + Nbf uses the DeviceContext for this parameter. + +Return Value: + + None + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + UINT i; + NTSTATUS Status; + KIRQL oldirql2; + BOOLEAN Command; + PDLC_U_FRAME UHeader; + PDLC_FRAME DlcHeader; + PLIST_ENTRY linkage; + UINT NdisBufferLength; + PVOID BufferPointer; + PBUFFER_TAG BufferTag; + PTP_ADDRESS Address; + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + PTP_CONNECTION Connection; + PTP_LINK Link; + + ENTER_NBF; + + // + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfReceiveComplete: Entered.\n"); + } + + DeviceContext = (PDEVICE_CONTEXT) BindingContext; + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql2); + + // + // Complete all pending receives. Do a quick check + // without the lock. + // + + while (!IsListEmpty (&DeviceContext->IrpCompletionQueue)) { + + linkage = ExInterlockedRemoveHeadList( + &DeviceContext->IrpCompletionQueue, + &DeviceContext->Interlock); + + if (linkage != NULL) { + + Irp = CONTAINING_RECORD (linkage, IRP, Tail.Overlay.ListEntry); + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + Connection = IRP_RECEIVE_CONNECTION(IrpSp); + + if (Connection == NULL) { +#if DBG + DbgPrint ("Connection of Irp %lx is NULL\n", Irp); + DbgBreakPoint(); +#endif + } + + IRP_RECEIVE_REFCOUNT(IrpSp) = 0; + IRP_RECEIVE_IRP (IrpSp) = NULL; + + LEAVE_NBF; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + ENTER_NBF; + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + if (Connection->Flags & CONNECTION_FLAGS_RC_PENDING) { + + Connection->Flags &= ~CONNECTION_FLAGS_RC_PENDING; + + if (Connection->Flags & CONNECTION_FLAGS_PEND_INDICATE) { + + Connection->Flags &= ~CONNECTION_FLAGS_PEND_INDICATE; + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // We got an indicate (and sent a NO_RECEIVE) while + // this was in progress, so send the receive outstanding + // now. + // + + NbfSendReceiveOutstanding (Connection); + + } else { + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + } + + } else { + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + } + + NbfDereferenceConnectionMacro ("receive completed", Connection, CREF_RECEIVE_IRP); + + } else { + + // + // ExInterlockedRemoveHeadList returned NULL, so don't + // bother looping back. + // + + break; + + } + + } + + + // + // Packetize all waiting connections + // + + if (!IsListEmpty(&DeviceContext->PacketizeQueue)) { + + PacketizeConnections (DeviceContext); + + } + + if (!IsListEmpty (&DeviceContext->DeferredRrQueue)) { + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->Interlock); + + while (!IsListEmpty(&DeviceContext->DeferredRrQueue)) { + + linkage = RemoveHeadList (&DeviceContext->DeferredRrQueue); + + Link = CONTAINING_RECORD (linkage, TP_LINK, DeferredRrLinkage); + + Link->OnDeferredRrQueue = FALSE; + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->Interlock); + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + StopT2 (Link); // we're acking, so no delay req'd. + NbfSendRr (Link, FALSE, FALSE); // releases lock + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->Interlock); + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->Interlock); + + } + + + // + // Get every waiting packet, in order... + // + + + if (!IsListEmpty (&DeviceContext->ReceiveInProgress)) { + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + while (!IsListEmpty (&DeviceContext->ReceiveInProgress)) { + + linkage = RemoveHeadList (&DeviceContext->ReceiveInProgress); + BufferTag = CONTAINING_RECORD( linkage, BUFFER_TAG, Linkage); + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 (" NbfReceiveComplete: Processing Buffer %lx\n", BufferTag); + } + + // + // NdisTransferData may have failed at async completion; check and + // see. If it did, then we discard this packet. If we're still waiting + // for transfer to complete, go back to sleep and hope (no guarantee!) + // we get waken up later. + // + +#if DBG + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 (" NbfReceiveComplete: NdisStatus: %s.\n", + NbfGetNdisStatus(BufferTag->NdisStatus)); + } +#endif + if (BufferTag->NdisStatus == NDIS_STATUS_PENDING) { + InsertHeadList (&DeviceContext->ReceiveInProgress, linkage); + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfReceiveComplete: Status pending, returning packet to queue.\n"); + } + KeLowerIrql (oldirql2); + LEAVE_NBF; + return; + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + if (BufferTag->NdisStatus != NDIS_STATUS_SUCCESS) { +#if DBG + NbfPrint1 (" NbfReceiveComplete: Failed return from Transfer data, reason: %s\n", + NbfGetNdisStatus (BufferTag->NdisStatus)); +#endif + // BUGBUG: some statistics go here for lost packet + + goto FreeBuffer; // skip the buffer, continue with while loop + } + + // + // Have a buffer. Since I allocated the storage for it, I know it's + // virtually contiguous and can treat it that way, which I will + // henceforth. + // + + NdisQueryBuffer (BufferTag->NdisBuffer, &BufferPointer, &NdisBufferLength); + + IF_NBFDBG (NBF_DEBUG_DLC) { + PUCHAR pc; + NbfPrint0 (" NbfRC Packet: \n "); + pc = (PUCHAR)BufferPointer; + for (i=0;i<25;i++) { + NbfPrint1 (" %2x",pc[i]); + } + NbfPrint0 ("\n"); + } + + // + // Determine what address this is for, which is stored + // in the buffer tag header. + // + + Address = BufferTag->Address; + + // + // Process the frame as a UI frame; only datagrams should + // be processed here. If Address is NULL then this datagram + // is not needed for any bound address and should be given + // to RAS only. + // + + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfReceiveComplete: Delivering this frame manually.\n"); + } + DlcHeader = (PDLC_FRAME)BufferPointer; + Command = (BOOLEAN)!(DlcHeader->Ssap & DLC_SSAP_RESPONSE); + UHeader = (PDLC_U_FRAME)DlcHeader; + + BufferPointer = (PUCHAR)BufferPointer + 3; + NdisBufferLength -= 3; // 3 less bytes. + + if (Address != NULL) { + + // + // Indicate it or complete posted datagrams. + // + + Status = NbfIndicateDatagram ( + DeviceContext, + Address, + BufferPointer, // the Dsdu, with some tweaking + NdisBufferLength); + + // + // Dereference the address. + // + + NbfDereferenceAddress ("Datagram done", Address, AREF_PROCESS_DATAGRAM); + + } + + // + // Let the RAS clients have a crack at this if they want + // (they only want directed datagrams, not broadcast). + // + + if ((((PNBF_HDR_CONNECTIONLESS)BufferPointer)->Command == NBF_CMD_DATAGRAM) && + (DeviceContext->IndicationQueuesInUse)) { + + NbfActionDatagramIndication( + DeviceContext, + (PNBF_HDR_CONNECTIONLESS)BufferPointer, + NdisBufferLength); + + } + + BufferPointer = (PUCHAR)BufferPointer - 3; + NdisBufferLength += 3; // 3 more bytes. + + // + // Finished with buffer; return to pool. + // + +FreeBuffer:; + + NdisAdjustBufferLength (BufferTag->NdisBuffer, BufferTag->Length); + ExInterlockedPushEntryList( + &DeviceContext->ReceiveBufferPool, + (PSINGLE_LIST_ENTRY)&BufferTag->Linkage, + &DeviceContext->Interlock); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + } // if queue not empty + + KeLowerIrql (oldirql2); + LEAVE_NBF; + return; + +} /* NbfReceiveComplete */ + + +VOID +NbfTransferLoopbackData ( + OUT PNDIS_STATUS NdisStatus, + IN PDEVICE_CONTEXT DeviceContext, + IN NDIS_HANDLE ReceiveContext, + IN UINT ByteOffset, + IN UINT BytesToTransfer, + IN PNDIS_PACKET Packet, + OUT PUINT BytesTransferred + ) + +/*++ + +Routine Description: + + This routine is called instead of NdisTransferData for + loopback indications. It copies the data from the + source packet to the receive packet. + +Arguments: + + NdisStatus - Returns the status of the operation. + + DeviceContext - The device context. + + ReceiveContext - A pointer to the source packet. + + ByteOffset - The offset from the start of the source packet + that the transfer should begin at. + + BytesToTransfer - The number of bytes to transfer. + + Packet - A pointer to the receive packet. + + BytesTransferred - Returns the number of bytes copied. + +Return Value: + + None. + +--*/ + +{ + PNDIS_PACKET SourcePacket = (PNDIS_PACKET)ReceiveContext; + + NdisCopyFromPacketToPacket( + Packet, + 0, + BytesToTransfer, + SourcePacket, + DeviceContext->LoopbackHeaderLength + ByteOffset, + BytesTransferred + ); + + *NdisStatus = NDIS_STATUS_SUCCESS; // BUGBUG: what if BytesTransferred is too small + +} + + +VOID +NbfCopyFromPacketToBuffer( + IN PNDIS_PACKET Packet, + IN UINT Offset, + IN UINT BytesToCopy, + OUT PCHAR Buffer, + OUT PUINT BytesCopied + ) + +/*++ + +Routine Description: + + Copy from an ndis packet into a buffer. + +Arguments: + + Packet - The packet to copy from. + + Offset - The offset from which to start the copy. + + BytesToCopy - The number of bytes to copy from the packet. + + Buffer - The destination of the copy. + + BytesCopied - The number of bytes actually copied. Can be less then + BytesToCopy if the packet is shorter than BytesToCopy. + +Return Value: + + None + +--*/ + +{ + + // + // Holds the number of ndis buffers comprising the packet. + // + UINT NdisBufferCount; + + // + // Points to the buffer from which we are extracting data. + // + PNDIS_BUFFER CurrentBuffer; + + // + // Holds the virtual address of the current buffer. + // + PVOID VirtualAddress; + + // + // Holds the length of the current buffer of the packet. + // + UINT CurrentLength; + + // + // Keep a local variable of BytesCopied so we aren't referencing + // through a pointer. + // + UINT LocalBytesCopied = 0; + + // + // Take care of boundary condition of zero length copy. + // + + *BytesCopied = 0; + if (!BytesToCopy) return; + + // + // Get the first buffer. + // + + NdisQueryPacket( + Packet, + NULL, + &NdisBufferCount, + &CurrentBuffer, + NULL + ); + + // + // Could have a null packet. + // + + if (!NdisBufferCount) return; + + NdisQueryBuffer( + CurrentBuffer, + &VirtualAddress, + &CurrentLength + ); + + while (LocalBytesCopied < BytesToCopy) { + + if (!CurrentLength) { + + NdisGetNextBuffer( + CurrentBuffer, + &CurrentBuffer + ); + + // + // We've reached the end of the packet. We return + // with what we've done so far. (Which must be shorter + // than requested. + // + + if (!CurrentBuffer) break; + + NdisQueryBuffer( + CurrentBuffer, + &VirtualAddress, + &CurrentLength + ); + continue; + + } + + // + // Try to get us up to the point to start the copy. + // + + if (Offset) { + + if (Offset > CurrentLength) { + + // + // What we want isn't in this buffer. + // + + Offset -= CurrentLength; + CurrentLength = 0; + continue; + + } else { + + VirtualAddress = (PCHAR)VirtualAddress + Offset; + CurrentLength -= Offset; + Offset = 0; + + } + + } + + // + // Copy the data. + // + + + { + + // + // Holds the amount of data to move. + // + UINT AmountToMove; + + AmountToMove = + ((CurrentLength <= (BytesToCopy - LocalBytesCopied))? + (CurrentLength):(BytesToCopy - LocalBytesCopied)); + + RtlCopyMemory( + Buffer, + VirtualAddress, + AmountToMove + ); + + Buffer = (PCHAR)Buffer + AmountToMove; + VirtualAddress = (PCHAR)VirtualAddress + AmountToMove; + + LocalBytesCopied += AmountToMove; + CurrentLength -= AmountToMove; + + } + + } + + *BytesCopied = LocalBytesCopied; + +} + |