You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3297 lines
89 KiB
3297 lines
89 KiB
/*++
|
|
|
|
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;
|
|
|
|
}
|
|
|