Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1009 lines
28 KiB

  1. /*++
  2. Copyright (c) 1989-1993 Microsoft Corporation
  3. Module Name:
  4. Timer.c
  5. Abstract:
  6. This file contains the code to implement timer functions.
  7. Author:
  8. Jim Stewart (Jimst) 10-2-92
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. #include "timer.h"
  13. #include "ntddndis.h"
  14. // the timer Q
  15. tTIMERQ TimerQ;
  16. //******************* Pageable Routine Declarations ****************
  17. #ifdef ALLOC_PRAGMA
  18. #pragma CTEMakePageable(PAGE, InitTimerQ)
  19. #pragma CTEMakePageable(PAGE, DestroyTimerQ)
  20. #pragma CTEMakePageable(PAGE, DelayedNbtStopWakeupTimer)
  21. #endif
  22. // #pragma CTEMakePageable(PAGE, WakeupTimerExpiry)
  23. //******************* Pageable Routine Declarations ****************
  24. //----------------------------------------------------------------------------
  25. NTSTATUS
  26. InterlockedCallCompletion(
  27. IN tTIMERQENTRY *pTimer,
  28. IN NTSTATUS status
  29. )
  30. /*++
  31. Routine Description:
  32. This routine calls the completion routine if it hasn't been called
  33. yet, by first getting the JointLock spin lock and then getting the
  34. Completion routine ptr. If the ptr is null then the completion routine
  35. has already been called. Holding the Spin lock interlocks this
  36. with the timer expiry routine to prevent more than one call to the
  37. completion routine.
  38. Arguments:
  39. Return Value:
  40. there is no return value
  41. --*/
  42. {
  43. CTELockHandle OldIrq;
  44. COMPLETIONCLIENT pClientCompletion;
  45. // to synch. with the the Timer completion routines, Null the client completion
  46. // routine so it gets called just once, either from here or from the
  47. // timer completion routine setup when the timer was started.(in namesrv.c)
  48. //
  49. CTESpinLock(&NbtConfig.JointLock,OldIrq);
  50. pClientCompletion = pTimer->ClientCompletion;
  51. pTimer->ClientCompletion = NULL;
  52. if (pClientCompletion)
  53. {
  54. // remove the link from the name table to this timer block
  55. CHECK_PTR(((tNAMEADDR *)pTimer->pCacheEntry));
  56. ((tNAMEADDR *)pTimer->pCacheEntry)->pTimer = NULL;
  57. CTESpinFree(&NbtConfig.JointLock,OldIrq);
  58. (*pClientCompletion) (pTimer->ClientContext, status);
  59. return(STATUS_SUCCESS);
  60. }
  61. else
  62. {
  63. CTESpinFree(&NbtConfig.JointLock,OldIrq);
  64. }
  65. return(STATUS_UNSUCCESSFUL);
  66. }
  67. //----------------------------------------------------------------------------
  68. NTSTATUS
  69. InitTimerQ(
  70. IN int NumInQ
  71. )
  72. /*++
  73. Routine Description:
  74. This routine sets up the timer Q to have NumInQ entries to start with.
  75. These are blocks allocated for timers so that ExAllocatePool does not
  76. need to be done later.
  77. Arguments:
  78. Return Value:
  79. The function value is the status of the operation.
  80. --*/
  81. {
  82. tTIMERQENTRY *pTimerEntry;
  83. CTEPagedCode();
  84. InitializeListHead(&TimerQ.ActiveHead);
  85. InitializeListHead(&TimerQ.FreeHead);
  86. // allocate memory for the free list
  87. while(NumInQ--)
  88. {
  89. pTimerEntry = (tTIMERQENTRY *) NbtAllocMem (sizeof(tTIMERQENTRY), NBT_TAG2('15'));
  90. if (!pTimerEntry)
  91. {
  92. KdPrint(("Nbt.InitTimerQ: Unable to allocate memory!! - for the timer Q\n"));
  93. return(STATUS_INSUFFICIENT_RESOURCES);
  94. }
  95. else
  96. {
  97. pTimerEntry->Verify = NBT_VERIFY_TIMER_DOWN;
  98. InsertHeadList(&TimerQ.FreeHead, &pTimerEntry->Linkage);
  99. }
  100. }
  101. return(STATUS_SUCCESS);
  102. }
  103. //----------------------------------------------------------------------------
  104. NTSTATUS
  105. DestroyTimerQ(
  106. )
  107. /*++
  108. Routine Description:
  109. This routine clears up the TimerQEntry structures allocated
  110. Arguments:
  111. Return Value:
  112. The function value is the status of the operation.
  113. --*/
  114. {
  115. tTIMERQENTRY *pTimer;
  116. PLIST_ENTRY pEntry;
  117. CTEPagedCode();
  118. while (!IsListEmpty (&TimerQ.FreeHead))
  119. {
  120. pEntry = RemoveHeadList(&TimerQ.FreeHead);
  121. pTimer = CONTAINING_RECORD(pEntry, tTIMERQENTRY, Linkage);
  122. CTEMemFree(pTimer);
  123. }
  124. return(STATUS_SUCCESS);
  125. }
  126. //----------------------------------------------------------------------------
  127. NTSTATUS
  128. GetTimerEntry(
  129. OUT tTIMERQENTRY **ppTimerEntry
  130. )
  131. /*++
  132. Routine Description:
  133. This routine gets a free block from the &TimerQ.FreeHead, and if the
  134. list is empty it allocates another memory block for the queue.
  135. NOTE: this function is called with the JointLock held.
  136. Arguments:
  137. Return Value:
  138. The function value is the status of the operation.
  139. --*/
  140. {
  141. PLIST_ENTRY pEntry;
  142. tTIMERQENTRY *pTimerEntry;
  143. if (!IsListEmpty(&TimerQ.FreeHead))
  144. {
  145. pEntry = RemoveHeadList(&TimerQ.FreeHead);
  146. pTimerEntry = CONTAINING_RECORD(pEntry,tTIMERQENTRY,Linkage);
  147. ASSERT (pTimerEntry->Verify == NBT_VERIFY_TIMER_DOWN);
  148. }
  149. else if (!(pTimerEntry = (tTIMERQENTRY *) NbtAllocMem (sizeof(tTIMERQENTRY), NBT_TAG2('16'))))
  150. {
  151. KdPrint(("Unable to allocate memory!! - for the timer Q\n"));
  152. return (STATUS_INSUFFICIENT_RESOURCES);
  153. }
  154. *ppTimerEntry = pTimerEntry;
  155. pTimerEntry->Verify = NBT_VERIFY_TIMER_ACTIVE;
  156. NbtConfig.NumTimersRunning++;
  157. return(STATUS_SUCCESS);
  158. }
  159. //----------------------------------------------------------------------------
  160. VOID
  161. ReturnTimerToFreeQ(
  162. tTIMERQENTRY *pTimerEntry,
  163. BOOLEAN fLocked
  164. )
  165. {
  166. CTELockHandle OldIrq;
  167. if (!fLocked)
  168. {
  169. CTESpinLock(&NbtConfig.JointLock,OldIrq);
  170. }
  171. // return the timer block
  172. ASSERT (pTimerEntry->Verify == NBT_VERIFY_TIMER_ACTIVE);
  173. pTimerEntry->Verify = NBT_VERIFY_TIMER_DOWN;
  174. InsertTailList(&TimerQ.FreeHead,&pTimerEntry->Linkage);
  175. if (!--NbtConfig.NumTimersRunning)
  176. {
  177. KeSetEvent(&NbtConfig.TimerQLastEvent, 0, FALSE);
  178. }
  179. if (!fLocked)
  180. {
  181. CTESpinFree(&NbtConfig.JointLock,OldIrq);
  182. }
  183. }
  184. //----------------------------------------------------------------------------
  185. NTSTATUS
  186. CleanupCTETimer(
  187. IN tTIMERQENTRY *pTimerEntry
  188. )
  189. /*++
  190. Routine Description:
  191. This routine cleans up the timer. Called with the JointLock held.
  192. Arguments:
  193. Return Value:
  194. returns the reference count after the decrement
  195. --*/
  196. {
  197. COMPLETIONROUTINE TimeoutRoutine;
  198. PVOID Context;
  199. PVOID Context2;
  200. pTimerEntry->RefCount = 0;
  201. // the expiry routine is not currently running so we can call the
  202. // completion routine and remove the timer from the active timer Q
  203. TimeoutRoutine = (COMPLETIONROUTINE)pTimerEntry->TimeoutRoutine;
  204. pTimerEntry->TimeoutRoutine = NULL;
  205. Context = pTimerEntry->Context;
  206. Context2 = pTimerEntry->Context2;
  207. if (pTimerEntry->pDeviceContext)
  208. {
  209. NBT_DEREFERENCE_DEVICE ((tDEVICECONTEXT *) pTimerEntry->pDeviceContext, REF_DEV_TIMER, TRUE);
  210. pTimerEntry->pDeviceContext = NULL;
  211. }
  212. // release any tracker block hooked to the timer entry.. This could
  213. // be modified to not call the completion routine if there was
  214. // no context value... ie. for those timers that do not have anything
  215. // to cleanup ...however, for now we require all completion routines
  216. // to have a if (pTimerQEntry) if around the code so when it gets hit
  217. // from this call it does not access any part of pTimerQEntry.
  218. //
  219. if (TimeoutRoutine)
  220. {
  221. // call the completion routine so it can clean up its own buffers
  222. // the routine that called this one will call the client's completion
  223. // routine. A NULL timerEntry value indicates to the routine that
  224. // cleanup should be done.
  225. (VOID)(*TimeoutRoutine) (Context, Context2, NULL);
  226. }
  227. // move to the free list
  228. RemoveEntryList(&pTimerEntry->Linkage);
  229. ReturnTimerToFreeQ (pTimerEntry, TRUE);
  230. return(STATUS_SUCCESS);
  231. }
  232. //----------------------------------------------------------------------------
  233. NTSTATUS
  234. StartTimer(
  235. IN PVOID TimeoutRoutine,
  236. IN ULONG DeltaTime,
  237. IN PVOID Context,
  238. IN PVOID Context2,
  239. IN PVOID ContextClient,
  240. IN PVOID CompletionClient,
  241. IN tDEVICECONTEXT *pDeviceContext,
  242. OUT tTIMERQENTRY **ppTimerEntry,
  243. IN USHORT Retries,
  244. BOOLEAN fLocked)
  245. /*++
  246. Routine Description:
  247. This routine starts a timer.
  248. It has to be called with the JointLock held!
  249. Arguments:
  250. The value passed in is in milliseconds - must be converted to 100ns
  251. so multiply to 10,000
  252. Return Value:
  253. The function value is the status of the operation.
  254. --*/
  255. {
  256. tTIMERQENTRY *pTimerEntry;
  257. NTSTATUS status;
  258. CTELockHandle OldIrq;
  259. //
  260. // Do not allow any timers to be started if we are currently
  261. // Unloading!
  262. //
  263. if (NbtConfig.Unloading)
  264. {
  265. return STATUS_UNSUCCESSFUL;
  266. }
  267. if (!fLocked)
  268. {
  269. CTESpinLock(&NbtConfig.JointLock,OldIrq);
  270. }
  271. if ((!pDeviceContext) ||
  272. (NBT_REFERENCE_DEVICE (pDeviceContext, REF_DEV_TIMER, TRUE)))
  273. {
  274. // get a free timer block
  275. status = GetTimerEntry (&pTimerEntry);
  276. if (NT_SUCCESS(status))
  277. {
  278. pTimerEntry->DeltaTime = DeltaTime;
  279. pTimerEntry->RefCount = 1;
  280. //
  281. // this is the context value and routine called when the timer expires,
  282. // called by TimerExpiry below.
  283. //
  284. pTimerEntry->Context = Context;
  285. pTimerEntry->Context2 = Context2;
  286. pTimerEntry->TimeoutRoutine = TimeoutRoutine;
  287. pTimerEntry->Flags = 0; // no flags
  288. // now fill in the client's completion routines that ultimately get called
  289. // after one or more timeouts...
  290. pTimerEntry->ClientContext = (PVOID)ContextClient;
  291. pTimerEntry->ClientCompletion = (COMPLETIONCLIENT)CompletionClient;
  292. pTimerEntry->Retries = Retries;
  293. pTimerEntry->pDeviceContext = (PVOID) pDeviceContext;
  294. CTEInitTimer(&pTimerEntry->VxdTimer);
  295. CTEStartTimer(&pTimerEntry->VxdTimer,
  296. pTimerEntry->DeltaTime,
  297. (CTEEventRtn)TimerExpiry,
  298. (PVOID)pTimerEntry);
  299. // check if there is a ptr to return
  300. if (ppTimerEntry)
  301. {
  302. *ppTimerEntry = pTimerEntry;
  303. }
  304. // put on list
  305. InsertHeadList(&TimerQ.ActiveHead, &pTimerEntry->Linkage);
  306. }
  307. else if (pDeviceContext)
  308. {
  309. NBT_DEREFERENCE_DEVICE (pDeviceContext, REF_DEV_TIMER, TRUE);
  310. }
  311. }
  312. else
  313. {
  314. status = STATUS_INVALID_DEVICE_STATE;
  315. }
  316. if (!fLocked)
  317. {
  318. CTESpinFree(&NbtConfig.JointLock,OldIrq);
  319. }
  320. return(status);
  321. }
  322. //----------------------------------------------------------------------------
  323. NTSTATUS
  324. StopTimer(
  325. IN tTIMERQENTRY *pTimerEntry,
  326. OUT COMPLETIONCLIENT *ppClientCompletion,
  327. OUT PVOID *ppContext)
  328. /*++
  329. Routine Description:
  330. This routine stops a timer. Must be called with the Joint lock held.
  331. Arguments:
  332. Return Value:
  333. The function value is the status of the operation.
  334. --*/
  335. {
  336. NTSTATUS status;
  337. COMPLETIONROUTINE TimeoutRoutine;
  338. // null the client completion routine and Context so that it can't be called again
  339. // accidently
  340. if (ppClientCompletion)
  341. {
  342. *ppClientCompletion = NULL;
  343. }
  344. if (ppContext)
  345. {
  346. *ppContext = NULL;
  347. }
  348. // it is possible that the timer expiry routine has just run and the timer
  349. // has not been restarted, so check the refcount, it will be zero if the
  350. // timer was not restarted and 2 if the timer expiry is currently running.
  351. if (pTimerEntry->RefCount == 1)
  352. {
  353. // this allows the caller to call the client's completion routine with
  354. // the context value.
  355. if (ppClientCompletion)
  356. {
  357. *ppClientCompletion = pTimerEntry->ClientCompletion;
  358. }
  359. if (ppContext)
  360. {
  361. *ppContext = pTimerEntry->ClientContext;
  362. }
  363. pTimerEntry->ClientCompletion = NULL;
  364. if (!(pTimerEntry->Flags & TIMER_NOT_STARTED))
  365. {
  366. if (!CTEStopTimer((CTETimer *)&pTimerEntry->VxdTimer ))
  367. {
  368. //
  369. // It means the TimerExpiry routine is waiting to run,
  370. // so let it return this timer block to the free Q
  371. // Bug # 229535
  372. //
  373. // Before returning from here, we should do the cleanup since
  374. // the CompletionRoutine (if any) can result in some data
  375. // that is required for cleanup to be cleaned up (Bug # 398730)
  376. //
  377. if (TimeoutRoutine = (COMPLETIONROUTINE)pTimerEntry->TimeoutRoutine)
  378. {
  379. // call the completion routine so it can clean up its own buffers
  380. // the routine that called this one will call the client's completion
  381. // routine. A NULL timerEntry value indicates to the routine that
  382. // cleanup should be done.
  383. pTimerEntry->TimeoutRoutine = NULL;
  384. (VOID)(*TimeoutRoutine) (pTimerEntry->Context, pTimerEntry->Context2, NULL);
  385. }
  386. pTimerEntry->RefCount = 2;
  387. return (STATUS_SUCCESS);
  388. }
  389. }
  390. status = STATUS_SUCCESS;
  391. status = CleanupCTETimer(pTimerEntry);
  392. }
  393. else if (pTimerEntry->RefCount == 2)
  394. {
  395. // the timer expiry completion routines must set this routine to
  396. // null with the spin lock held to synchronize with this stop timer
  397. // routine. Likewise that routine checks this value too, to synchronize
  398. // with this routine.
  399. //
  400. if (pTimerEntry->ClientCompletion)
  401. {
  402. // this allows the caller to call the client's completion routine with
  403. // the context value.
  404. if (ppClientCompletion)
  405. {
  406. *ppClientCompletion = pTimerEntry->ClientCompletion;
  407. }
  408. if (ppContext)
  409. {
  410. *ppContext = pTimerEntry->ClientContext;
  411. }
  412. // so that the timer completion routine cannot also call the client
  413. // completion routine.
  414. pTimerEntry->ClientCompletion = NULL;
  415. }
  416. // signal the TimerExpiry routine that the timer has been cancelled
  417. //
  418. pTimerEntry->RefCount++;
  419. status = STATUS_UNSUCCESSFUL;
  420. }
  421. else
  422. {
  423. status = STATUS_UNSUCCESSFUL;
  424. }
  425. return(status);
  426. }
  427. //----------------------------------------------------------------------------
  428. VOID
  429. TimerExpiry(
  430. #ifndef VXD
  431. IN PKDPC Dpc,
  432. IN PVOID DeferredContext,
  433. IN PVOID SystemArg1,
  434. IN PVOID SystemArg2
  435. #else
  436. IN CTEEvent * pCTEEvent,
  437. IN PVOID DeferredContext
  438. #endif
  439. )
  440. /*++
  441. Routine Description:
  442. This routine is the timer expiry completion routine. It is called by the
  443. kernel when the timer expires.
  444. Arguments:
  445. Return Value:
  446. The function value is the status of the operation.
  447. --*/
  448. {
  449. tTIMERQENTRY *pTimerEntry;
  450. CTELockHandle OldIrq1;
  451. CTESpinLock(&NbtConfig.JointLock,OldIrq1);
  452. // get the timer Q list entry from the context passed in
  453. pTimerEntry = (tTIMERQENTRY *)DeferredContext;
  454. if (pTimerEntry->RefCount == 0)
  455. {
  456. // the timer has been cancelled already!
  457. CTESpinFree(&NbtConfig.JointLock,OldIrq1);
  458. return;
  459. }
  460. else if (pTimerEntry->RefCount >= 2) // Bug #: 229535
  461. {
  462. // the timer has been cancelled already!
  463. // Bug # 324655
  464. // If the Timer has been cancelled, we still need to do cleanup,
  465. // so do not NULL the TimeoutRoutine!
  466. //
  467. // pTimerEntry->TimeoutRoutine = NULL;
  468. ASSERT ((pTimerEntry->RefCount == 2) || (pTimerEntry->TimeoutRoutine == NULL));
  469. CleanupCTETimer (pTimerEntry);
  470. CTESpinFree(&NbtConfig.JointLock,OldIrq1);
  471. return;
  472. }
  473. // increment the reference count because we are handling a timer completion
  474. // now
  475. pTimerEntry->RefCount++;
  476. CTESpinFree(&NbtConfig.JointLock,OldIrq1);
  477. // call the completion routine passing the context value
  478. pTimerEntry->Flags &= ~TIMER_RESTART; // incase the clients wants to restart the timer
  479. (*(COMPLETIONROUTINE)pTimerEntry->TimeoutRoutine)(
  480. pTimerEntry->Context,
  481. pTimerEntry->Context2,
  482. pTimerEntry);
  483. CTESpinLock(&NbtConfig.JointLock,OldIrq1);
  484. pTimerEntry->RefCount--;
  485. if (pTimerEntry->Flags & TIMER_RESTART)
  486. {
  487. if (pTimerEntry->RefCount == 2)
  488. {
  489. // the timer was stopped during the expiry processing, so call the
  490. // deference routine
  491. //
  492. CleanupCTETimer(pTimerEntry);
  493. CTESpinFree(&NbtConfig.JointLock,OldIrq1);
  494. return;
  495. }
  496. else
  497. {
  498. CTEStartTimer (&pTimerEntry->VxdTimer,
  499. pTimerEntry->DeltaTime,
  500. (CTEEventRtn)TimerExpiry,
  501. (PVOID)pTimerEntry);
  502. }
  503. CTESpinFree(&NbtConfig.JointLock,OldIrq1);
  504. return;
  505. }
  506. else
  507. {
  508. // move to the free list after setting the reference count to zero
  509. // since this timer block is no longer active.
  510. //
  511. pTimerEntry->TimeoutRoutine = NULL;
  512. CleanupCTETimer (pTimerEntry);
  513. CTESpinFree(&NbtConfig.JointLock,OldIrq1);
  514. }
  515. }
  516. //----------------------------------------------------------------------------
  517. VOID
  518. ExpireTimer(
  519. IN tTIMERQENTRY *pTimerEntry,
  520. IN CTELockHandle *OldIrq1
  521. )
  522. /*++
  523. Routine Description:
  524. This routine causes the timer to be stopped (if it hasn't already)
  525. and if successful, calls the Completion routine.
  526. Arguments:
  527. Return Value:
  528. The function value is the status of the operation.
  529. --*/
  530. {
  531. //
  532. // Reset the Number of retries
  533. //
  534. pTimerEntry->Retries = 1;
  535. //
  536. // RefCount == 0 => Timer was stopped, but not restarted
  537. // RefCount == 1 => Timer is still running *
  538. // RefCount == 2 => TimerExpiry is currently running
  539. //
  540. if ((pTimerEntry->RefCount == 1) &&
  541. (!(pTimerEntry->Flags & TIMER_NOT_STARTED)) &&
  542. (CTEStopTimer( (CTETimer *)&pTimerEntry->VxdTimer)))
  543. {
  544. // increment the reference count because we are handling a timer completion
  545. // now
  546. pTimerEntry->RefCount++;
  547. CTESpinFree(&NbtConfig.JointLock, *OldIrq1);
  548. // call the completion routine passing the context value
  549. pTimerEntry->Flags &= ~TIMER_RESTART; // incase the clients wants to restart the timer
  550. (*(COMPLETIONROUTINE)pTimerEntry->TimeoutRoutine) (pTimerEntry->Context,
  551. pTimerEntry->Context2,
  552. pTimerEntry);
  553. CTESpinLock(&NbtConfig.JointLock, *OldIrq1);
  554. pTimerEntry->RefCount--;
  555. if (pTimerEntry->Flags & TIMER_RESTART)
  556. {
  557. if (pTimerEntry->RefCount == 2)
  558. {
  559. // the timer was stopped during the expiry processing, so call the
  560. // deference routine
  561. //
  562. CleanupCTETimer(pTimerEntry);
  563. }
  564. else
  565. {
  566. CTEStartTimer(&pTimerEntry->VxdTimer,
  567. pTimerEntry->DeltaTime,
  568. (CTEEventRtn)TimerExpiry,
  569. (PVOID)pTimerEntry);
  570. }
  571. }
  572. else
  573. {
  574. // move to the free list after setting the reference count to zero
  575. // since this tierm block is no longer active.
  576. //
  577. pTimerEntry->TimeoutRoutine = NULL;
  578. CleanupCTETimer (pTimerEntry);
  579. }
  580. }
  581. CTESpinFree(&NbtConfig.JointLock, *OldIrq1);
  582. }
  583. //----------------------------------------------------------------------------
  584. //
  585. // Wakeup Timer routines
  586. //
  587. //----------------------------------------------------------------------------
  588. VOID
  589. WakeupTimerExpiry(
  590. PVOID DeferredContext,
  591. ULONG LowTime,
  592. LONG HighTime
  593. )
  594. {
  595. BOOLEAN fAttached = FALSE;
  596. CTELockHandle OldIrq;
  597. tTIMERQENTRY *pTimerEntry = (tTIMERQENTRY *)DeferredContext;
  598. //
  599. // Call the TimerExpiry function while holding the NbtConfig.Resource
  600. //
  601. CTEExAcquireResourceExclusive (&NbtConfig.Resource,TRUE);
  602. if (pTimerEntry->RefCount > 1)
  603. {
  604. //
  605. // The timer is waiting to be cleaned up on a Worker thread,
  606. // so let it cleanup!
  607. //
  608. CTEExReleaseResource (&NbtConfig.Resource);
  609. return;
  610. }
  611. //
  612. // The Timeout routine has to ensure that it cleans up
  613. // properly since this pTimerEntry + handle will not be valid
  614. // at the end of this routine!
  615. //
  616. (*(COMPLETIONROUTINE) pTimerEntry->TimeoutRoutine) (pTimerEntry->Context,
  617. pTimerEntry->Context2,
  618. pTimerEntry);
  619. //
  620. // Close the timer handle
  621. //
  622. CTEAttachFsp(&fAttached, REF_FSP_WAKEUP_TIMER_EXPIRY);
  623. //
  624. // The Expiry routine should always be called in the context
  625. // of the system process
  626. //
  627. ASSERT (fAttached == FALSE);
  628. ZwClose (pTimerEntry->WakeupTimerHandle);
  629. CTEDetachFsp(fAttached, REF_FSP_WAKEUP_TIMER_EXPIRY);
  630. CTEExReleaseResource (&NbtConfig.Resource);
  631. ReturnTimerToFreeQ (pTimerEntry, FALSE);
  632. }
  633. //----------------------------------------------------------------------------
  634. VOID
  635. DelayedNbtStopWakeupTimer(
  636. IN tDGRAM_SEND_TRACKING *pUnused1,
  637. IN PVOID pClientContext,
  638. IN PVOID Unused2,
  639. IN tDEVICECONTEXT *Unused3
  640. )
  641. /*++
  642. Routine Description:
  643. This Routine stops a timer.
  644. This function has to be called after ensuring that the TimerExpiry has not
  645. yet cleaned up (while holding the NbtConfig.Resource)
  646. The NbtConfig.Resource has to be held while this routine is called
  647. Arguments:
  648. Timer - Timer structure
  649. Return Value:
  650. PVOID - a pointer to the memory or NULL if a failure
  651. --*/
  652. {
  653. NTSTATUS Status;
  654. BOOLEAN CurrentState = FALSE;
  655. BOOLEAN fAttached = FALSE;
  656. tTIMERQENTRY *pTimerEntry = (tTIMERQENTRY *) pClientContext;
  657. CTEPagedCode();
  658. ASSERT (pTimerEntry->fIsWakeupTimer);
  659. CTEAttachFsp(&fAttached, REF_FSP_STOP_WAKEUP_TIMER);
  660. Status = ZwCancelTimer (pTimerEntry->WakeupTimerHandle, &CurrentState);
  661. ZwClose (pTimerEntry->WakeupTimerHandle);
  662. CTEDetachFsp(fAttached, REF_FSP_STOP_WAKEUP_TIMER);
  663. ReturnTimerToFreeQ (pTimerEntry, FALSE);
  664. return;
  665. }
  666. //----------------------------------------------------------------------------
  667. VOID
  668. DelayedNbtStartWakeupTimer(
  669. IN tDGRAM_SEND_TRACKING *pUnused1,
  670. IN PVOID Unused2,
  671. IN PVOID Unused3,
  672. IN tDEVICECONTEXT *Unused4
  673. )
  674. /*++
  675. Routine Description:
  676. This routine starts a Wakeup timer.
  677. NbtConfig.Resource may be held on entry into this routine!
  678. Arguments:
  679. The value passed in is in milliseconds - must be converted to 100ns
  680. so multiply to 10,000
  681. Return Value:
  682. The function value is the status of the operation.
  683. --*/
  684. {
  685. NTSTATUS Status = STATUS_UNSUCCESSFUL;
  686. OBJECT_ATTRIBUTES ObjectAttributes;
  687. BOOLEAN fAttached = FALSE;
  688. LARGE_INTEGER Time;
  689. tTIMERQENTRY *pTimerEntry;
  690. CTELockHandle OldIrq;
  691. ULONG TimerInterval = 0;
  692. ULONG MilliSecsLeftInTtl = 0;
  693. LIST_ENTRY *pEntry;
  694. LIST_ENTRY *pHead;
  695. tDEVICECONTEXT *pDeviceContext;
  696. BOOLEAN fValidDevice = FALSE;
  697. CTEAttachFsp(&fAttached, REF_FSP_START_WAKEUP_TIMER);
  698. CTEExAcquireResourceExclusive(&NbtConfig.Resource,TRUE);
  699. CTESpinLock(&NbtConfig.JointLock,OldIrq);
  700. ASSERT (!NbtConfig.pWakeupRefreshTimer);
  701. //
  702. // Verify that at least 1 WOL-enabled device has an Ip + Wins address!
  703. //
  704. pHead = pEntry = &NbtConfig.DeviceContexts;
  705. while ((pEntry = pEntry->Flink) != pHead)
  706. {
  707. pDeviceContext = CONTAINING_RECORD (pEntry,tDEVICECONTEXT,Linkage);
  708. if ((pDeviceContext->WOLProperties & NDIS_DEVICE_WAKE_UP_ENABLE) &&
  709. (pDeviceContext->IpAddress) &&
  710. (pDeviceContext->lNameServerAddress != LOOP_BACK))
  711. {
  712. fValidDevice = TRUE;
  713. break;
  714. }
  715. }
  716. if ((NbtConfig.Unloading) || // Problem!!!
  717. (!fValidDevice) || // No valid device ?
  718. !(NbtConfig.GlobalRefreshState & NBT_G_REFRESH_SLEEPING)) // check if request was cancelled!
  719. {
  720. CTESpinFree(&NbtConfig.JointLock,OldIrq);
  721. KdPrint (("Nbt.NbtStartWakeupTimer: FAIL: Either: Unloading=<%x>, fValidDevice=<%x>, RefreshState=<%x>\n",
  722. NbtConfig.Unloading, fValidDevice, NbtConfig.GlobalRefreshState));
  723. }
  724. else if (!NT_SUCCESS (Status = GetTimerEntry (&pTimerEntry))) // get a free timer block
  725. {
  726. CTESpinFree(&NbtConfig.JointLock,OldIrq);
  727. KdPrint (("Nbt.NbtStartWakeupTimer: ERROR: GetTimerEntry returned <%x>\n", Status));
  728. }
  729. else
  730. {
  731. pTimerEntry->RefCount = 1;
  732. pTimerEntry->TimeoutRoutine = WakeupRefreshTimeout;
  733. pTimerEntry->Context = NULL;
  734. pTimerEntry->Context2 = NULL;
  735. CTESpinFree(&NbtConfig.JointLock,OldIrq);
  736. #ifdef HDL_FIX
  737. InitializeObjectAttributes (&ObjectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
  738. #else
  739. InitializeObjectAttributes (&ObjectAttributes, NULL, 0, NULL, NULL);
  740. #endif // HDL_FIX
  741. Status = ZwCreateTimer (&pTimerEntry->WakeupTimerHandle,
  742. TIMER_ALL_ACCESS,
  743. &ObjectAttributes,
  744. NotificationTimer);
  745. if (NT_SUCCESS (Status))
  746. {
  747. //
  748. // Set the machine to Wakeup at 1/2 the time between now and Ttl
  749. // This should not be less than the configured MinimumRefreshSleepTimeout
  750. // (default = 6 hrs)
  751. //
  752. MilliSecsLeftInTtl = NbtConfig.MinimumTtl
  753. - (ULONG) (((ULONGLONG) NbtConfig.sTimeoutCount * NbtConfig.MinimumTtl)
  754. / NbtConfig.RefreshDivisor);
  755. if ((MilliSecsLeftInTtl/2) < NbtConfig.MinimumRefreshSleepTimeout)
  756. {
  757. TimerInterval = NbtConfig.MinimumRefreshSleepTimeout;
  758. }
  759. else
  760. {
  761. TimerInterval = MilliSecsLeftInTtl/2;
  762. }
  763. pTimerEntry->DeltaTime = TimerInterval;
  764. IF_DBG(NBT_DEBUG_PNP_POWER)
  765. KdPrint(("Nbt.DelayedNbtStartWakeupTimer: TimerInterval=<%d:%d> (h:m), Currently: <%d/%d>\n",
  766. TimerInterval/(3600000), ((TimerInterval/60000)%60),
  767. NbtConfig.sTimeoutCount, NbtConfig.RefreshDivisor));
  768. //
  769. // convert to 100 ns units by multiplying by 10,000
  770. //
  771. Time.QuadPart = UInt32x32To64(pTimerEntry->DeltaTime,(LONG)MILLISEC_TO_100NS);
  772. Time.QuadPart = -(Time.QuadPart); // to make a delta time, negate the time
  773. pTimerEntry->fIsWakeupTimer = TRUE;
  774. ASSERT(Time.QuadPart < 0);
  775. Status = ZwSetTimer(pTimerEntry->WakeupTimerHandle,
  776. &Time,
  777. (PTIMER_APC_ROUTINE) WakeupTimerExpiry,
  778. pTimerEntry,
  779. TRUE,
  780. 0,
  781. NULL);
  782. if (!NT_SUCCESS (Status))
  783. {
  784. KdPrint (("Nbt.NbtStartWakeupTimer: ERROR: ZwSetTimer returned <%x>, TimerHandle=<%x>\n",
  785. Status, pTimerEntry->WakeupTimerHandle));
  786. ZwClose (pTimerEntry->WakeupTimerHandle);
  787. }
  788. }
  789. else
  790. {
  791. KdPrint (("Nbt.NbtStartWakeupTimer: ERROR: ZwCreateTimer returned <%x>\n", Status));
  792. }
  793. if (NT_SUCCESS (Status))
  794. {
  795. NbtConfig.pWakeupRefreshTimer = pTimerEntry;
  796. }
  797. else
  798. {
  799. ReturnTimerToFreeQ (pTimerEntry, FALSE);
  800. }
  801. }
  802. CTEExReleaseResource(&NbtConfig.Resource);
  803. CTEDetachFsp(fAttached, REF_FSP_START_WAKEUP_TIMER);
  804. KeSetEvent (&NbtConfig.WakeupTimerStartedEvent, 0, FALSE);
  805. return;
  806. }