summaryrefslogtreecommitdiffstats
path: root/private/ntos/tdi/isnp/nb/timer.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/tdi/isnp/nb/timer.c')
-rw-r--r--private/ntos/tdi/isnp/nb/timer.c1233
1 files changed, 1233 insertions, 0 deletions
diff --git a/private/ntos/tdi/isnp/nb/timer.c b/private/ntos/tdi/isnp/nb/timer.c
new file mode 100644
index 000000000..381b120e5
--- /dev/null
+++ b/private/ntos/tdi/isnp/nb/timer.c
@@ -0,0 +1,1233 @@
+/*++
+
+Copyright (c) 1989-1993 Microsoft Corporation
+
+Module Name:
+
+ timer.c
+
+Abstract:
+
+ This module contains code which implements the timers for
+ netbios.
+
+Environment:
+
+ Kernel mode
+
+Revision History:
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+
+ULONG NbiTickIncrement = 0;
+ULONG NbiShortTimerDeltaTicks = 0;
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(INIT,NbiInitializeTimers)
+#endif
+
+
+VOID
+NbiStartRetransmit(
+ IN PCONNECTION Connection
+ )
+
+/*++
+
+Routine Description:
+
+ This routine starts the retransmit timer for the given connection.
+ The connection is inserted on the short list if it isn't on already.
+
+ NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL.
+
+Arguments:
+
+ Connection - pointer to the connection.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PDEVICE Device = NbiDevice;
+ NB_DEFINE_LOCK_HANDLE (LockHandle)
+
+ //
+ // Insert us in the queue if we aren't in it.
+ //
+
+ Connection->Retransmit =
+ Device->ShortAbsoluteTime + Connection->CurrentRetransmitTimeout;
+
+ if (!Connection->OnShortList) {
+
+ CTEAssert (KeGetCurrentIrql() == DISPATCH_LEVEL);
+
+ NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle);
+
+ if (!Connection->OnShortList) {
+ Connection->OnShortList = TRUE;
+ InsertTailList (&Device->ShortList, &Connection->ShortList);
+ }
+
+ if (!Device->ShortListActive) {
+ NbiStartShortTimer (Device);
+ Device->ShortListActive = TRUE;
+ }
+
+ NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle);
+ }
+
+} /* NbiStartRetransmit */
+
+
+VOID
+NbiStartWatchdog(
+ IN PCONNECTION Connection
+ )
+
+/*++
+
+Routine Description:
+
+ This routine starts the watchdog timer for a connection.
+
+ NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL.
+
+Arguments:
+
+ Connection - pointer to the connection.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PDEVICE Device = NbiDevice;
+ NB_DEFINE_LOCK_HANDLE (LockHandle);
+
+
+ Connection->Watchdog = Device->LongAbsoluteTime + Connection->WatchdogTimeout;
+
+ if (!Connection->OnLongList) {
+
+ ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL);
+
+ NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle);
+
+ if (!Connection->OnLongList) {
+ Connection->OnLongList = TRUE;
+ InsertTailList (&Device->LongList, &Connection->LongList);
+ }
+
+ NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle);
+ }
+
+} /* NbiStartWatchdog */
+
+#if DBG
+
+VOID
+NbiStopRetransmit(
+ IN PCONNECTION Connection
+ )
+
+/*++
+
+Routine Description:
+
+ This routine stops the retransmit timer for a connection.
+
+Arguments:
+
+ Connection - pointer to the connection.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ Connection->Retransmit = 0;
+
+} /* NbiStopRetransmit */
+
+
+VOID
+NbiStopWatchdog(
+ IN PCONNECTION Connection
+ )
+
+/*++
+
+Routine Description:
+
+ This routine stops the watchdog timer for a connection.
+
+Arguments:
+
+ Connection - pointer to the connection.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ Connection->Watchdog = 0;
+
+} /* NbiStopWatchdog */
+#endif
+
+
+VOID
+NbiExpireRetransmit(
+ IN PCONNECTION Connection
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called when the connection's retransmit timer
+ expires. It is called from NbiShortTimeout.
+
+Arguments:
+
+ Connection - Pointer to the connection whose timer has expired.
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ PDEVICE Device = NbiDevice;
+ BOOLEAN SendFindRoute;
+ NB_DEFINE_LOCK_HANDLE (LockHandle);
+
+ NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle);
+
+ if (Connection->State == CONNECTION_STATE_ACTIVE) {
+
+ SendFindRoute = FALSE;
+
+ ++Device->Statistics.ResponseTimerExpirations;
+
+ if (!(Connection->NewNetbios) &&
+ (Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK)) {
+
+ if (--Connection->Retries == 0) {
+
+ //
+ // Shut down the connection. This will send
+ // out half the usual number of session end
+ // frames.
+ //
+
+ NB_DEBUG2 (CONNECTION, ("Wait for ack timeout of active connection %lx\n", Connection));
+
+ //
+ // This free the connection lock.
+ //
+
+ NbiStopConnection(
+ Connection,
+ STATUS_LINK_FAILED
+ NB_LOCK_HANDLE_ARG (LockHandle)
+ );
+
+ } else {
+
+ //
+ // Set our current packetize location back to the
+ // spot of the last ack, and start up again.
+ //
+ // BUGBUG: Should we send a probe here?
+ //
+
+ Connection->CurrentSend = Connection->UnAckedSend;
+ Connection->RetransmitThisWindow = TRUE;
+ if (Connection->CurrentRetransmitTimeout < (Connection->BaseRetransmitTimeout*8)) {
+ Connection->CurrentRetransmitTimeout =
+ (Connection->CurrentRetransmitTimeout * 3) / 2;
+ }
+
+ NB_DEBUG2 (SEND, ("Connection %lx retransmit timeout\n", Connection));
+
+ //
+ // After half the retries, send a find route unless we
+ // are already doing one, or the connection is to network
+ // 0. When this completes we update the local target,
+ // for whatever good that does.
+ //
+
+ if ((!Connection->FindRouteInProgress) &&
+ (Connection->Retries == (Device->KeepAliveCount/2)) &&
+ (*(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork != 0)) {
+
+ SendFindRoute = TRUE;
+ Connection->FindRouteInProgress = TRUE;
+ NbiReferenceConnectionSync (Connection, CREF_FIND_ROUTE);
+
+ }
+
+ //
+ // This releases the lock.
+ //
+
+ NbiPacketizeSend(
+ Connection
+ NB_LOCK_HANDLE_ARG(LockHandle)
+ );
+
+ }
+
+ } else if ((Connection->SubState == CONNECTION_SUBSTATE_A_W_PROBE) ||
+ (Connection->SubState == CONNECTION_SUBSTATE_A_REMOTE_W) ||
+ (Connection->SubState == CONNECTION_SUBSTATE_A_W_ACK)) {
+
+ if (--Connection->Retries == 0) {
+
+ //
+ // Shut down the connection. This will send
+ // out half the usual number of session end
+ // frames.
+ //
+
+ NB_DEBUG2 (CONNECTION, ("Probe timeout of active connection %lx\n", Connection));
+
+ //
+ // This free the connection lock.
+ //
+
+ NbiStopConnection(
+ Connection,
+ STATUS_LINK_FAILED
+ NB_LOCK_HANDLE_ARG (LockHandle)
+ );
+
+ } else {
+
+ Connection->RetransmitThisWindow = TRUE;
+ if (Connection->CurrentRetransmitTimeout < (Connection->BaseRetransmitTimeout*8)) {
+ Connection->CurrentRetransmitTimeout =
+ (Connection->CurrentRetransmitTimeout * 3) / 2;
+ }
+
+ NbiStartRetransmit (Connection);
+
+ //
+ // After half the retries, send a find route unless we
+ // are already doing one, or the connection is to network
+ // 0. When this completes we update the local target,
+ // for whatever good that does.
+ //
+
+ if ((!Connection->FindRouteInProgress) &&
+ (Connection->Retries == (Device->KeepAliveCount/2)) &&
+ (*(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork != 0)) {
+
+ SendFindRoute = TRUE;
+ Connection->FindRouteInProgress = TRUE;
+ NbiReferenceConnectionSync (Connection, CREF_FIND_ROUTE);
+
+ }
+
+ //
+ // Set this so we know to retransmit when the ack
+ // is received.
+ //
+
+ if (Connection->SubState != CONNECTION_SUBSTATE_A_W_PROBE) {
+ Connection->ResponseTimeout = TRUE;
+ }
+
+
+ //
+ // This releases the lock.
+ //
+
+ NbiSendDataAck(
+ Connection,
+ NbiAckQuery
+ NB_LOCK_HANDLE_ARG(LockHandle));
+
+
+ }
+
+ } else {
+
+ NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
+
+ }
+
+ if (SendFindRoute) {
+
+ Connection->FindRouteRequest.Identifier = IDENTIFIER_NB;
+ *(UNALIGNED ULONG *)Connection->FindRouteRequest.Network =
+ *(UNALIGNED ULONG *)Connection->RemoteHeader.DestinationNetwork;
+ RtlCopyMemory(Connection->FindRouteRequest.Node,Connection->RemoteHeader.DestinationNode,6);
+ Connection->FindRouteRequest.Type = IPX_FIND_ROUTE_FORCE_RIP;
+
+ (*Device->Bind.FindRouteHandler)(
+ &Connection->FindRouteRequest);
+
+ }
+
+ } else {
+
+ NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
+
+ }
+
+} /* NbiExpireRetansmit */
+
+
+VOID
+NbiExpireWatchdog(
+ IN PCONNECTION Connection
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called when the connection's watchdog timer
+ expires. It is called from NbiLongTimeout.
+
+Arguments:
+
+ Connection - Pointer to the connection whose timer has expired.
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ NB_DEFINE_LOCK_HANDLE (LockHandle);
+
+
+ NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle);
+
+ //
+ // If we are not idle, then something else is happening
+ // so the watchdog is unnecessary.
+ //
+
+ if ((Connection->State == CONNECTION_STATE_ACTIVE) &&
+ (Connection->SubState == CONNECTION_SUBSTATE_A_IDLE)) {
+
+ Connection->Retries = NbiDevice->KeepAliveCount;
+ Connection->SubState = CONNECTION_SUBSTATE_A_W_PROBE;
+ NbiStartRetransmit (Connection);
+
+ //
+ // This releases the lock.
+ //
+
+ NbiSendDataAck(
+ Connection,
+ NbiAckQuery
+ NB_LOCK_HANDLE_ARG(LockHandle));
+
+ } else {
+
+ NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
+
+ }
+
+} /* NbiExpireWatchdog */
+
+
+VOID
+NbiShortTimeout(
+ IN CTEEvent * Event,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called at regular intervals to see if any of
+ the short connection timers have expired, and if so to execute their
+ expiration routines.
+
+Arguments:
+
+ Event - The event controlling the timer.
+
+ Context - Points to our device.
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ PLIST_ENTRY p, nextp;
+ PDEVICE Device = (PDEVICE)Context;
+ PCONNECTION Connection;
+ BOOLEAN RestartTimer = FALSE;
+ LARGE_INTEGER CurrentTick;
+ LARGE_INTEGER TickDifference;
+ ULONG TickDelta;
+ NB_DEFINE_LOCK_HANDLE (LockHandle);
+
+
+ NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle);
+
+ //
+ // This prevents anybody from starting the timer while we
+ // are in this routine (the main reason for this is that it
+ // makes it easier to determine whether we should restart
+ // it at the end of this routine).
+ //
+
+ Device->ProcessingShortTimer = TRUE;
+
+ //
+ // Advance the up-counter used to mark time in SHORT_TIMER_DELTA units. If we
+ // advance it all the way to 0xf0000000, then reset it to 0x10000000.
+ // We also run all the lists, decreasing all counters by 0xe0000000.
+ //
+
+
+ KeQueryTickCount (&CurrentTick);
+
+ TickDifference.QuadPart = CurrentTick.QuadPart -
+ Device->ShortTimerStart.QuadPart;
+
+ TickDelta = TickDifference.LowPart / NbiShortTimerDeltaTicks;
+ if (TickDelta == 0) {
+ TickDelta = 1;
+ }
+
+ Device->ShortAbsoluteTime += TickDelta;
+
+ if (Device->ShortAbsoluteTime >= 0xf0000000) {
+
+ ULONG Timeout;
+
+ Device->ShortAbsoluteTime -= 0xe0000000;
+
+ p = Device->ShortList.Flink;
+ while (p != &Device->ShortList) {
+
+ Connection = CONTAINING_RECORD (p, CONNECTION, ShortList);
+
+ Timeout = Connection->Retransmit;
+ if (Timeout) {
+ Connection->Retransmit = Timeout - 0xe0000000;
+ }
+
+ p = p->Flink;
+ }
+
+ }
+
+ p = Device->ShortList.Flink;
+ while (p != &Device->ShortList) {
+
+ Connection = CONTAINING_RECORD (p, CONNECTION, ShortList);
+
+ ASSERT (Connection->OnShortList);
+
+ //
+ // To avoid problems with the refcount being 0, don't
+ // do this if we are in ADM.
+ //
+
+ if (Connection->State == CONNECTION_STATE_ACTIVE) {
+
+ if (Connection->Retransmit &&
+ (Device->ShortAbsoluteTime > Connection->Retransmit)) {
+
+ Connection->Retransmit = 0;
+ NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle);
+
+ NbiExpireRetransmit (Connection); // no locks held
+
+ NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle);
+
+ }
+
+ }
+
+ if (!Connection->OnShortList) {
+
+ //
+ // The link has been taken out of the list while
+ // we were processing it. In this (rare) case we
+ // stop processing the whole list, we'll get it
+ // next time.
+ //
+
+ break;
+
+ }
+
+ nextp = p->Flink;
+
+ if (Connection->Retransmit == 0) {
+
+ Connection->OnShortList = FALSE;
+ RemoveEntryList(p);
+
+ //
+ // Do another check; that way if someone slipped in between
+ // the check of Connection->Tx and the OnShortList = FALSE and
+ // therefore exited without inserting, we'll catch that here.
+ //
+
+ if (Connection->Retransmit != 0) {
+ InsertTailList(&Device->ShortList, &Connection->ShortList);
+ Connection->OnShortList = TRUE;
+ }
+
+ }
+
+ p = nextp;
+
+ }
+
+ //
+ // If the list is empty note that, otherwise ShortListActive
+ // remains TRUE.
+ //
+
+ if (IsListEmpty (&Device->ShortList)) {
+ Device->ShortListActive = FALSE;
+ }
+
+
+ //
+ // Connection Data Ack timers. This queue is used to indicate
+ // that a piggyback ack is pending for this connection. We walk
+ // the queue, for each element we check if the connection has
+ // been on the queue for enough times through here,
+ // If so, we take it off and send an ack. Note that
+ // we have to be very careful how we walk the queue, since
+ // it may be changing while this is running.
+ //
+
+ for (p = Device->DataAckConnections.Flink;
+ p != &Device->DataAckConnections;
+ p = p->Flink) {
+
+ Connection = CONTAINING_RECORD (p, CONNECTION, DataAckLinkage);
+
+ //
+ // Skip this connection if it is not queued or it is
+ // too recent to matter. We may skip incorrectly if
+ // the connection is just being queued, but that is
+ // OK, we will get it next time.
+ //
+
+ if (!Connection->DataAckPending) {
+ continue;
+ }
+
+ ++Connection->DataAckTimeouts;
+
+ if (Connection->DataAckTimeouts < Device->AckDelayTime) {
+ continue;
+ }
+
+ NbiReferenceConnectionSync (Connection, CREF_SHORT_D_ACK);
+
+ Device->DataAckQueueChanged = FALSE;
+
+ NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle);
+
+ //
+ // Check the correct connection flag, to ensure that a
+ // send has not just taken him off the queue.
+ //
+
+ NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle);
+
+ if (Connection->DataAckPending) {
+
+ //
+ // Yes, we were waiting to piggyback an ack, but no send
+ // has come along. Turn off the flags and send an ack.
+ // We set PiggybackAckTimeout to TRUE so that we won't try
+ // to piggyback a response until we get back traffic.
+ //
+
+ Connection->DataAckPending = FALSE;
+ Connection->PiggybackAckTimeout = TRUE;
+ ++Device->Statistics.AckTimerExpirations;
+ ++Device->Statistics.PiggybackAckTimeouts;
+
+ //
+ // This call releases the lock.
+ //
+
+ NbiSendDataAck(
+ Connection,
+ NbiAckResponse
+ NB_LOCK_HANDLE_ARG(LockHandle));
+
+ } else {
+
+ NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle);
+
+ }
+
+ NbiDereferenceConnection (Connection, CREF_SHORT_D_ACK);
+
+ NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle);
+
+ //
+ // If the list has changed, then we need to stop processing
+ // since p->Flink is not valid.
+ //
+
+ if (Device->DataAckQueueChanged) {
+ break;
+ }
+
+ }
+
+ if (IsListEmpty (&Device->DataAckConnections)) {
+ Device->DataAckActive = FALSE;
+ }
+
+
+ //
+ // Update the real counters from the temp ones. We have
+ // TimerLock here, which is good enough.
+ //
+
+ ADD_TO_LARGE_INTEGER(
+ &Device->Statistics.DataFrameBytesSent,
+ Device->TempFrameBytesSent);
+ Device->Statistics.DataFramesSent += Device->TempFramesSent;
+
+ Device->TempFrameBytesSent = 0;
+ Device->TempFramesSent = 0;
+
+ ADD_TO_LARGE_INTEGER(
+ &Device->Statistics.DataFrameBytesReceived,
+ Device->TempFrameBytesReceived);
+ Device->Statistics.DataFramesReceived += Device->TempFramesReceived;
+
+ Device->TempFrameBytesReceived = 0;
+ Device->TempFramesReceived = 0;
+
+
+ //
+ // Determine if we have to restart the timer.
+ //
+
+ Device->ProcessingShortTimer = FALSE;
+
+ if ((Device->ShortListActive || Device->DataAckActive) &&
+ (Device->State != DEVICE_STATE_STOPPING)) {
+
+ RestartTimer = TRUE;
+
+ }
+
+ NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle);
+
+ if (RestartTimer) {
+
+ //
+ // Start up the timer again. Note that because we start the timer
+ // after doing work (above), the timer values will slip somewhat,
+ // depending on the load on the protocol. This is entirely acceptable
+ // and will prevent us from using the timer DPC in two different
+ // threads of execution.
+ //
+
+ KeQueryTickCount(&Device->ShortTimerStart);
+
+ CTEStartTimer(
+ &Device->ShortTimer,
+ SHORT_TIMER_DELTA,
+ NbiShortTimeout,
+ (PVOID)Device);
+
+ } else {
+
+ NbiDereferenceDevice (Device, DREF_SHORT_TIMER);
+
+ }
+
+} /* NbiShortTimeout */
+
+
+VOID
+NbiLongTimeout(
+ IN CTEEvent * Event,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called at regular intervals to see if any of
+ the long connection timers have expired, and if so to execute their
+ expiration routines.
+
+Arguments:
+
+ Event - The event controlling the timer.
+
+ Context - Points to our device.
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ PDEVICE Device = (PDEVICE)Context;
+ PLIST_ENTRY p, nextp;
+ LIST_ENTRY AdapterStatusList;
+ PREQUEST AdapterStatusRequest;
+ PCONNECTION Connection;
+ PNETBIOS_CACHE CacheName;
+ NB_DEFINE_LOCK_HANDLE (LockHandle)
+ NB_DEFINE_LOCK_HANDLE (LockHandle1)
+
+
+ //
+ // Advance the up-counter used to mark time in LONG_TIMER_DELTA units. If we
+ // advance it all the way to 0xf0000000, then reset it to 0x10000000.
+ // We also run all the lists, decreasing all counters by 0xe0000000.
+ //
+
+ NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle);
+
+ if (++Device->LongAbsoluteTime == 0xf0000000) {
+
+ ULONG Timeout;
+
+ Device->LongAbsoluteTime = 0x10000000;
+
+ p = Device->LongList.Flink;
+ while (p != &Device->LongList) {
+
+ Connection = CONTAINING_RECORD (p, CONNECTION, LongList);
+
+ Timeout = Connection->Watchdog;
+ if (Timeout) {
+ Connection->Watchdog = Timeout - 0xe0000000;
+ }
+
+ p = p->Flink;
+ }
+
+ }
+
+
+ if ((Device->LongAbsoluteTime % 4) == 0) {
+
+ p = Device->LongList.Flink;
+ while (p != &Device->LongList) {
+
+ Connection = CONTAINING_RECORD (p, CONNECTION, LongList);
+
+ ASSERT (Connection->OnLongList);
+
+ //
+ // To avoid problems with the refcount being 0, don't
+ // do this if we are in ADM.
+ //
+
+ if (Connection->State == CONNECTION_STATE_ACTIVE) {
+
+ if (Connection->Watchdog && (Device->LongAbsoluteTime > Connection->Watchdog)) {
+
+ Connection->Watchdog = 0;
+ NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle);
+
+ NbiExpireWatchdog (Connection); // no spinlocks held
+
+ NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle);
+
+ }
+
+ }
+
+ if (!Connection->OnLongList) {
+
+ //
+ // The link has been taken out of the list while
+ // we were processing it. In this (rare) case we
+ // stop processing the whole list, we'll get it
+ // next time.
+ //
+
+#if DBG
+ DbgPrint ("NBI: Stop processing LongList, %lx removed\n", Connection);
+#endif
+ break;
+
+ }
+
+ nextp = p->Flink;
+
+ if (Connection->Watchdog == 0) {
+
+ Connection->OnLongList = FALSE;
+ RemoveEntryList(p);
+
+ if (Connection->Watchdog != 0) {
+ InsertTailList(&Device->LongList, &Connection->LongList);
+ Connection->OnLongList = TRUE;
+ }
+
+ }
+
+ p = nextp;
+
+ }
+
+ }
+
+
+ //
+ // Now scan the data ack queue, looking for connections with
+ // no acks queued that we can get rid of.
+ //
+ // Note: The timer spinlock is held here.
+ //
+
+ for (p = Device->DataAckConnections.Flink;
+ p != &Device->DataAckConnections;
+ p = p->Flink) {
+
+ Connection = CONTAINING_RECORD (p, CONNECTION, DataAckLinkage);
+
+ if (Connection->DataAckPending) {
+ continue;
+ }
+
+ NbiReferenceConnectionSync (Connection, CREF_LONG_D_ACK);
+
+ NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle);
+
+ NB_SYNC_GET_LOCK (&Connection->Lock, &LockHandle1);
+ NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle);
+
+ //
+ // Have to check again, because the connection might
+ // just have been stopped, and it also might just have
+ // had a data ack queued.
+ //
+
+ if (Connection->OnDataAckQueue) {
+
+ Connection->OnDataAckQueue = FALSE;
+
+ RemoveEntryList (&Connection->DataAckLinkage);
+
+ if (Connection->DataAckPending) {
+ InsertTailList (&Device->DataAckConnections, &Connection->DataAckLinkage);
+ Connection->OnDataAckQueue = TRUE;
+ }
+
+ Device->DataAckQueueChanged = TRUE;
+
+ }
+
+ NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle);
+ NB_SYNC_FREE_LOCK (&Connection->Lock, LockHandle1);
+
+ NbiDereferenceConnection (Connection, CREF_LONG_D_ACK);
+
+ NB_SYNC_GET_LOCK (&Device->TimerLock, &LockHandle);
+
+ //
+ // Since we have changed the list, we can't tell if p->Flink
+ // is valid, so break. The effect is that we gradually peel
+ // connections off the queue.
+ //
+
+ break;
+
+ }
+
+ NB_SYNC_FREE_LOCK (&Device->TimerLock, LockHandle);
+
+
+ //
+ // Scan for any uncompleted receive IRPs, this may happen if
+ // the cable is pulled and we don't get any more ReceiveComplete
+ // indications.
+
+ NbiReceiveComplete((USHORT)0);
+
+
+ //
+ // Check if any adapter status queries are getting old.
+ //
+
+ InitializeListHead (&AdapterStatusList);
+
+ NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle);
+
+ p = Device->ActiveAdapterStatus.Flink;
+
+ while (p != &Device->ActiveAdapterStatus) {
+
+ AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p);
+
+ p = p->Flink;
+
+ if (REQUEST_INFORMATION(AdapterStatusRequest) == 1) {
+
+ //
+ // BUGBUG: We should resend a certain number of times.
+ //
+
+ RemoveEntryList (REQUEST_LINKAGE(AdapterStatusRequest));
+ InsertTailList (&AdapterStatusList, REQUEST_LINKAGE(AdapterStatusRequest));
+
+ //
+ // We are going to abort this request, so dereference
+ // the cache entry it used.
+ //
+
+ CacheName = (PNETBIOS_CACHE)REQUEST_STATUS(AdapterStatusRequest);
+ if (--CacheName->ReferenceCount == 0) {
+
+ NB_DEBUG2 (CACHE, ("Free delete name cache entry %lx\n", CacheName));
+ NbiFreeMemory(
+ CacheName,
+ sizeof(NETBIOS_CACHE) + ((CacheName->NetworksAllocated-1) * sizeof(NETBIOS_NETWORK)),
+ MEMORY_CACHE,
+ "Name deleted");
+
+ }
+
+ } else {
+
+ ++REQUEST_INFORMATION(AdapterStatusRequest);
+
+ }
+
+ }
+
+ NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
+
+
+ for (p = AdapterStatusList.Flink; p != &AdapterStatusList; ) {
+
+ AdapterStatusRequest = LIST_ENTRY_TO_REQUEST(p);
+ p = p->Flink;
+
+ NB_DEBUG2 (QUERY, ("AdapterStatus %lx got name but no response\n", AdapterStatusRequest));
+
+ REQUEST_INFORMATION(AdapterStatusRequest) = 0;
+ REQUEST_STATUS(AdapterStatusRequest) = STATUS_IO_TIMEOUT;
+
+ NbiCompleteRequest(AdapterStatusRequest);
+ NbiFreeRequest (Device, AdapterStatusRequest);
+
+ NbiDereferenceDevice (Device, DREF_STATUS_QUERY);
+
+ }
+
+ //
+ // See if a minute has passed and we need to check for empty
+ // cache entries to age out. We check for 64 seconds to make
+ // the mod operation faster.
+ //
+
+#if defined(_PNP_POWER)
+ NB_SYNC_GET_LOCK (&Device->Lock, &LockHandle);
+#endif _PNP_POWER
+
+ ++Device->CacheTimeStamp;
+
+ if ((Device->CacheTimeStamp % 64) == 0) {
+
+
+ //
+ // flush all the entries which have been around for ten minutes
+ // (LONG_TIMER_DELTA is in milliseconds).
+ //
+
+ FlushOldFromNetbiosCacheTable( Device->NameCache, (600000 / LONG_TIMER_DELTA) );
+
+ }
+
+
+ //
+ // Start up the timer again. Note that because we start the timer
+ // after doing work (above), the timer values will slip somewhat,
+ // depending on the load on the protocol. This is entirely acceptable
+ // and will prevent us from using the timer DPC in two different
+ // threads of execution.
+ //
+
+ if (Device->State != DEVICE_STATE_STOPPING) {
+
+ CTEStartTimer(
+ &Device->LongTimer,
+ LONG_TIMER_DELTA,
+ NbiLongTimeout,
+ (PVOID)Device);
+
+ } else {
+#if defined(_PNP_POWER)
+ Device->LongTimerRunning = FALSE;
+#endif _PNP_POWER
+ NbiDereferenceDevice (Device, DREF_LONG_TIMER);
+ }
+
+#if defined(_PNP_POWER)
+ NB_SYNC_FREE_LOCK (&Device->Lock, LockHandle);
+#endif _PNP_POWER
+} /* NbiLongTimeout */
+
+
+VOID
+NbiStartShortTimer(
+ IN PDEVICE Device
+ )
+
+/*++
+
+Routine Description:
+
+ This routine starts the short timer, if it is not already running.
+
+Arguments:
+
+ Device - Pointer to our device context.
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+
+ //
+ // Start the timer unless it the DPC is already running (in
+ // which case it will restart the timer itself if needed),
+ // or some list is active (meaning the timer is already
+ // queued up).
+ //
+
+ if ((!Device->ProcessingShortTimer) &&
+ (!(Device->ShortListActive)) &&
+ (!(Device->DataAckActive))) {
+
+ NbiReferenceDevice (Device, DREF_SHORT_TIMER);
+
+ KeQueryTickCount(&Device->ShortTimerStart);
+
+ CTEStartTimer(
+ &Device->ShortTimer,
+ SHORT_TIMER_DELTA,
+ NbiShortTimeout,
+ (PVOID)Device);
+
+ }
+
+} /* NbiStartShortTimer */
+
+
+VOID
+NbiInitializeTimers(
+ IN PDEVICE Device
+ )
+
+/*++
+
+Routine Description:
+
+ This routine initializes the lightweight timer system for the transport
+ provider.
+
+Arguments:
+
+ Device - Pointer to our device.
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+
+ //
+ // NbiTickIncrement is the number of NT time increments
+ // which pass between each tick. NbiShortTimerDeltaTicks
+ // is the number of ticks which should happen in
+ // SHORT_TIMER_DELTA milliseconds (i.e. between each
+ // expiration of the short timer).
+ //
+
+ NbiTickIncrement = KeQueryTimeIncrement();
+
+ if (NbiTickIncrement > (SHORT_TIMER_DELTA * MILLISECONDS)) {
+ NbiShortTimerDeltaTicks = 1;
+ } else {
+ NbiShortTimerDeltaTicks = (SHORT_TIMER_DELTA * MILLISECONDS) / NbiTickIncrement;
+ }
+
+ //
+ // The AbsoluteTime cycles between 0x10000000 and 0xf0000000.
+ //
+
+ Device->ShortAbsoluteTime = 0x10000000;
+ Device->LongAbsoluteTime = 0x10000000;
+
+ CTEInitTimer (&Device->ShortTimer);
+ CTEInitTimer (&Device->LongTimer);
+
+#if !defined(_PNP_POWER)
+ //
+ // One reference for the long timer.
+ //
+
+ NbiReferenceDevice (Device, DREF_LONG_TIMER);
+
+ CTEStartTimer(
+ &Device->LongTimer,
+ LONG_TIMER_DELTA,
+ NbiLongTimeout,
+ (PVOID)Device);
+
+#endif !_PNP_POWER
+
+ Device->TimersInitialized = TRUE;
+ Device->ShortListActive = FALSE;
+ Device->ProcessingShortTimer = FALSE;
+
+ InitializeListHead (&Device->ShortList);
+ InitializeListHead (&Device->LongList);
+
+ CTEInitLock (&Device->TimerLock.Lock);
+
+} /* NbiInitializeTimers */
+