/*++
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 */