2328 lines
63 KiB
C
2328 lines
63 KiB
C
/*++
|
||
|
||
Copyright (c) 1989, 1990, 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
link.c
|
||
|
||
Abstract:
|
||
|
||
This module contains code which implements the TP_LINK object.
|
||
Routines are provided to create, destroy, reference, and dereference,
|
||
transport link objects.
|
||
|
||
Author:
|
||
|
||
David Beaver (dbeaver) 1-July-1991
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
|
||
extern ULONG StartTimerLinkDeferredAdd;
|
||
extern ULONG StartTimerLinkDeferredDelete;
|
||
|
||
#if DBG
|
||
// The following is here for debugging purposes to make it easy to change
|
||
// the maximum packet size.
|
||
|
||
ULONG MaxUserPacketData = 18000;
|
||
#endif
|
||
|
||
#if 0
|
||
|
||
VOID
|
||
DisconnectCompletionHandler(
|
||
IN PTP_LINK TransportLink
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called as an I/O completion handler at the time a
|
||
TdiDisconnect request is completed. Here we dereference the link
|
||
object, and optionally reference it again and start up the link if
|
||
some transport connection started up on the link during the time we
|
||
were trying to shut it down.
|
||
|
||
Arguments:
|
||
|
||
TransportLink - Pointer to a transport link object.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
IF_NBFDBG (NBF_DEBUG_LINK) {
|
||
NbfPrint1 ("DisconnectCompletionHandler: Entered for link %lx.\n",
|
||
TransportLink);
|
||
}
|
||
|
||
//
|
||
// The following call will dereference this link for the last time,
|
||
// unless another transport connection has been assigned to the link
|
||
// during the time the data link layer was bringing the link down and
|
||
// when we got here. If this condition exists, then now is the time
|
||
// to bring the link back up, else destroy it.
|
||
//
|
||
|
||
// don't forget to check for bringing it back up again.
|
||
|
||
NbfDereferenceLink ("Disconnecting", TransportLink, LREF_CONNECTION); // this makes it go away.
|
||
#if DBG
|
||
NbfPrint0("Disconnecting Completion Handler\n");
|
||
#endif
|
||
|
||
} /* DisconnectCompletionHandler */
|
||
#endif
|
||
|
||
|
||
VOID
|
||
NbfCompleteLink(
|
||
IN PTP_LINK Link
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by the UA-r/x handler, NbfWaitLink, and
|
||
NbfActivateLink to startup the NBF connections associated with
|
||
a link because they were waiting for the link to become established.
|
||
|
||
When we get here, the link has been established, so we need to
|
||
start the next set of connection-establishment protocols:
|
||
|
||
SESSION_INIT ----------------->
|
||
<----------------- SESSION_CONFIRM
|
||
|
||
(TdiConnect completes) (TdiListen completes)
|
||
|
||
NOTE: THIS ROUTINE MUST BE CALLED FROM DPC LEVEL.
|
||
|
||
Arguments:
|
||
|
||
Link - Pointer to a transport link object.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
PTP_CONNECTION Connection;
|
||
BOOLEAN TimerWasCleared;
|
||
|
||
IF_NBFDBG (NBF_DEBUG_LINK) {
|
||
NbfPrint1 ("NbfCompleteLink: Entered for link %lx.\n", Link);
|
||
}
|
||
|
||
ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL);
|
||
|
||
//
|
||
// Officially declare that this link is ready for I-frame business.
|
||
//
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
|
||
//
|
||
// We can now send and receive I-frames on this link. We are in ABME.
|
||
//
|
||
|
||
//
|
||
// This probably isn't necessary, but to be safe for now.. (adb 6/28)
|
||
//
|
||
if (Link->State == LINK_STATE_ADM) {
|
||
// Moving out of ADM, add special reference
|
||
NbfReferenceLinkSpecial("To READY in NbfCompleteLink", Link, LREF_NOT_ADM);
|
||
}
|
||
|
||
Link->State = LINK_STATE_READY;
|
||
Link->SendState = SEND_STATE_READY;
|
||
Link->ReceiveState = RECEIVE_STATE_READY;
|
||
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
|
||
//
|
||
// Complete all of the listens first, so they will be expecting
|
||
// incoming SESSION_INITIALIZEs. Then do the connects.
|
||
//
|
||
|
||
// This creates a connection reference which is removed below.
|
||
while ((Connection=NbfLookupPendingListenOnLink (Link)) != NULL) {
|
||
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
|
||
//
|
||
// This loop looks unnecessary, let's make sure... - adb 9/11/91
|
||
//
|
||
ASSERT(Connection->Flags & CONNECTION_FLAGS_WAIT_SI);
|
||
|
||
Connection->Flags |= CONNECTION_FLAGS_WAIT_SI; // wait session initialize.
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
NbfDereferenceConnection ("Pending listen", Connection, CREF_P_LINK);
|
||
} /* while */
|
||
|
||
//
|
||
// And do the connects. If there are connections in progress, they'll
|
||
// also have timers associated with them. Cancel those timers.
|
||
//
|
||
|
||
while ((Connection=NbfLookupPendingConnectOnLink (Link)) != NULL) {
|
||
TimerWasCleared = KeCancelTimer (&Connection->Timer);
|
||
IF_NBFDBG (NBF_DEBUG_LINK) {
|
||
NbfPrint2 ("NbfCompleteLink: Timer for connection %lx %s canceled.\n",
|
||
Connection, TimerWasCleared ? "was" : "was NOT" );
|
||
}
|
||
if (TimerWasCleared) {
|
||
NbfDereferenceConnection("Cancel timer", Connection, CREF_TIMER);
|
||
}
|
||
ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
Connection->Flags |= CONNECTION_FLAGS_WAIT_SC; // wait session confirm.
|
||
RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
|
||
|
||
//
|
||
// No timeout for this frame is required since the link is responsible
|
||
// for reliable delivery. If we can't send this frame, however, the
|
||
// data link connection will happily keep quiet without timeouts.
|
||
//
|
||
|
||
NbfSendSessionInitialize (Connection);
|
||
NbfDereferenceConnection ("NbfCompleteLink", Connection, CREF_P_CONNECT);
|
||
} /* while */
|
||
|
||
} /* NbfCompleteLink */
|
||
|
||
|
||
VOID
|
||
NbfAllocateLink(
|
||
IN PDEVICE_CONTEXT DeviceContext,
|
||
OUT PTP_LINK *TransportLink
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine allocates storage for a data link connection. It
|
||
performs minimal initialization of the object.
|
||
|
||
NOTE: This routine is called with the device context spinlock
|
||
held, or at such a time as synchronization is unnecessary.
|
||
|
||
Arguments:
|
||
|
||
DeviceContext - Pointer to the device context (which is really just
|
||
the device object with its extension) to be associated with the
|
||
link.
|
||
|
||
TransportLink - Pointer to a place where this routine will return a
|
||
pointer to an allocated transport link structure. Returns
|
||
NULL if no storage can be allocated.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PTP_LINK Link;
|
||
|
||
if ((DeviceContext->MemoryLimit != 0) &&
|
||
((DeviceContext->MemoryUsage + sizeof(TP_LINK)) >
|
||
DeviceContext->MemoryLimit)) {
|
||
PANIC("NBF: Could not allocate link: limit\n");
|
||
NbfWriteResourceErrorLog(
|
||
DeviceContext,
|
||
EVENT_TRANSPORT_RESOURCE_LIMIT,
|
||
105,
|
||
sizeof(TP_LINK),
|
||
LINK_RESOURCE_ID);
|
||
*TransportLink = NULL;
|
||
return;
|
||
}
|
||
Link = (PTP_LINK)ExAllocatePoolWithTag (
|
||
NonPagedPool,
|
||
sizeof (TP_LINK),
|
||
NBF_MEM_TAG_TP_LINK);
|
||
if (Link == NULL) {
|
||
PANIC("NBF: Could not allocate link: no pool\n");
|
||
NbfWriteResourceErrorLog(
|
||
DeviceContext,
|
||
EVENT_TRANSPORT_RESOURCE_POOL,
|
||
205,
|
||
sizeof(TP_LINK),
|
||
LINK_RESOURCE_ID);
|
||
*TransportLink = NULL;
|
||
return;
|
||
}
|
||
RtlZeroMemory (Link, sizeof(TP_LINK));
|
||
|
||
IF_NBFDBG (NBF_DEBUG_DYNAMIC) {
|
||
NbfPrint1 ("ExAllocatePool Link %08x\n", Link);
|
||
}
|
||
|
||
++DeviceContext->LinkAllocated;
|
||
DeviceContext->MemoryUsage += sizeof(TP_LINK);
|
||
|
||
Link->Type = NBF_LINK_SIGNATURE;
|
||
Link->Size = sizeof (TP_LINK);
|
||
|
||
KeInitializeSpinLock (&Link->SpinLock);
|
||
Link->Provider = DeviceContext;
|
||
Link->ProviderInterlock = &DeviceContext->Interlock;
|
||
|
||
InitializeListHead (&Link->Linkage);
|
||
InitializeListHead (&Link->ConnectionDatabase);
|
||
InitializeListHead (&Link->WackQ);
|
||
InitializeListHead (&Link->NdisSendQueue);
|
||
InitializeListHead (&Link->ShortList);
|
||
Link->OnShortList = FALSE;
|
||
InitializeListHead (&Link->LongList);
|
||
Link->OnLongList = FALSE;
|
||
InitializeListHead (&Link->PurgeList);
|
||
|
||
Link->T1 = 0; // 0 indicates they are not in the list
|
||
Link->T2 = 0;
|
||
Link->Ti = 0;
|
||
|
||
NbfAddSendPacket (DeviceContext);
|
||
NbfAddReceivePacket (DeviceContext);
|
||
|
||
*TransportLink = Link;
|
||
|
||
} /* NbfAllocateLink */
|
||
|
||
|
||
VOID
|
||
NbfDeallocateLink(
|
||
IN PDEVICE_CONTEXT DeviceContext,
|
||
IN PTP_LINK TransportLink
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine frees storage for a data link connection.
|
||
|
||
NOTE: This routine is called with the device context spinlock
|
||
held, or at such a time as synchronization is unnecessary.
|
||
|
||
Arguments:
|
||
|
||
DeviceContext - Pointer to the device context (which is really just
|
||
the device object with its extension) to be associated with the
|
||
link.
|
||
|
||
TransportLink - Pointer to the transport link structure.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
IF_NBFDBG (NBF_DEBUG_DYNAMIC) {
|
||
NbfPrint1 ("ExFreePool Link: %08x\n", TransportLink);
|
||
}
|
||
|
||
ExFreePool (TransportLink);
|
||
--DeviceContext->LinkAllocated;
|
||
DeviceContext->MemoryUsage -= sizeof(TP_LINK);
|
||
|
||
NbfRemoveSendPacket (DeviceContext);
|
||
NbfRemoveReceivePacket (DeviceContext);
|
||
|
||
} /* NbfDeallocateLink */
|
||
|
||
|
||
NTSTATUS
|
||
NbfCreateLink(
|
||
IN PDEVICE_CONTEXT DeviceContext,
|
||
IN PHARDWARE_ADDRESS HardwareAddress,
|
||
IN PUCHAR SourceRouting,
|
||
IN UINT SourceRoutingLength,
|
||
IN USHORT LoopbackLinkIndex,
|
||
OUT PTP_LINK *TransportLink
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine creates a data link connection between the local
|
||
data link station and the specified remote data link address.
|
||
As an option (Passive=TRUE), the caller may specify that instead
|
||
of a Connect activity, a Listen is to be performed instead.
|
||
|
||
Normally, if a link to the remote address is not already active,
|
||
then a link object is allocated, the reference count in the link
|
||
is set to 1, and the reference count of the device context is
|
||
incremented.
|
||
|
||
If a link is already active to the remote address, then the existing
|
||
link object is referenced with NbfReferenceLink() so that it can be
|
||
shared between the transport connections.
|
||
|
||
NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL.
|
||
|
||
Arguments:
|
||
|
||
DeviceContext - Pointer to the device context (which is really just
|
||
the device object with its extension) to be associated with the
|
||
link.
|
||
|
||
HardwareAddress - Pointer to a HARDWARE_ADDRESS type containing the
|
||
hardware address of the REMOTE link station to connect to/listen for.
|
||
|
||
LoopbackLinkIndex - In the case that this turns out to be created
|
||
as one of the LoopbackLinks, this will indicate which one to
|
||
use.
|
||
|
||
TransportLink - Pointer to a place where this routine will return a
|
||
pointer to an allocated transport link structure.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
PTP_LINK Link;
|
||
PLIST_ENTRY p;
|
||
UCHAR TempSR[MAX_SOURCE_ROUTING];
|
||
PUCHAR ResponseSR;
|
||
USHORT i;
|
||
|
||
ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL);
|
||
|
||
|
||
IF_NBFDBG (NBF_DEBUG_LINK) {
|
||
NbfPrint1 ("NbfCreateLink: Entered, DeviceContext: %lx\n", DeviceContext);
|
||
}
|
||
|
||
//
|
||
// Walk the list of addresses to see if we already have a link to this
|
||
// remote address.
|
||
//
|
||
|
||
// This adds a reference if the link is found.
|
||
|
||
Link = NbfFindLink (DeviceContext, HardwareAddress->Address);
|
||
|
||
|
||
if (Link == (PTP_LINK)NULL) {
|
||
|
||
//
|
||
// If necessary, check whether we are looking for one of
|
||
// the loopback links (NbfFindLink won't find those).
|
||
//
|
||
|
||
if (RtlEqualMemory(
|
||
HardwareAddress->Address,
|
||
DeviceContext->LocalAddress.Address,
|
||
DeviceContext->MacInfo.AddressLength)) {
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock);
|
||
Link = DeviceContext->LoopbackLinks[LoopbackLinkIndex];
|
||
|
||
if (Link != (PTP_LINK)NULL) {
|
||
|
||
//
|
||
// Add a reference to simulate the one from NbfFindLink
|
||
//
|
||
// This needs to be atomically done with the assignment above.
|
||
//
|
||
|
||
NbfReferenceLink ("Found loopback link", Link, LREF_TREE);
|
||
|
||
RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock);
|
||
} else {
|
||
|
||
RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock);
|
||
//
|
||
// May have the first loopback link; need to make sure the
|
||
// buffer for indications is allocated.
|
||
//
|
||
|
||
if (DeviceContext->LookaheadContiguous == NULL) {
|
||
|
||
DeviceContext->LookaheadContiguous =
|
||
ExAllocatePoolWithTag (
|
||
NonPagedPool,
|
||
NBF_MAX_LOOPBACK_LOOKAHEAD,
|
||
NBF_MEM_TAG_LOOPBACK_BUFFER);
|
||
if (DeviceContext->LookaheadContiguous == NULL) {
|
||
PANIC ("NbfCreateLink: Could not allocate loopback buffer!\n");
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
if (Link != (PTP_LINK)NULL) {
|
||
|
||
//
|
||
// Found the link structure here, so use the existing link.
|
||
//
|
||
|
||
#if DBG
|
||
//
|
||
// These two operations have no net effect, so if not in debug
|
||
// mode we can remove them.
|
||
//
|
||
|
||
// This reference is removed by NbfDisconnectFromLink
|
||
// (this assumes that NbfConnectToLink is always called
|
||
// if this function returns success).
|
||
|
||
NbfReferenceLink ("New Ref, Found existing link", Link, LREF_CONNECTION); // extra reference.
|
||
|
||
// Now we can remove the NbfFindLinkInTree reference.
|
||
|
||
NbfDereferenceLink ("Found link in tree", Link, LREF_TREE);
|
||
#endif
|
||
|
||
*TransportLink = Link; // return pointer to the link.
|
||
IF_NBFDBG (NBF_DEBUG_LINK) {
|
||
NbfPrint0 ("NbfCreateLink: returning ptr to existing link object.\n");
|
||
}
|
||
return STATUS_SUCCESS; // all done.
|
||
|
||
} /* if LINK != NULL */
|
||
|
||
|
||
//
|
||
// We don't have an existing link, so we have to create one.
|
||
//
|
||
|
||
IF_NBFDBG (NBF_DEBUG_LINK) {
|
||
NbfPrint0 ("NbfCreateLink: using new link object.\n");
|
||
}
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock);
|
||
|
||
p = RemoveHeadList (&DeviceContext->LinkPool);
|
||
if (p == &DeviceContext->LinkPool) {
|
||
|
||
if ((DeviceContext->LinkMaxAllocated == 0) ||
|
||
(DeviceContext->LinkAllocated < DeviceContext->LinkMaxAllocated)) {
|
||
|
||
NbfAllocateLink (DeviceContext, &Link);
|
||
IF_NBFDBG (NBF_DEBUG_DYNAMIC) {
|
||
NbfPrint1 ("NBF: Allocated link at %lx\n", Link);
|
||
}
|
||
|
||
} else {
|
||
|
||
NbfWriteResourceErrorLog(
|
||
DeviceContext,
|
||
EVENT_TRANSPORT_RESOURCE_SPECIFIC,
|
||
405,
|
||
sizeof(TP_LINK),
|
||
LINK_RESOURCE_ID);
|
||
Link = NULL;
|
||
|
||
}
|
||
|
||
if (Link == NULL) {
|
||
++DeviceContext->LinkExhausted;
|
||
RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock);
|
||
PANIC ("NbfCreateConnection: Could not allocate link object!\n");
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
} else {
|
||
|
||
Link = CONTAINING_RECORD (p, TP_LINK, Linkage);
|
||
|
||
}
|
||
|
||
++DeviceContext->LinkInUse;
|
||
ASSERT(DeviceContext->LinkInUse > 0);
|
||
|
||
if (DeviceContext->LinkInUse > DeviceContext->LinkMaxInUse) {
|
||
++DeviceContext->LinkMaxInUse;
|
||
}
|
||
|
||
DeviceContext->LinkTotal += DeviceContext->LinkInUse;
|
||
++DeviceContext->LinkSamples;
|
||
|
||
RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock);
|
||
|
||
|
||
IF_NBFDBG (NBF_DEBUG_LINK) {
|
||
NbfPrint1 ("NbfCreateLink: Link at %lx.\n", Link);
|
||
}
|
||
|
||
//
|
||
// Initialize all of the static data for this link.
|
||
//
|
||
|
||
Link->SpecialRefCount = 1;
|
||
Link->ReferenceCount = 0;
|
||
#if DBG
|
||
{
|
||
UINT Counter;
|
||
for (Counter = 0; Counter < NUMBER_OF_LREFS; Counter++) {
|
||
Link->RefTypes[Counter] = 0;
|
||
}
|
||
|
||
// This reference is removed by NbfDisconnectFromLink
|
||
// (this assumes that NbfConnectToLink is always called
|
||
// if this function returns success).
|
||
//
|
||
|
||
Link->RefTypes[LREF_CONNECTION] = 1;
|
||
Link->RefTypes[LREF_SPECIAL_TEMP] = 1;
|
||
}
|
||
Link->Destroyed = FALSE;
|
||
Link->TotalReferences = 0;
|
||
Link->TotalDereferences = 0;
|
||
Link->NextRefLoc = 0;
|
||
ExInterlockedInsertHeadList (&NbfGlobalLinkList, &Link->GlobalLinkage, &NbfGlobalInterlock);
|
||
StoreLinkHistory (Link, TRUE);
|
||
#endif
|
||
Link->Flags = 0; // in the beginning, the link is closed.
|
||
Link->DeferredFlags = 0;
|
||
Link->State = LINK_STATE_ADM; // async disconnected mode.
|
||
|
||
Link->NdisSendsInProgress = 0;
|
||
Link->ResendingPackets = FALSE;
|
||
|
||
//
|
||
// Initialize the counters
|
||
//
|
||
|
||
Link->FrmrsReceived = 0;
|
||
Link->FrmrsTransmitted = 0;
|
||
Link->ErrorIFramesReceived = 0;
|
||
Link->ErrorIFramesTransmitted = 0;
|
||
Link->AbortedTransmissions = 0;
|
||
Link->BuffersNotAvailable = 0;
|
||
Link->SuccessfulTransmits = 0;
|
||
Link->SuccessfulReceives = 0;
|
||
Link->T1Expirations = 0;
|
||
Link->TiExpirations = 0;
|
||
|
||
#if DBG
|
||
Link->CreatePacketFailures = 0;
|
||
#endif
|
||
|
||
|
||
//
|
||
// At first, the delay and throughput are unknown.
|
||
//
|
||
|
||
Link->Delay = 0xffffffff;
|
||
Link->Throughput.HighPart = 0xffffffff;
|
||
Link->Throughput.LowPart = 0xffffffff;
|
||
Link->ThroughputAccurate = FALSE;
|
||
Link->CurrentT1Backoff = FALSE;
|
||
|
||
Link->OnDeferredRrQueue = FALSE;
|
||
InitializeListHead (&Link->DeferredRrLinkage);
|
||
|
||
|
||
//
|
||
// Determine the maximum sized data frame that can be sent
|
||
// on this link, based on the source routing information and
|
||
// the size of the MAC header ("data frame" means the frame
|
||
// without the MAC header). We don't assume the worst case
|
||
// about source routing since we create a link in response
|
||
// to a received frame, so if there is no source routing it
|
||
// is because we are not going over a bridge. The exception
|
||
// is if we are creating a link to a group name, in which
|
||
// case we come back later and hack the MaxFrameSize in.
|
||
//
|
||
|
||
MacReturnMaxDataSize(
|
||
&DeviceContext->MacInfo,
|
||
SourceRouting,
|
||
SourceRoutingLength,
|
||
DeviceContext->CurSendPacketSize,
|
||
FALSE,
|
||
(PUINT)&(Link->MaxFrameSize));
|
||
|
||
|
||
#if DBG
|
||
if (Link->MaxFrameSize > MaxUserPacketData) {
|
||
Link->MaxFrameSize = MaxUserPacketData;
|
||
}
|
||
#endif
|
||
|
||
// Link->Provider = DeviceContext;
|
||
|
||
//
|
||
// Build the default MAC header. I-frames go out as
|
||
// non-broadcast source routing.
|
||
//
|
||
|
||
if (SourceRouting != NULL) {
|
||
|
||
RtlCopyMemory(
|
||
TempSR,
|
||
SourceRouting,
|
||
SourceRoutingLength);
|
||
|
||
MacCreateNonBroadcastReplySR(
|
||
&DeviceContext->MacInfo,
|
||
TempSR,
|
||
SourceRoutingLength,
|
||
&ResponseSR);
|
||
|
||
} else {
|
||
|
||
ResponseSR = NULL;
|
||
|
||
}
|
||
|
||
MacConstructHeader (
|
||
&DeviceContext->MacInfo,
|
||
Link->Header,
|
||
HardwareAddress->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;
|
||
}
|
||
|
||
Link->RespondToPoll = FALSE;
|
||
Link->NumberOfConnectors = 0;
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
NbfResetLink (Link);
|
||
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
|
||
Link->ActiveConnectionCount = 0;
|
||
if (!IsListEmpty(&Link->ConnectionDatabase)) {
|
||
|
||
//
|
||
// Not good; we've got something left over...
|
||
//
|
||
#if DBG
|
||
NbfPrint1 ("NbfCreateLink: Link 0x%lx has connections at startup, disconnecting...\n", Link);
|
||
DbgBreakPoint();
|
||
#endif
|
||
//
|
||
// This won't work, the link ref count will be bad.
|
||
//
|
||
NbfStopLink (Link);
|
||
}
|
||
|
||
for (i=0; i<(USHORT)DeviceContext->MacInfo.AddressLength; i++) {
|
||
Link->HardwareAddress.Address[i] = HardwareAddress->Address[i];
|
||
}
|
||
MacReturnMagicAddress (&DeviceContext->MacInfo, HardwareAddress, &Link->MagicAddress);
|
||
|
||
//
|
||
// Determine if this is a loopback link.
|
||
//
|
||
|
||
if (RtlEqualMemory(
|
||
HardwareAddress->Address,
|
||
DeviceContext->LocalAddress.Address,
|
||
DeviceContext->MacInfo.AddressLength)) {
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock);
|
||
//
|
||
// Yes, just fill it in, no need to do deferred processing
|
||
// since this link does not go in the tree.
|
||
//
|
||
|
||
if (LoopbackLinkIndex == LISTENER_LINK) {
|
||
Link->LoopbackDestinationIndex = LOOPBACK_TO_CONNECTOR;
|
||
} else {
|
||
Link->LoopbackDestinationIndex = LOOPBACK_TO_LISTENER;
|
||
}
|
||
|
||
Link->Loopback = TRUE;
|
||
DeviceContext->LoopbackLinks[LoopbackLinkIndex] = Link;
|
||
|
||
RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock);
|
||
} else {
|
||
|
||
Link->Loopback = FALSE;
|
||
|
||
//
|
||
// Now put the link in the deferred operations queue and go away. We'll
|
||
// insert this link in the tree at some future time (soon).
|
||
//
|
||
|
||
IF_NBFDBG (NBF_DEBUG_TEARDOWN) {
|
||
NbfPrint6 ("NbfCreateLink: link to deferred queue %lx %lx %lx %lx %lx Flags: %lx \n",
|
||
Link, Link->DeferredList.Flink, Link->DeferredList.Blink,
|
||
DeviceContext->LinkDeferred.Flink, DeviceContext->LinkDeferred.Blink,
|
||
Link->Flags);
|
||
}
|
||
|
||
//
|
||
// We should not have any deferred flags yet!
|
||
//
|
||
|
||
ASSERT ((Link->DeferredFlags & LINK_FLAGS_DEFERRED_MASK) == 0);
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock);
|
||
ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
|
||
if ((Link->DeferredFlags & LINK_FLAGS_DEFERRED_DELETE) == 0) {
|
||
Link->DeferredFlags |= LINK_FLAGS_DEFERRED_ADD;
|
||
InsertTailList (&DeviceContext->LinkDeferred, &Link->DeferredList);
|
||
|
||
if (!(DeviceContext->a.i.LinkDeferredActive)) {
|
||
StartTimerLinkDeferredAdd++;
|
||
NbfStartShortTimer (DeviceContext);
|
||
DeviceContext->a.i.LinkDeferredActive = TRUE;
|
||
}
|
||
}
|
||
else {
|
||
Link->DeferredFlags = LINK_FLAGS_DEFERRED_ADD;
|
||
if (!(DeviceContext->a.i.LinkDeferredActive)) {
|
||
StartTimerLinkDeferredAdd++;
|
||
NbfStartShortTimer (DeviceContext);
|
||
DeviceContext->a.i.LinkDeferredActive = TRUE;
|
||
}
|
||
}
|
||
RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
|
||
RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock);
|
||
|
||
IF_NBFDBG (NBF_DEBUG_TEARDOWN) {
|
||
NbfPrint6 ("NbfCreateLink: link on deferred queue %lx %lx %lx %lx %lx Flags: %lx \n",
|
||
Link, Link->DeferredList.Flink, Link->DeferredList.Blink,
|
||
DeviceContext->LinkDeferred.Flink, DeviceContext->LinkDeferred.Blink,
|
||
Link->DeferredFlags);
|
||
}
|
||
|
||
}
|
||
|
||
#if PKT_LOG
|
||
RtlZeroMemory (&Link->LastNRecvs, sizeof(PKT_LOG_QUE));
|
||
RtlZeroMemory (&Link->LastNSends, sizeof(PKT_LOG_QUE));
|
||
#endif // PKT_LOG
|
||
|
||
NbfReferenceDeviceContext ("Create Link", DeviceContext, DCREF_LINK); // count refs to the device context.
|
||
*TransportLink = Link; // return a pointer to the link object.
|
||
return STATUS_SUCCESS;
|
||
} /* NbfCreateLink */
|
||
|
||
|
||
NTSTATUS
|
||
NbfDestroyLink(
|
||
IN PTP_LINK TransportLink
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine destroys a transport link and removes all references
|
||
made to it by other objects in the transport. The link is expected
|
||
to still be on the splay tree of links. This routine merely marks the
|
||
link as needing to be deleted and pushes it onto the deferred operations
|
||
queue. The deferred operations processor actually removes the link from
|
||
tree and returns the link to pool.
|
||
|
||
Arguments:
|
||
|
||
TransportLink - Pointer to a transport link structure to be destroyed.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL oldirql;
|
||
PTP_PACKET packet;
|
||
PLIST_ENTRY pkt;
|
||
PDEVICE_CONTEXT DeviceContext;
|
||
|
||
IF_NBFDBG (NBF_DEBUG_LINK) {
|
||
NbfPrint1 ("NbfDestroyLink: Entered for link %lx.\n", TransportLink);
|
||
}
|
||
|
||
#if DBG
|
||
if (TransportLink->Destroyed) {
|
||
NbfPrint1 ("attempt to destroy already-destroyed link 0x%lx\n", TransportLink);
|
||
DbgBreakPoint ();
|
||
}
|
||
TransportLink->Destroyed = TRUE;
|
||
#if 1
|
||
ACQUIRE_SPIN_LOCK (&NbfGlobalInterlock, &oldirql);
|
||
RemoveEntryList (&TransportLink->GlobalLinkage);
|
||
RELEASE_SPIN_LOCK (&NbfGlobalInterlock, oldirql);
|
||
#else
|
||
ExInterlockedRemoveHeadList (TransportLink->GlobalLinkage.Blink, &NbfGlobalInterlock);
|
||
#endif
|
||
#endif
|
||
|
||
DeviceContext = TransportLink->Provider;
|
||
|
||
//
|
||
// In case there's a holdover from the DISC link shutdown protocol
|
||
//
|
||
|
||
//
|
||
// We had better be in ADM, otherwise the reference count should
|
||
// be non-zero and what are we doing in NbfDestroyLink?
|
||
//
|
||
|
||
ASSERT(TransportLink->State == LINK_STATE_ADM);
|
||
// TransportLink->State = LINK_STATE_ADM;
|
||
|
||
StopT1 (TransportLink);
|
||
StopT2 (TransportLink);
|
||
StopTi (TransportLink);
|
||
|
||
|
||
//
|
||
// Make sure we are not in the deferred timer queue.
|
||
//
|
||
|
||
ACQUIRE_SPIN_LOCK (&DeviceContext->TimerSpinLock, &oldirql);
|
||
|
||
if (TransportLink->OnShortList) {
|
||
TransportLink->OnShortList = FALSE;
|
||
RemoveEntryList (&TransportLink->ShortList);
|
||
}
|
||
|
||
if (TransportLink->OnLongList) {
|
||
TransportLink->OnLongList = FALSE;
|
||
RemoveEntryList (&TransportLink->LongList);
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK (&DeviceContext->TimerSpinLock, oldirql);
|
||
|
||
ASSERT (!TransportLink->OnDeferredRrQueue);
|
||
|
||
//
|
||
// Now free this link object's resources.
|
||
// later, we'll spin through the WackQ and verify that sequencing
|
||
// is correct and we've gotten an implicit ack for these packets. This
|
||
// maybe should be handled in ResendLlcPackets for non-final, non-command
|
||
// packets.
|
||
//
|
||
|
||
while (!IsListEmpty (&TransportLink->WackQ)) {
|
||
pkt = RemoveHeadList (&TransportLink->WackQ);
|
||
packet = CONTAINING_RECORD (pkt, TP_PACKET, Linkage);
|
||
#if DBG
|
||
// IF_NBFDBG (NBF_DEBUG_TEARDOWN) {
|
||
NbfPrint1 ("NbfDereferenceLink: Destroying packets on Link WackQ! %lx\n", packet);
|
||
// }
|
||
#endif
|
||
NbfDereferencePacket (packet);
|
||
|
||
}
|
||
|
||
//
|
||
// The NDIS send queue should be empty!!
|
||
//
|
||
|
||
ASSERT (IsListEmpty (&TransportLink->NdisSendQueue));
|
||
|
||
#if DBG
|
||
if (!IsListEmpty (&TransportLink->ConnectionDatabase)) {
|
||
NbfPrint1 ("NbfDestroyLink: link 0x%lx still has connections\n", TransportLink);
|
||
DbgBreakPoint ();
|
||
}
|
||
#endif
|
||
|
||
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);
|
||
|
||
DeviceContext->LinkTotal += DeviceContext->LinkInUse;
|
||
++DeviceContext->LinkSamples;
|
||
ASSERT(DeviceContext->LinkInUse > 0);
|
||
--DeviceContext->LinkInUse;
|
||
|
||
ASSERT(DeviceContext->LinkAllocated > DeviceContext->LinkInUse);
|
||
|
||
if ((DeviceContext->LinkAllocated - DeviceContext->LinkInUse) >
|
||
DeviceContext->LinkInitAllocated) {
|
||
NbfDeallocateLink (DeviceContext, TransportLink);
|
||
IF_NBFDBG (NBF_DEBUG_DYNAMIC) {
|
||
NbfPrint1 ("NBF: Deallocated link at %lx\n", TransportLink);
|
||
}
|
||
} else {
|
||
InsertTailList (&DeviceContext->LinkPool, &TransportLink->Linkage);
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
|
||
|
||
NbfDereferenceDeviceContext ("Destroy Link", DeviceContext, DCREF_LINK); // just housekeeping.
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} /* NbfDestroyLink */
|
||
|
||
|
||
VOID
|
||
NbfDisconnectLink(
|
||
IN PTP_LINK Link
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine calls the data link provider to disconnect a data link
|
||
connection associated with a TP_LINK object.
|
||
|
||
Arguments:
|
||
|
||
Link - Pointer to a transport link object.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL oldirql;
|
||
|
||
IF_NBFDBG (NBF_DEBUG_LINK) {
|
||
NbfPrint1 ("NbfDisconnectLink: Entered for link %lx.\n", Link);
|
||
}
|
||
|
||
ACQUIRE_SPIN_LOCK (&Link->SpinLock, &oldirql);
|
||
|
||
if ((Link->Flags & LINK_FLAGS_LOCAL_DISC) != 0) {
|
||
|
||
Link->Flags &= ~LINK_FLAGS_LOCAL_DISC;
|
||
|
||
if (Link->State == LINK_STATE_ADM) {
|
||
|
||
RELEASE_SPIN_LOCK (&Link->SpinLock, oldirql);
|
||
|
||
} else {
|
||
|
||
PLIST_ENTRY p;
|
||
PTP_PACKET packet;
|
||
|
||
Link->State = LINK_STATE_W_DISC_RSP; // we are awaiting a DISC/f.
|
||
Link->SendState = SEND_STATE_DOWN;
|
||
Link->ReceiveState = RECEIVE_STATE_DOWN;
|
||
StopT1 (Link);
|
||
StopT2 (Link);
|
||
StopTi (Link);
|
||
|
||
//
|
||
// check for left over packets on the link WackQ; we'll never get
|
||
// acked for these if the link is in W_DISC_RSP.
|
||
//
|
||
|
||
while (!IsListEmpty (&Link->WackQ)) {
|
||
p = RemoveHeadList (&Link->WackQ);
|
||
RELEASE_SPIN_LOCK (&Link->SpinLock, oldirql);
|
||
packet = CONTAINING_RECORD (p, TP_PACKET, Linkage);
|
||
NbfDereferencePacket (packet);
|
||
ACQUIRE_SPIN_LOCK (&Link->SpinLock, &oldirql);
|
||
}
|
||
|
||
Link->SendRetries = (UCHAR)Link->LlcRetries;
|
||
StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); // retransmit timer.
|
||
RELEASE_SPIN_LOCK (&Link->SpinLock, oldirql);
|
||
NbfSendDisc (Link, TRUE); // send DISC-c/p.
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
RELEASE_SPIN_LOCK (&Link->SpinLock, oldirql);
|
||
|
||
}
|
||
|
||
} /* NbfDisconnectLink */
|
||
|
||
#if DBG
|
||
|
||
VOID
|
||
NbfRefLink(
|
||
IN PTP_LINK TransportLink
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine increments the reference count on a transport link. If we are
|
||
currently in the state waiting for disconnect response, we do not
|
||
reference; this avoids the link "bouncing" during disconnect (trying to
|
||
disconnect multiple times).
|
||
|
||
Arguments:
|
||
|
||
TransportLink - Pointer to a transport link object.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
LONG result;
|
||
|
||
IF_NBFDBG (NBF_DEBUG_LINK) {
|
||
NbfPrint2 ("NbfReferenceLink: Entered for link %lx, current level=%ld.\n",
|
||
TransportLink, TransportLink->ReferenceCount);
|
||
}
|
||
|
||
#if DBG
|
||
StoreLinkHistory( TransportLink, TRUE );
|
||
#endif
|
||
|
||
result = InterlockedIncrement (&TransportLink->ReferenceCount);
|
||
|
||
if (result == 0) {
|
||
|
||
//
|
||
// The first increment causes us to increment the
|
||
// "ref count is not zero" special ref.
|
||
//
|
||
|
||
NbfReferenceLinkSpecial ("first ref", TransportLink, LREF_SPECIAL_TEMP);
|
||
|
||
}
|
||
|
||
ASSERT (result >= 0);
|
||
|
||
} /* NbfRefLink */
|
||
#endif
|
||
|
||
|
||
VOID
|
||
NbfDerefLink(
|
||
IN PTP_LINK TransportLink
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine dereferences a transport link by decrementing the
|
||
reference count contained in the structure.
|
||
|
||
There are two special reference counts, 1 and 0. If, after dereferencing,
|
||
the reference count is one (1), then we initiate a disconnect protocol
|
||
sequence (DISC/UA) to terminate the connection. When this request
|
||
completes, the completion routine will dereference the link object again.
|
||
While this protocol is in progress, we will not allow the link to be
|
||
incremented again.
|
||
|
||
If the reference count becomes 0 after dereferencing, then we are in
|
||
the disconnection request completion handler, and we should actually
|
||
destroy the link object. We place the link on the deferred operations
|
||
queue and let the link get deleted later at a safe time.
|
||
|
||
Warning: Watch out for cases where a link is going down, and it is
|
||
suddenly needed again. Keep a bitflag for that in the link object.
|
||
|
||
Arguments:
|
||
|
||
TransportLink - Pointer to a transport link object.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
LONG result;
|
||
|
||
IF_NBFDBG (NBF_DEBUG_LINK) {
|
||
NbfPrint2 ("NbfDereferenceLink: Entered for link %lx, current level=%ld.\n",
|
||
TransportLink, TransportLink->ReferenceCount);
|
||
}
|
||
|
||
#if DBG
|
||
StoreLinkHistory( TransportLink, FALSE );
|
||
#endif
|
||
|
||
result = InterlockedDecrement(&TransportLink->ReferenceCount);
|
||
|
||
//
|
||
// If all the normal references to this link are gone, then
|
||
// we can remove the special reference that stood for
|
||
// "the regular ref count is non-zero".
|
||
//
|
||
|
||
|
||
if (result < 0) {
|
||
|
||
//
|
||
// If the refcount is -1 we want to call DisconnectLink,
|
||
// we do this before removing the special ref so that
|
||
// the link does not go away during the call.
|
||
//
|
||
|
||
IF_NBFDBG (NBF_DEBUG_LINK) {
|
||
NbfPrint0 ("NbfDereferenceLink: refcnt=1, disconnecting Link object.\n");
|
||
}
|
||
|
||
NbfDisconnectLink (TransportLink);
|
||
|
||
//
|
||
// Now it is OK to let the link go away.
|
||
//
|
||
|
||
NbfDereferenceLinkSpecial ("Regular ref 0", TransportLink, LREF_SPECIAL_TEMP);
|
||
|
||
}
|
||
|
||
} /* NbfDerefLink */
|
||
|
||
|
||
VOID
|
||
NbfRefLinkSpecial(
|
||
IN PTP_LINK TransportLink
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine increments the special reference count on a transport link.
|
||
|
||
Arguments:
|
||
|
||
TransportLink - Pointer to a transport link object.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG result;
|
||
|
||
IF_NBFDBG (NBF_DEBUG_LINK) {
|
||
NbfPrint3 ("NbfRefLinkSpecial: Entered for link %lx, current level=%ld (%ld).\n",
|
||
TransportLink, TransportLink->ReferenceCount, TransportLink->SpecialRefCount);
|
||
}
|
||
|
||
#if DBG
|
||
StoreLinkHistory( TransportLink, TRUE );
|
||
#endif
|
||
|
||
result = ExInterlockedAddUlong (
|
||
(PULONG)&TransportLink->SpecialRefCount,
|
||
1,
|
||
TransportLink->ProviderInterlock);
|
||
|
||
} /* NbfRefLinkSpecial */
|
||
|
||
|
||
VOID
|
||
NbfDerefLinkSpecial(
|
||
IN PTP_LINK TransportLink
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine dereferences a transport link by decrementing the
|
||
special reference count contained in the structure.
|
||
|
||
The special reference may be decremented at any time, however
|
||
the effect of those dereferences only happen when the normal
|
||
reference count is 0, to prevent the link from going away
|
||
while the operations due to the ->0 transition of the
|
||
normal reference count are done.
|
||
|
||
If the special reference count becomes 0 after dereferencing, then we
|
||
are in the disconnection request completion handler, and we should actually
|
||
destroy the link object. We place the link on the deferred operations
|
||
queue and let the link get deleted later at a safe time.
|
||
|
||
Warning: Watch out for cases where a link is going down, and it is
|
||
suddenly needed again. Keep a bitflag for that in the link object.
|
||
|
||
Arguments:
|
||
|
||
TransportLink - Pointer to a transport link object.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL oldirql, oldirql1;
|
||
ULONG OldRefCount;
|
||
PDEVICE_CONTEXT DeviceContext = TransportLink->Provider;
|
||
|
||
|
||
IF_NBFDBG (NBF_DEBUG_LINK) {
|
||
NbfPrint3 ("NbfDerefLinkSpecial: Entered for link %lx, current level=%ld (%ld).\n",
|
||
TransportLink, TransportLink->ReferenceCount, TransportLink->SpecialRefCount);
|
||
}
|
||
|
||
#if DBG
|
||
StoreLinkHistory( TransportLink, FALSE );
|
||
#endif
|
||
|
||
//
|
||
// Links stay in the device context tree with a ref count
|
||
// of 0. Routines that scan this queue check the DEFERRED_DELETE
|
||
// flag, so we need to synchronize the decrementing of the
|
||
// ref count with setting that flag. DeviceContext->LinkSpinLock
|
||
// is used to synchronize this.
|
||
//
|
||
|
||
ACQUIRE_SPIN_LOCK (&DeviceContext->LinkSpinLock, &oldirql1);
|
||
|
||
OldRefCount = ExInterlockedAddUlong (
|
||
(PULONG)&TransportLink->SpecialRefCount,
|
||
(ULONG)-1,
|
||
TransportLink->ProviderInterlock);
|
||
|
||
ASSERT (OldRefCount > 0);
|
||
|
||
if ((OldRefCount == 1) &&
|
||
(TransportLink->ReferenceCount == -1)) {
|
||
|
||
if (TransportLink->Loopback) {
|
||
|
||
//
|
||
// It is a loopback link, hence not in the link
|
||
// tree so we don't need to queue a deferred removal.
|
||
//
|
||
|
||
if (TransportLink == DeviceContext->LoopbackLinks[0]) {
|
||
DeviceContext->LoopbackLinks[0] = NULL;
|
||
} else if (TransportLink == DeviceContext->LoopbackLinks[1]) {
|
||
DeviceContext->LoopbackLinks[1] = NULL;
|
||
} else {
|
||
#if DBG
|
||
NbfPrint0("Destroying unknown loopback link!!\n");
|
||
#endif
|
||
ASSERT(FALSE);
|
||
}
|
||
|
||
NbfDestroyLink (TransportLink);
|
||
RELEASE_SPIN_LOCK (&DeviceContext->LinkSpinLock, oldirql1);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Not only are all transport connections gone, but the data link
|
||
// provider does not have a reference to this object, so we can
|
||
// safely delete it from the system. Make sure we haven't already
|
||
// been here before we try to insert this link.
|
||
//
|
||
|
||
IF_NBFDBG (NBF_DEBUG_TEARDOWN) {
|
||
NbfPrint6 ("NbfDerefLink: link to deferred queue %lx %lx %lx %lx %lx Flags: %lx \n",
|
||
TransportLink, TransportLink->DeferredList.Flink,
|
||
TransportLink->DeferredList.Blink, DeviceContext->LinkDeferred.Flink,
|
||
DeviceContext->LinkDeferred.Blink, TransportLink->Flags);
|
||
}
|
||
|
||
ACQUIRE_SPIN_LOCK (&DeviceContext->TimerSpinLock, &oldirql);
|
||
if ((TransportLink->DeferredFlags & LINK_FLAGS_DEFERRED_MASK) == 0) {
|
||
|
||
TransportLink->DeferredFlags |= LINK_FLAGS_DEFERRED_DELETE;
|
||
|
||
InsertTailList (&DeviceContext->LinkDeferred, &TransportLink->DeferredList);
|
||
if (!(DeviceContext->a.i.LinkDeferredActive)) {
|
||
StartTimerLinkDeferredDelete++;
|
||
NbfStartShortTimer (DeviceContext);
|
||
DeviceContext->a.i.LinkDeferredActive = TRUE;
|
||
}
|
||
|
||
} else {
|
||
|
||
TransportLink->DeferredFlags |= LINK_FLAGS_DEFERRED_DELETE;
|
||
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK (&DeviceContext->TimerSpinLock, oldirql);
|
||
RELEASE_SPIN_LOCK (&DeviceContext->LinkSpinLock, oldirql1);
|
||
|
||
IF_NBFDBG (NBF_DEBUG_LINK) {
|
||
NbfPrint0 ("NbfDereferenceLink: refcnt=0, link placed on deferred operations queue.\n");
|
||
}
|
||
|
||
IF_NBFDBG (NBF_DEBUG_TEARDOWN) {
|
||
NbfPrint6 ("NbfDerefLink: link on deferred queue %lx %lx %lx %lx %lx Flags: %lx \n",
|
||
TransportLink, TransportLink->DeferredList.Flink,
|
||
TransportLink->DeferredList.Blink, DeviceContext->LinkDeferred.Flink,
|
||
DeviceContext->LinkDeferred.Blink, TransportLink->DeferredFlags);
|
||
}
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
RELEASE_SPIN_LOCK (&DeviceContext->LinkSpinLock, oldirql1);
|
||
|
||
}
|
||
|
||
} /* NbfDerefLinkSpecial */
|
||
|
||
|
||
NTSTATUS
|
||
NbfAssignGroupLsn(
|
||
IN PTP_CONNECTION TransportConnection
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to assign a global LSN to the connection
|
||
in question. If successful, it fills in the connection's LSN
|
||
appropriately.
|
||
|
||
Arguments:
|
||
|
||
TransportConnection - Pointer to a transport connection object.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if we got an LSN for the connection;
|
||
STATUS_INSUFFICIENT_RESOURCES if we didn't.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL oldirql;
|
||
UCHAR Lsn;
|
||
PDEVICE_CONTEXT DeviceContext;
|
||
BOOLEAN FoundLsn = FALSE;
|
||
|
||
DeviceContext = TransportConnection->Provider;
|
||
|
||
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);
|
||
|
||
//
|
||
// Scan through the device context tables to find an LSN that
|
||
// is not in use, starting with NextLsnStart+128.
|
||
//
|
||
|
||
Lsn = (UCHAR)DeviceContext->NextLsnStart;
|
||
|
||
do {
|
||
|
||
if (DeviceContext->LsnTable[Lsn] == 0) {
|
||
DeviceContext->LsnTable[Lsn] = LSN_TABLE_MAX;
|
||
FoundLsn = TRUE;
|
||
break;
|
||
}
|
||
|
||
Lsn = (Lsn % NETBIOS_SESSION_LIMIT) + 1;
|
||
|
||
} while (Lsn != DeviceContext->NextLsnStart);
|
||
|
||
DeviceContext->NextLsnStart = (DeviceContext->NextLsnStart % 64) + 1;
|
||
|
||
if (!FoundLsn) {
|
||
|
||
//
|
||
// Could not find an empty LSN; have to fail.
|
||
//
|
||
|
||
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
}
|
||
|
||
TransportConnection->Lsn = Lsn;
|
||
|
||
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NbfConnectToLink(
|
||
IN PTP_LINK Link,
|
||
IN PTP_CONNECTION TransportConnection
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to establish a linkage between a transport
|
||
connection and a transport link. We find a session number in one
|
||
of two ways. If the last connection on the link's list has a number less
|
||
than the maximum session number, we simply increment it's number and
|
||
assign it to this session. If that doesn't work, we scan through the
|
||
sessions associated with this link until we find a hole in the LSNs;
|
||
we then use the first number in that hole. If that fails, we've used
|
||
the number of sessions we can create on this link and we fail.
|
||
|
||
It is assumed that the caller holds at least temporary references
|
||
on both the connection and link objects, or they could go away during
|
||
the call sequence or during this routine's execution.
|
||
|
||
Arguments:
|
||
|
||
Link - Pointer to a transport link object.
|
||
|
||
TransportConnection - Pointer to a transport connection object.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if we got an LSN for the connection;
|
||
STATUS_INSUFFICIENT_RESOURCES if we didn't.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL oldirql;
|
||
UCHAR lastSession=0;
|
||
PTP_CONNECTION connection;
|
||
PLIST_ENTRY p;
|
||
PDEVICE_CONTEXT DeviceContext;
|
||
UCHAR Lsn;
|
||
BOOLEAN FoundLsn;
|
||
|
||
//
|
||
// Assign an LSN for a new connection. If this connection makes for more
|
||
// connections than the maximum, blow off the creation.
|
||
//
|
||
|
||
IF_NBFDBG (NBF_DEBUG_LINK) {
|
||
NbfPrint2 ("NbfConnectToLink: Entered for connection %lx, link %lx.\n",
|
||
TransportConnection, Link);
|
||
}
|
||
|
||
DeviceContext = Link->Provider;
|
||
|
||
ACQUIRE_SPIN_LOCK (&Link->SpinLock, &oldirql);
|
||
#if DBG
|
||
if (!(IsListEmpty(&TransportConnection->LinkList)) ||
|
||
(TransportConnection->Link != NULL)) {
|
||
DbgPrint ("Connecting C %lx to L %lx, appears to be in use\n", TransportConnection, Link);
|
||
DbgBreakPoint();
|
||
}
|
||
#endif
|
||
|
||
if ((TransportConnection->Flags2 & CONNECTION_FLAGS2_GROUP_LSN) == 0) {
|
||
|
||
//
|
||
// This connection is to a remote unique name, which means
|
||
// we need to assign the LSN here based on the link. We
|
||
// scan through our LSN table starting with NextLsnStart
|
||
// (which cycles from 1 to 64) to find an LSN which is not
|
||
// used by any connections on this link.
|
||
//
|
||
|
||
ASSERT (TransportConnection->Lsn == 0);
|
||
|
||
FoundLsn = FALSE;
|
||
Lsn = (UCHAR)DeviceContext->NextLsnStart;
|
||
|
||
//
|
||
// First scan through the database until we reach
|
||
// Lsn (or hit the end of the database).
|
||
//
|
||
|
||
for (p = Link->ConnectionDatabase.Flink;
|
||
p != &Link->ConnectionDatabase;
|
||
p = p->Flink) {
|
||
|
||
connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList);
|
||
if (connection->Lsn >= Lsn) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// p now points to the first element after Lsn's spot.
|
||
// We now scan forwards until we hit NETBIOS_SESSION_LIMIT,
|
||
// looking for an Lsn that is available.
|
||
//
|
||
|
||
for ( ; Lsn <= NETBIOS_SESSION_LIMIT; ++Lsn) {
|
||
|
||
//
|
||
// At some point (perhaps right away) we may
|
||
// pass the end of the database without finding
|
||
// an LSN. If we have not yet done this, see
|
||
// if we need to skip this lsn because it is
|
||
// in use by a connection on this link.
|
||
//
|
||
|
||
if (p != &Link->ConnectionDatabase) {
|
||
if (connection->Lsn == Lsn) {
|
||
p = p->Flink;
|
||
if (p != &Link->ConnectionDatabase) {
|
||
connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList);
|
||
}
|
||
continue;
|
||
}
|
||
}
|
||
|
||
//
|
||
// This lsn is not in use on this link, see if
|
||
// there is room for it to be used.
|
||
//
|
||
|
||
if (DeviceContext->LsnTable[Lsn] < LSN_TABLE_MAX) {
|
||
++(DeviceContext->LsnTable[Lsn]);
|
||
TransportConnection->Lsn = Lsn;
|
||
InsertTailList (p, &TransportConnection->LinkList);
|
||
FoundLsn = TRUE;
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
DeviceContext->NextLsnStart = (DeviceContext->NextLsnStart % 64) + 1;
|
||
|
||
} else {
|
||
|
||
//
|
||
// This connection is to a group name; we already assigned
|
||
// the LSN on a global basis.
|
||
//
|
||
|
||
FoundLsn = TRUE;
|
||
|
||
//
|
||
// Find the spot for this LSN in the database.
|
||
//
|
||
|
||
p = Link->ConnectionDatabase.Flink;
|
||
while (p != &Link->ConnectionDatabase) {
|
||
|
||
connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList);
|
||
if (TransportConnection->Lsn < connection->Lsn) {
|
||
InsertTailList (p, &TransportConnection->LinkList);
|
||
break;
|
||
}
|
||
p = p->Flink;
|
||
|
||
}
|
||
|
||
if (p == &Link->ConnectionDatabase) {
|
||
InsertTailList (&Link->ConnectionDatabase, &TransportConnection->LinkList);
|
||
}
|
||
|
||
}
|
||
|
||
if (!FoundLsn) {
|
||
|
||
ULONG DumpData = NETBIOS_SESSION_LIMIT;
|
||
|
||
ASSERT (Link->ActiveConnectionCount == NETBIOS_SESSION_LIMIT);
|
||
|
||
RELEASE_SPIN_LOCK (&Link->SpinLock, oldirql);
|
||
|
||
PANIC ("NbfConnectToLink: PANIC! too many active connections!\n");
|
||
|
||
NbfWriteGeneralErrorLog(
|
||
DeviceContext,
|
||
EVENT_TRANSPORT_TOO_MANY_LINKS,
|
||
602,
|
||
STATUS_INSUFFICIENT_RESOURCES,
|
||
NULL,
|
||
1,
|
||
&DumpData);
|
||
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
}
|
||
|
||
TransportConnection->Link = Link;
|
||
TransportConnection->LinkSpinLock = &Link->SpinLock;
|
||
TransportConnection->Flags |= CONNECTION_FLAGS_WAIT_LINK_UP;
|
||
|
||
TransportConnection->LastPacketsSent = Link->PacketsSent;
|
||
TransportConnection->LastPacketsResent = Link->PacketsResent;
|
||
|
||
Link->ActiveConnectionCount++;
|
||
|
||
//
|
||
// Note that the connection is already inserted in the
|
||
// link's ConnectionDatabase.
|
||
//
|
||
|
||
// This reference is removed in NbfDisconnectFromLink
|
||
NbfReferenceConnection("Adding link", TransportConnection, CREF_LINK);
|
||
|
||
RELEASE_SPIN_LOCK (&Link->SpinLock, oldirql);
|
||
|
||
return STATUS_SUCCESS; // we did it!
|
||
|
||
} /* NbfConnectToLink */
|
||
|
||
|
||
BOOLEAN
|
||
NbfDisconnectFromLink(
|
||
IN PTP_CONNECTION TransportConnection,
|
||
IN BOOLEAN VerifyReferenceCount
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to terminate a linkage between a transport
|
||
connection and its associated transport link. If it turns out that
|
||
this is the last connection to be removed from this link, then the
|
||
link's disconnection protocol is engaged.
|
||
|
||
Arguments:
|
||
|
||
TransportConnection - Pointer to a transport connection object.
|
||
|
||
VerifyReferenceCount - TRUE if we should check that the refcount
|
||
is still -1 before removing the connection from the link.
|
||
If it is not, it means someone just referenced us and we
|
||
exit.
|
||
|
||
Return Value:
|
||
|
||
FALSE if VerifyReferenceCount was TRUE but the refcount was
|
||
not -1; TRUE otherwise.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL oldirql, oldirql1;
|
||
PTP_LINK Link;
|
||
|
||
IF_NBFDBG (NBF_DEBUG_LINK) {
|
||
NbfPrint2 ("NbfDisconnectFromLink: Entered for connection %lx, link %lx.\n",
|
||
TransportConnection, TransportConnection->Link);
|
||
}
|
||
|
||
ACQUIRE_C_SPIN_LOCK (&TransportConnection->SpinLock, &oldirql);
|
||
Link = TransportConnection->Link;
|
||
if (Link != NULL) {
|
||
|
||
ACQUIRE_SPIN_LOCK (&Link->SpinLock, &oldirql1);
|
||
|
||
if ((VerifyReferenceCount) &&
|
||
(TransportConnection->ReferenceCount != -1)) {
|
||
|
||
RELEASE_SPIN_LOCK (&Link->SpinLock, oldirql1);
|
||
RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql);
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
TransportConnection->Link = NULL;
|
||
TransportConnection->LinkSpinLock = NULL;
|
||
RemoveEntryList (&TransportConnection->LinkList);
|
||
#if DBG
|
||
InitializeListHead (&TransportConnection->LinkList);
|
||
#endif
|
||
|
||
//
|
||
// If this was the last connection being serviced by this link,
|
||
// then we can shut the link down. It still has a reference
|
||
// from the device context, which will go away in the DM/UA
|
||
// DLC frame handler.
|
||
//
|
||
|
||
if (--Link->ActiveConnectionCount == 0) {
|
||
|
||
//
|
||
// only want to send DISC if the remote was NOT the originator
|
||
// of the disconnect.
|
||
//
|
||
|
||
if ((TransportConnection->Status == STATUS_LOCAL_DISCONNECT) ||
|
||
(TransportConnection->Status == STATUS_CANCELLED)) {
|
||
|
||
//
|
||
// This is a local disconnect of the last connection
|
||
// on the link, let's get the disconnect ball rolling.
|
||
//
|
||
|
||
Link->Flags |= LINK_FLAGS_LOCAL_DISC;
|
||
|
||
//
|
||
// When the link reference count drops down to 1,
|
||
// that will cause the DISC to get sent.
|
||
//
|
||
|
||
}
|
||
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK (&Link->SpinLock, oldirql1);
|
||
|
||
//
|
||
// Clear these now that we are off the link's database.
|
||
//
|
||
|
||
NbfClearConnectionLsn (TransportConnection);
|
||
TransportConnection->Rsn = 0;
|
||
|
||
RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql);
|
||
|
||
if ((TransportConnection->Flags2 & CONNECTION_FLAGS2_CONNECTOR) != 0) {
|
||
|
||
(VOID)InterlockedDecrement(&Link->NumberOfConnectors);
|
||
}
|
||
|
||
//
|
||
// All done with this connection's reference to link.
|
||
//
|
||
|
||
NbfDereferenceLink ("Disconnecting connection",Link, LREF_CONNECTION);
|
||
|
||
} else {
|
||
|
||
//
|
||
// A group LSN may have been assigned even though Link is NULL.
|
||
//
|
||
|
||
if ((TransportConnection->Flags2 & CONNECTION_FLAGS2_GROUP_LSN) != 0) {
|
||
NbfClearConnectionLsn (TransportConnection);
|
||
}
|
||
|
||
RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql);
|
||
|
||
}
|
||
|
||
return TRUE;
|
||
|
||
} /* NbfDisconnectFromLink */
|
||
|
||
|
||
PTP_CONNECTION
|
||
NbfLookupPendingListenOnLink(
|
||
IN PTP_LINK Link
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine scans the LSN database on a transport link object to find
|
||
a TP_CONNECTION object which has the CONNECTION_FLAGS_WAIT_LINK_UP and
|
||
CONNECTION_FLAGS2_LISTENER flags set. It returns a pointer to the found
|
||
connection object (and simultaneously resets the LINK_UP flag) or NULL
|
||
if it could not be found. The reference count is also incremented
|
||
atomically on the connection.
|
||
|
||
NOTE: THIS ROUTINE MUST BE CALLED FROM DPC LEVEL.
|
||
|
||
Arguments:
|
||
|
||
Link - Pointer to a transport link object.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
PTP_CONNECTION connection;
|
||
PLIST_ENTRY p;
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
|
||
for (p = Link->ConnectionDatabase.Flink;
|
||
p != &Link->ConnectionDatabase;
|
||
p = p->Flink) {
|
||
connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList);
|
||
if ((connection->Flags & CONNECTION_FLAGS_WAIT_LINK_UP) &&
|
||
(connection->Flags2 & CONNECTION_FLAGS2_LISTENER) &&
|
||
((connection->Flags2 & CONNECTION_FLAGS2_STOPPING) == 0)) {
|
||
// This reference is removed by the calling function
|
||
NbfReferenceConnection ("Found Pending Listen", connection, CREF_P_LINK);
|
||
connection->Flags &= ~CONNECTION_FLAGS_WAIT_LINK_UP;
|
||
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
return connection;
|
||
}
|
||
}
|
||
|
||
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
|
||
return NULL;
|
||
|
||
} /* NbfLookupPendingListenOnLink */
|
||
|
||
|
||
PTP_CONNECTION
|
||
NbfLookupPendingConnectOnLink(
|
||
IN PTP_LINK Link
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine scans the LSN database on a transport link object to find
|
||
a TP_CONNECTION object which has the CONNECTION_FLAGS_WAIT_LINK_UP and
|
||
CONNECTION_FLAGS2_CONNECTOR flags set. It returns a pointer to the found
|
||
connection object (and simultaneously resets the LINK_UP flag) or NULL
|
||
if it could not be found. The reference count is also incremented
|
||
atomically on the connection.
|
||
|
||
NOTE: THIS ROUTINE MUST BE CALLED FROM DPC LEVEL.
|
||
|
||
Arguments:
|
||
|
||
Link - Pointer to a transport link object.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - status of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
PTP_CONNECTION connection;
|
||
PLIST_ENTRY p;
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
|
||
for (p = Link->ConnectionDatabase.Flink;
|
||
p != &Link->ConnectionDatabase;
|
||
p = p->Flink) {
|
||
connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList);
|
||
if ((connection->Flags & CONNECTION_FLAGS_WAIT_LINK_UP) &&
|
||
(connection->Flags2 & CONNECTION_FLAGS2_CONNECTOR) &&
|
||
((connection->Flags2 & CONNECTION_FLAGS2_STOPPING) == 0)) {
|
||
// This reference is removed by the calling function
|
||
NbfReferenceConnection ("Found pending Connect", connection, CREF_P_CONNECT);
|
||
connection->Flags &= ~CONNECTION_FLAGS_WAIT_LINK_UP;
|
||
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
return connection;
|
||
}
|
||
}
|
||
|
||
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
|
||
return NULL;
|
||
|
||
} /* NbfLookupPendingConnectOnLink */
|
||
|
||
|
||
VOID
|
||
NbfActivateLink(
|
||
IN PTP_LINK Link
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine activates a link if it is not already active. The other
|
||
related routines, NbfCreateLink and NbfConnectToLink, simply set up data
|
||
structures which represent active links so that we can reuse links
|
||
wherever possible.
|
||
|
||
NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL.
|
||
|
||
Arguments:
|
||
|
||
Link - Pointer to a transport link object.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
IF_NBFDBG (NBF_DEBUG_LINK) {
|
||
NbfPrint1 ("NbfActivateLink: Entered for link %lx.\n", Link);
|
||
}
|
||
|
||
ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL);
|
||
|
||
switch (Link->State) {
|
||
case LINK_STATE_READY:
|
||
NbfCompleteLink (Link);
|
||
break;
|
||
|
||
case LINK_STATE_ADM:
|
||
|
||
// Moving out of ADM, add reference
|
||
|
||
NbfReferenceLinkSpecial("Wait on ADM", Link, LREF_NOT_ADM);
|
||
|
||
//
|
||
// Intentionally fall through to the next case.
|
||
//
|
||
|
||
case LINK_STATE_W_DISC_RSP:
|
||
case LINK_STATE_CONNECTING:
|
||
ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
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
|
||
break;
|
||
|
||
}
|
||
} /* NbfActivateLink */
|
||
|
||
|
||
VOID
|
||
NbfWaitLink(
|
||
IN PTP_LINK Link
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine waits for a remote link activation if it is not already
|
||
active.
|
||
|
||
NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL.
|
||
|
||
Arguments:
|
||
|
||
Link - Pointer to a transport link object.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
IF_NBFDBG (NBF_DEBUG_LINK) {
|
||
NbfPrint1 ("NbfWaitLink: Entered for link %lx.\n", Link);
|
||
}
|
||
|
||
ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL);
|
||
|
||
switch (Link->State) {
|
||
case LINK_STATE_READY:
|
||
NbfCompleteLink (Link);
|
||
break;
|
||
|
||
case LINK_STATE_W_DISC_RSP:
|
||
ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
Link->State = LINK_STATE_CONNECTING;
|
||
Link->SendState = SEND_STATE_DOWN;
|
||
Link->ReceiveState = RECEIVE_STATE_DOWN;
|
||
NbfSendSabme (Link, TRUE); // send SABME/p, StartT1, release lock
|
||
break;
|
||
|
||
}
|
||
} /* NbfWaitLink */
|
||
|
||
|
||
VOID
|
||
NbfStopLink(
|
||
IN PTP_LINK Link
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine terminates a link and all outstanding connections attached
|
||
to the link. It is called from routines such as ExpireT2Timer, because
|
||
the remote connection partner seems dead or inoperative. As a consequence
|
||
of this routine being called, every outstanding connection will have its
|
||
disconnect handler called (in NbfStopConnection).
|
||
|
||
NOTE: THIS ROUTINE MUST BE CALLED FROM DPC LEVEL.
|
||
|
||
Arguments:
|
||
|
||
Link - Pointer to a transport link object.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY p;
|
||
PTP_PACKET packet;
|
||
PTP_CONNECTION connection;
|
||
|
||
IF_NBFDBG (NBF_DEBUG_LINK) {
|
||
NbfPrint1 ("NbfStopLink: Entered for link %lx.\n", Link);
|
||
}
|
||
|
||
ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL);
|
||
|
||
// Take a reference so the link won't go away inside this function
|
||
|
||
NbfReferenceLink("Temp in NbfStopLink", Link, LREF_STOPPING);
|
||
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
|
||
StopT1 (Link);
|
||
StopT2 (Link);
|
||
StopTi (Link);
|
||
|
||
p = RemoveHeadList (&Link->ConnectionDatabase);
|
||
|
||
while (p != &Link->ConnectionDatabase) {
|
||
|
||
//
|
||
// This will allow this connection to be "removed"
|
||
// from its link's list in NbfDisconnectFromLink, even if
|
||
// its not on a list.
|
||
//
|
||
InitializeListHead (p);
|
||
|
||
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList);
|
||
IF_NBFDBG (NBF_DEBUG_TEARDOWN) {
|
||
NbfPrint1 ("NbfStopLink stopping connection, refcnt=%ld",
|
||
connection->ReferenceCount);
|
||
}
|
||
#if DBG
|
||
if (NbfDisconnectDebug) {
|
||
STRING remoteName, localName;
|
||
remoteName.Length = NETBIOS_NAME_LENGTH - 1;
|
||
remoteName.Buffer = connection->RemoteName;
|
||
localName.Length = NETBIOS_NAME_LENGTH - 1;
|
||
localName.Buffer = connection->AddressFile->Address->NetworkName->NetbiosName;
|
||
NbfPrint2( "TpStopLink stopping connection to %S from %S\n",
|
||
&remoteName, &localName );
|
||
}
|
||
#endif
|
||
NbfStopConnection (connection, STATUS_LINK_FAILED);
|
||
ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
p = RemoveHeadList (&Link->ConnectionDatabase);
|
||
}
|
||
|
||
//
|
||
// We hold the link spinlock here.
|
||
//
|
||
|
||
//
|
||
// check for left over packets on the link WackQ; we'll never get
|
||
// acked for these if the link is in ADM mode.
|
||
//
|
||
|
||
while (!IsListEmpty (&Link->WackQ)) {
|
||
p = RemoveHeadList (&Link->WackQ);
|
||
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
packet = CONTAINING_RECORD (p, TP_PACKET, Linkage);
|
||
NbfDereferencePacket (packet);
|
||
ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
}
|
||
|
||
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
|
||
StopT1 (Link);
|
||
StopT2 (Link);
|
||
StopTi (Link);
|
||
|
||
|
||
//
|
||
// Make sure we are not waiting for a deferred RR to be sent.
|
||
//
|
||
|
||
if (Link->OnDeferredRrQueue) {
|
||
|
||
ACQUIRE_DPC_SPIN_LOCK (Link->ProviderInterlock);
|
||
if (Link->OnDeferredRrQueue) {
|
||
RemoveEntryList (&Link->DeferredRrLinkage);
|
||
Link->OnDeferredRrQueue = FALSE;
|
||
}
|
||
RELEASE_DPC_SPIN_LOCK (Link->ProviderInterlock);
|
||
|
||
}
|
||
|
||
// Remove the temporary reference.
|
||
|
||
NbfDereferenceLink ("Temp in NbfStopLink", Link, LREF_STOPPING);
|
||
|
||
|
||
} /* NbfStopLink */
|
||
|
||
|
||
VOID
|
||
NbfResetLink(
|
||
IN PTP_LINK Link
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by DLC.C routines only to reset this link
|
||
object and restart in-progress transport data transfers.
|
||
|
||
NOTE: This routine is called with the link spinlock acquired
|
||
at *OldIrqlP, and will return with it held, although it may
|
||
release it in the interim.
|
||
|
||
Arguments:
|
||
|
||
Link - Pointer to a transport link object.
|
||
|
||
OldIrqlP - Pointer to where the IRQL at which Link->SpinLock
|
||
was acquired is stored.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
PTP_PACKET packet;
|
||
PLIST_ENTRY p;
|
||
|
||
IF_NBFDBG (NBF_DEBUG_LINK) {
|
||
NbfPrint1 ("NbfResetLink: Entered for link %lx.\n", Link);
|
||
}
|
||
|
||
//
|
||
// Reset the link state to waiting for connection to start.
|
||
// Note that this is NOT the same as initiating a new link, as some things
|
||
// don't change, such as provider (devicecontext binding stays the same),
|
||
// Max Packet Length (can't change if provider doesn't), and other things
|
||
// that would bind this link structure to a different provider or provider
|
||
// type. Note also that we acquire the spinlock because, in the case of a
|
||
// link that's dropped (remotely) and is restarting, activities on this
|
||
// link could be occurring while we're in this routine.
|
||
//
|
||
|
||
StopT1 (Link);
|
||
StopT2 (Link);
|
||
// StopTi (Link);
|
||
Link->Flags = 0; // clear this, keep DeferredFlags
|
||
|
||
Link->SendState = SEND_STATE_DOWN; // send side is down.
|
||
Link->NextSend = 0;
|
||
Link->LastAckReceived = 0;
|
||
if (Link->Provider->MacInfo.MediumAsync) {
|
||
Link->SendWindowSize = (UCHAR)Link->Provider->RecommendedSendWindow;
|
||
Link->PrevWindowSize = (UCHAR)Link->Provider->RecommendedSendWindow;
|
||
} else {
|
||
Link->SendWindowSize = (UCHAR)1;
|
||
Link->PrevWindowSize = (UCHAR)1;
|
||
}
|
||
Link->WindowsUntilIncrease = 1;
|
||
Link->LinkBusy = FALSE;
|
||
Link->ConsecutiveLastPacketLost = 0;
|
||
|
||
//
|
||
// check for left over packets on the link WackQ; we'll never get
|
||
// acked for these if the link is resetting.
|
||
//
|
||
|
||
while (!IsListEmpty (&Link->WackQ)) {
|
||
p = RemoveHeadList (&Link->WackQ);
|
||
RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
packet = CONTAINING_RECORD (p, TP_PACKET, Linkage);
|
||
NbfDereferencePacket (packet);
|
||
ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock);
|
||
}
|
||
|
||
Link->ReceiveState = RECEIVE_STATE_DOWN; // receive side down.
|
||
Link->NextReceive = 0;
|
||
Link->LastAckSent = 0;
|
||
Link->ReceiveWindowSize = 1;
|
||
|
||
Link->WindowErrors = 0;
|
||
Link->BestWindowSize = 1;
|
||
Link->WorstWindowSize = (UCHAR)Link->MaxWindowSize;
|
||
Link->Flags |= LINK_FLAGS_JUMP_START;
|
||
|
||
//
|
||
// This must be accurate before we set up timeouts.
|
||
//
|
||
|
||
Link->CurrentT1Timeout = Link->Provider->DefaultT1Timeout;
|
||
Link->BaseT1Timeout = Link->Provider->DefaultT1Timeout << DLC_TIMER_ACCURACY;
|
||
Link->MinimumBaseT1Timeout = Link->Provider->MinimumT1Timeout << DLC_TIMER_ACCURACY;
|
||
Link->BaseT1RecalcThreshhold = Link->MaxFrameSize / 2;
|
||
Link->CurrentPollRetransmits = 0;
|
||
Link->CurrentT1Backoff = FALSE;
|
||
Link->CurrentPollOutstanding = FALSE;
|
||
Link->RemoteNoPoll = TRUE;
|
||
Link->ConsecutiveIFrames = 0;
|
||
Link->T2Timeout = Link->Provider->DefaultT2Timeout;
|
||
Link->TiTimeout = Link->Provider->DefaultTiTimeout;
|
||
Link->LlcRetries = Link->Provider->LlcRetries;
|
||
Link->MaxWindowSize = Link->Provider->LlcMaxWindowSize;
|
||
|
||
Link->SendRetries = (UCHAR)Link->LlcRetries;
|
||
|
||
} /* NbfResetLink */
|
||
|
||
|
||
VOID
|
||
NbfDumpLinkInfo (
|
||
IN PTP_LINK Link
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called when any of the link timers fire and the
|
||
link send state is not ready. This gives us a way to track the
|
||
link state when strange things are happening.
|
||
|
||
Arguments:
|
||
|
||
Link - Pointer to a transport link object.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
{
|
||
Link; // avoid compiler warnings in non-debug versions
|
||
|
||
#if DBG
|
||
NbfPrint4 ("NbfDumpLinkInfo: Link %lx : State: %x SendState: %x ReceiveState: %x\n",
|
||
Link, Link->State, Link->SendState, Link->ReceiveState);
|
||
NbfPrint1 (" Flags: %lx\n",Link->Flags);
|
||
NbfPrint4 (" NextReceive: %d LastAckRcvd: %d NextSend: %d LastAckSent: %d\n",
|
||
Link->NextReceive, Link->LastAckReceived, Link->NextSend, Link->LastAckSent);
|
||
#endif
|
||
|
||
}
|