0e8cdd4647387517bfaba20b934dd49ae2868e56
[reactos.git] / reactos / drivers / network / ndis / ndis / time.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS NDIS library
4 * FILE: ndis/time.c
5 * PURPOSE: Time related routines
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * Vizzini (vizzini@plasmic.com)
8 * REVISIONS:
9 * CSH 01/08-2000 Created
10 * Vizzini 08-Oct-2003 Formatting, commenting, and ASSERTs
11 *
12 * NOTES:
13 * - Although the standard kernel-mode M.O. is to trust the caller
14 * to not provide bad arguments, we have added lots of argument
15 * validation to assist in the effort to get third-party binaries
16 * working. It is easiest to track bugs when things break quickly
17 * and badly.
18 */
19
20 #include "ndissys.h"
21
22 \f
23 /*
24 * @implemented
25 */
26 VOID
27 EXPORT
28 NdisCancelTimer(
29 IN PNDIS_TIMER Timer,
30 OUT PBOOLEAN TimerCancelled)
31 /*
32 * FUNCTION: Cancels a scheduled NDIS timer
33 * ARGUMENTS:
34 * Timer: pointer to an NDIS_TIMER object to cancel
35 * TimerCancelled: boolean that returns cancellation status
36 * NOTES:
37 * - call at IRQL <= DISPATCH_LEVEL
38 */
39 {
40 ASSERT_IRQL(DISPATCH_LEVEL);
41 ASSERT(Timer);
42
43 *TimerCancelled = KeCancelTimer (&Timer->Timer);
44 }
45
46 \f
47 /*
48 * @implemented
49 */
50 #undef NdisGetCurrentSystemTime
51 VOID
52 EXPORT
53 NdisGetCurrentSystemTime (
54 IN OUT PLARGE_INTEGER pSystemTime)
55 /*
56 * FUNCTION: Retrieve the current system time
57 * ARGUMENTS:
58 * pSystemTime: pointer to the returned system time
59 * NOTES:
60 * - call at IRQL <= DISPATCH_LEVEL
61 */
62 {
63 ASSERT_IRQL(DISPATCH_LEVEL);
64 ASSERT(pSystemTime);
65
66 KeQuerySystemTime (pSystemTime);
67 }
68
69 \f
70 /*
71 * @implemented
72 */
73 VOID
74 EXPORT
75 NdisInitializeTimer(
76 IN OUT PNDIS_TIMER Timer,
77 IN PNDIS_TIMER_FUNCTION TimerFunction,
78 IN PVOID FunctionContext)
79 /*
80 * FUNCTION: Set up an NDIS_TIMER for later use
81 * ARGUMENTS:
82 * Timer: pointer to caller-allocated storage to receive an NDIS_TIMER
83 * TimerFunction: function pointer to routine to run when timer expires
84 * FunctionContext: context (param 2) to be passed to the timer function when it runs
85 * NOTES:
86 * - TimerFunction will be called at DISPATCH_LEVEL
87 * - call at IRQL = PASSIVE_LEVEL
88 */
89 {
90 PAGED_CODE();
91 ASSERT(Timer);
92
93 KeInitializeTimer (&Timer->Timer);
94
95 KeInitializeDpc (&Timer->Dpc, (PKDEFERRED_ROUTINE)TimerFunction, FunctionContext);
96 }
97
98 BOOLEAN DequeueMiniportTimer(PNDIS_MINIPORT_TIMER Timer)
99 {
100 PNDIS_MINIPORT_TIMER CurrentTimer;
101
102 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
103
104 if (!Timer->Miniport->TimerQueue)
105 return FALSE;
106
107 if (Timer->Miniport->TimerQueue == Timer)
108 {
109 Timer->Miniport->TimerQueue = Timer->NextDeferredTimer;
110 Timer->NextDeferredTimer = NULL;
111 return TRUE;
112 }
113 else
114 {
115 CurrentTimer = Timer->Miniport->TimerQueue;
116 while (CurrentTimer->NextDeferredTimer)
117 {
118 if (CurrentTimer->NextDeferredTimer == Timer)
119 {
120 CurrentTimer->NextDeferredTimer = Timer->NextDeferredTimer;
121 Timer->NextDeferredTimer = NULL;
122 return TRUE;
123 }
124 CurrentTimer = CurrentTimer->NextDeferredTimer;
125 }
126 return FALSE;
127 }
128 }
129
130 \f
131 /*
132 * @implemented
133 */
134 VOID
135 EXPORT
136 NdisMCancelTimer(
137 IN PNDIS_MINIPORT_TIMER Timer,
138 OUT PBOOLEAN TimerCancelled)
139 /*
140 * FUNCTION: cancel a scheduled NDIS_MINIPORT_TIMER
141 * ARGUMENTS:
142 * Timer: timer object to cancel
143 * TimerCancelled: status of cancel operation
144 * NOTES:
145 * - call at IRQL <= DISPATCH_LEVEL
146 */
147 {
148 KIRQL OldIrql;
149
150 ASSERT_IRQL(DISPATCH_LEVEL);
151 ASSERT(TimerCancelled);
152 ASSERT(Timer);
153
154 *TimerCancelled = KeCancelTimer (&Timer->Timer);
155
156 if (*TimerCancelled)
157 {
158 KeAcquireSpinLock(&Timer->Miniport->Lock, &OldIrql);
159 /* If it's somebody already dequeued it, something is wrong (maybe a double-cancel?) */
160 if (!DequeueMiniportTimer(Timer)) ASSERT(FALSE);
161 KeReleaseSpinLock(&Timer->Miniport->Lock, OldIrql);
162 }
163 }
164
165 VOID NTAPI
166 MiniTimerDpcFunction(PKDPC Dpc,
167 PVOID DeferredContext,
168 PVOID SystemArgument1,
169 PVOID SystemArgument2)
170 {
171 PNDIS_MINIPORT_TIMER Timer = DeferredContext;
172
173 Timer->MiniportTimerFunction(Dpc,
174 Timer->MiniportTimerContext,
175 SystemArgument1,
176 SystemArgument2);
177
178 /* Only dequeue if the timer has a period of 0 */
179 if (!Timer->Timer.Period)
180 {
181 KeAcquireSpinLockAtDpcLevel(&Timer->Miniport->Lock);
182 /* If someone already dequeued it, something is wrong (borked timer implementation?) */
183 if (!DequeueMiniportTimer(Timer)) ASSERT(FALSE);
184 KeReleaseSpinLockFromDpcLevel(&Timer->Miniport->Lock);
185 }
186 }
187
188 \f
189 /*
190 * @implemented
191 */
192 VOID
193 EXPORT
194 NdisMInitializeTimer(
195 IN OUT PNDIS_MINIPORT_TIMER Timer,
196 IN NDIS_HANDLE MiniportAdapterHandle,
197 IN PNDIS_TIMER_FUNCTION TimerFunction,
198 IN PVOID FunctionContext)
199 /*
200 * FUNCTION: Initialize an NDIS_MINIPORT_TIMER
201 * ARGUMENTS:
202 * Timer: Timer object to initialize
203 * MiniportAdapterHandle: Handle to the miniport, passed in to MiniportInitialize
204 * TimerFunction: function to be executed when the timer expires
205 * FunctionContext: argument passed to TimerFunction when it is called
206 * NOTES:
207 * - TimerFunction is called at IRQL = DISPATCH_LEVEL
208 * - call at IRQL = PASSIVE_LEVEL
209 */
210 {
211 PAGED_CODE();
212 ASSERT(Timer);
213
214 KeInitializeTimer (&Timer->Timer);
215 KeInitializeDpc (&Timer->Dpc, MiniTimerDpcFunction, Timer);
216
217 Timer->MiniportTimerFunction = TimerFunction;
218 Timer->MiniportTimerContext = FunctionContext;
219 Timer->Miniport = &((PLOGICAL_ADAPTER)MiniportAdapterHandle)->NdisMiniportBlock;
220 Timer->NextDeferredTimer = NULL;
221 }
222
223 \f
224 /*
225 * @implemented
226 */
227 VOID
228 EXPORT
229 NdisMSetPeriodicTimer(
230 IN PNDIS_MINIPORT_TIMER Timer,
231 IN UINT MillisecondsPeriod)
232 /*
233 * FUNCTION: Set a timer to go off periodically
234 * ARGUMENTS:
235 * Timer: pointer to the timer object to set
236 * MillisecondsPeriod: period of the timer
237 * NOTES:
238 * - Minimum predictible interval is ~10ms
239 * - Must be called at IRQL <= DISPATCH_LEVEL
240 */
241 {
242 LARGE_INTEGER Timeout;
243 KIRQL OldIrql;
244
245 ASSERT_IRQL(DISPATCH_LEVEL);
246 ASSERT(Timer);
247
248 /* relative delays are negative, absolute are positive; resolution is 100ns */
249 Timeout.QuadPart = Int32x32To64(MillisecondsPeriod, -10000);
250
251 KeAcquireSpinLock(&Timer->Miniport->Lock, &OldIrql);
252 /* If KeSetTimer(Ex) returns FALSE then the timer is not in the system's queue (and not in ours either) */
253 if (!KeSetTimerEx(&Timer->Timer, Timeout, MillisecondsPeriod, &Timer->Dpc))
254 {
255 /* Add the timer at the head of the timer queue */
256 Timer->NextDeferredTimer = Timer->Miniport->TimerQueue;
257 Timer->Miniport->TimerQueue = Timer;
258 }
259 KeReleaseSpinLock(&Timer->Miniport->Lock, OldIrql);
260 }
261
262 \f
263 /*
264 * @implemented
265 */
266 #undef NdisMSetTimer
267 VOID
268 EXPORT
269 NdisMSetTimer(
270 IN PNDIS_MINIPORT_TIMER Timer,
271 IN UINT MillisecondsToDelay)
272 /*
273 * FUNCTION: Set a NDIS_MINIPORT_TIMER so that it goes off
274 * ARGUMENTS:
275 * Timer: timer object to set
276 * MillisecondsToDelay: time to wait for the timer to expire
277 * NOTES:
278 * - Minimum predictible interval is ~10ms
279 * - Must be called at IRQL <= DISPATCH_LEVEL
280 */
281 {
282 LARGE_INTEGER Timeout;
283 KIRQL OldIrql;
284
285 ASSERT_IRQL(DISPATCH_LEVEL);
286 ASSERT(Timer);
287
288 /* relative delays are negative, absolute are positive; resolution is 100ns */
289 Timeout.QuadPart = Int32x32To64(MillisecondsToDelay, -10000);
290
291 KeAcquireSpinLock(&Timer->Miniport->Lock, &OldIrql);
292 /* If KeSetTimer(Ex) returns FALSE then the timer is not in the system's queue (and not in ours either) */
293 if (!KeSetTimer(&Timer->Timer, Timeout, &Timer->Dpc))
294 {
295 /* Add the timer at the head of the timer queue */
296 Timer->NextDeferredTimer = Timer->Miniport->TimerQueue;
297 Timer->Miniport->TimerQueue = Timer;
298 }
299 KeReleaseSpinLock(&Timer->Miniport->Lock, OldIrql);
300 }
301
302 \f
303 /*
304 * @implemented
305 */
306 VOID
307 EXPORT
308 NdisSetTimer(
309 IN PNDIS_TIMER Timer,
310 IN UINT MillisecondsToDelay)
311 /*
312 * FUNCTION: Set an NDIS_TIMER so that it goes off
313 * ARGUMENTS:
314 * Timer: timer object to set
315 * MillisecondsToDelay: time to wait for the timer to expire
316 * NOTES:
317 * - Minimum predictible interval is ~10ms
318 * - Must be called at IRQL <= DISPATCH_LEVEL
319 */
320 {
321 LARGE_INTEGER Timeout;
322
323 ASSERT_IRQL(DISPATCH_LEVEL);
324 ASSERT(Timer);
325
326 NDIS_DbgPrint(MAX_TRACE, ("Called. Timer is: 0x%x, Timeout is: %ld\n", Timer, MillisecondsToDelay));
327
328 /* relative delays are negative, absolute are positive; resolution is 100ns */
329 Timeout.QuadPart = Int32x32To64(MillisecondsToDelay, -10000);
330
331 KeSetTimer (&Timer->Timer, Timeout, &Timer->Dpc);
332 }
333
334 /*
335 * @implemented
336 */
337 VOID
338 EXPORT
339 NdisSetTimerEx(
340 IN PNDIS_TIMER Timer,
341 IN UINT MillisecondsToDelay,
342 IN PVOID FunctionContext)
343 {
344 NDIS_DbgPrint(MAX_TRACE, ("Called. Timer is: 0x%x, Timeout is: %ld, FunctionContext is: 0x%x\n",
345 Timer, MillisecondsToDelay, FunctionContext));
346
347 Timer->Dpc.DeferredContext = FunctionContext;
348
349 NdisSetTimer(Timer, MillisecondsToDelay);
350 }
351
352 /* EOF */
353