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.
2327 lines
65 KiB
2327 lines
65 KiB
/*++
|
|
|
|
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
|
|
|
|
}
|