3298 lines
85 KiB
C
3298 lines
85 KiB
C
/*++
|
||
|
||
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.
|
||
//
|
||
|
||
//
|
||
// 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[8];
|
||
|
||
PollFinal, Header; // prevent compiler warnings
|
||
|
||
IF_NBFDBG (NBF_DEBUG_DLC) {
|
||
NbfPrint0 (" NbfProcessFrmr: Entered.\n");
|
||
}
|
||
|
||
//
|
||
// Log an error, this shouldn't happen.
|
||
//
|
||
|
||
// Some state from Link and Packet Header
|
||
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);
|
||
|
||
|
||
// Log if this is a loopback link & loopback index
|
||
// Also log the remote MAC address for this link
|
||
DumpData[6] = (Link->Loopback << 24) +
|
||
(Link->LoopbackDestinationIndex << 16) +
|
||
(Link->HardwareAddress.Address[0] << 8) +
|
||
(Link->HardwareAddress.Address[1]);
|
||
|
||
DumpData[7] = (Link->HardwareAddress.Address[2] << 24) +
|
||
(Link->HardwareAddress.Address[3] << 16) +
|
||
(Link->HardwareAddress.Address[4] << 8) +
|
||
(Link->HardwareAddress.Address[5]);
|
||
|
||
NbfWriteGeneralErrorLog(
|
||
Link->Provider,
|
||
EVENT_TRANSPORT_BAD_PROTOCOL,
|
||
1,
|
||
STATUS_LINK_FAILED,
|
||
L"FRMR",
|
||
8,
|
||
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.
|
||
//
|
||
|
||
// 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.
|
||
//
|
||
|
||
// 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: 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);
|
||
|
||
//
|
||
// 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. (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");
|
||
}
|
||
|
||
#if PKT_LOG
|
||
// We have the connection here, log the packet for debugging
|
||
NbfLogRcvPacket(NULL,
|
||
Link,
|
||
(PUCHAR)DlcHeader,
|
||
PacketSize,
|
||
DlcSize);
|
||
#endif // PKT_LOG
|
||
|
||
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");
|
||
}
|
||
|
||
#if PKT_LOG
|
||
// We have the connection here, log the packet for debugging
|
||
NbfLogRcvPacket(NULL,
|
||
Link,
|
||
(PUCHAR)DlcHeader,
|
||
PacketSize,
|
||
DlcSize);
|
||
#endif // PKT_LOG
|
||
|
||
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 {
|
||
|
||
if (DeviceContext->NdisBindingHandle) {
|
||
|
||
NdisTransferData (
|
||
&NdisStatus,
|
||
DeviceContext->NdisBindingHandle,
|
||
ReceiveContext,
|
||
DeviceContext->MacInfo.TransferDataOffset,
|
||
PacketSize,
|
||
NdisPacket,
|
||
&BytesTransferred);
|
||
}
|
||
else {
|
||
NdisStatus = STATUS_INVALID_DEVICE_STATE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// 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
|
||
//
|
||
|
||
#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 {
|
||
|
||
//
|
||
// 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
|
||
|
||
#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;
|
||
}
|
||
|
||
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
|
||
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; // 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;
|
||
|
||
}
|
||
|