diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/tdi/nbf/link.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/ntos/tdi/nbf/link.c')
-rw-r--r-- | private/ntos/tdi/nbf/link.c | 2315 |
1 files changed, 2315 insertions, 0 deletions
diff --git a/private/ntos/tdi/nbf/link.c b/private/ntos/tdi/nbf/link.c new file mode 100644 index 000000000..e99bbb8f4 --- /dev/null +++ b/private/ntos/tdi/nbf/link.c @@ -0,0 +1,2315 @@ +/*++ + +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. + // + + // BUGBUG: don't forget to check for bringing it back up again. + + // BUGBUG: Is this right??? - ADB 6/26 + + 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), + 'lFBN'); + 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)); + + ++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. + +--*/ + +{ + + 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)) { + + Link = DeviceContext->LoopbackLinks[LoopbackLinkIndex]; + + if (Link != (PTP_LINK)NULL) { + + // + // Add a reference to simulate the one from NbfFindLink + // + // BUGBUG: This needs to be atomically done with + // the assignment above. + // + + NbfReferenceLink ("Found loopback link", Link, LREF_TREE); + + } else { + + // + // 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, + ' FBN'); + 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 + // + // BUGBUG: 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)) { + + // + // 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; + + } 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); + } + + } + + 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. + // BUGBUG: 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); + } + + RELEASE_SPIN_LOCK (&DeviceContext->LinkSpinLock, oldirql1); + + NbfDestroyLink (TransportLink); + + } 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); + } + + if ((TransportLink->DeferredFlags & LINK_FLAGS_DEFERRED_MASK) == 0) { + + TransportLink->DeferredFlags |= LINK_FLAGS_DEFERRED_DELETE; + + ACQUIRE_SPIN_LOCK (&DeviceContext->TimerSpinLock, &oldirql); + InsertTailList (&DeviceContext->LinkDeferred, &TransportLink->DeferredList); + if (!(DeviceContext->a.i.LinkDeferredActive)) { + StartTimerLinkDeferredDelete++; + NbfStartShortTimer (DeviceContext); + DeviceContext->a.i.LinkDeferredActive = TRUE; + } + RELEASE_SPIN_LOCK (&DeviceContext->TimerSpinLock, oldirql); + + } else { + + TransportLink->DeferredFlags |= LINK_FLAGS_DEFERRED_DELETE; + + } + + 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 + +} |