summaryrefslogtreecommitdiffstats
path: root/private/ntos/tdi/nbf/dlc.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/ntos/tdi/nbf/dlc.c3270
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;
+
+}
+