]> git.proxmox.com Git - mirror_edk2.git/blame - Nt32Pkg/TimerDxe/Timer.c
Removed CommonHeader.h from NT32Pkg. Did not fix BDS as it will get re-written
[mirror_edk2.git] / Nt32Pkg / TimerDxe / Timer.c
CommitLineData
e331c50d 1/*++\r
2\r
3Copyright (c) 2006, Intel Corporation \r
4All rights reserved. This program and the accompanying materials \r
5are licensed and made available under the terms and conditions of the BSD License \r
6which accompanies this distribution. The full text of the license may be found at \r
7http://opensource.org/licenses/bsd-license.php \r
8 \r
9THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
10WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
11\r
12Module Name:\r
13\r
14 Timer.c\r
15\r
16Abstract:\r
17\r
18 NT Emulation Timer Architectural Protocol Driver as defined in DXE CIS\r
19\r
20 This Timer module uses an NT Thread to simulate the timer-tick driven\r
21 timer service. In the future, the Thread creation should possibly be \r
22 abstracted by the CPU architectural protocol\r
23\r
24--*/\r
25\r
e331c50d 26#include "Timer.h"\r
27\r
28//\r
29// Pointer to the CPU Architectural Protocol instance\r
30//\r
31EFI_CPU_ARCH_PROTOCOL *mCpu;\r
32\r
33//\r
34// The Timer Architectural Protocol that this driver produces\r
35//\r
36EFI_TIMER_ARCH_PROTOCOL mTimer = {\r
37 WinNtTimerDriverRegisterHandler,\r
38 WinNtTimerDriverSetTimerPeriod,\r
39 WinNtTimerDriverGetTimerPeriod,\r
40 WinNtTimerDriverGenerateSoftInterrupt\r
41};\r
42\r
43//\r
44// Define a global that we can use to shut down the NT timer thread when\r
45// the timer is canceled.\r
46//\r
47BOOLEAN mCancelTimerThread = FALSE;\r
48\r
49//\r
50// The notification function to call on every timer interrupt\r
51//\r
52EFI_TIMER_NOTIFY mTimerNotifyFunction = NULL;\r
53\r
54//\r
55// The current period of the timer interrupt\r
56//\r
57UINT64 mTimerPeriod;\r
58\r
59//\r
60// The thread handle for this driver\r
61//\r
62HANDLE mNtMainThreadHandle;\r
63\r
64//\r
65// The timer value from the last timer interrupt\r
66//\r
67UINT32 mNtLastTick;\r
68\r
69//\r
70// Critical section used to update varibles shared between the main thread and\r
71// the timer interrupt thread.\r
72//\r
73CRITICAL_SECTION mNtCriticalSection;\r
74\r
75//\r
76// Worker Functions\r
77//\r
78UINT mMMTimerThreadID = 0;\r
79\r
80VOID\r
81CALLBACK\r
82MMTimerThread (\r
83 UINT wTimerID,\r
84 UINT msg,\r
85 DWORD dwUser,\r
86 DWORD dw1,\r
87 DWORD dw2\r
88 )\r
89/*++\r
90\r
91Routine Description:\r
92\r
93 TODO: Add function description\r
94\r
95Arguments:\r
96\r
97 wTimerID - TODO: add argument description\r
98 msg - TODO: add argument description\r
99 dwUser - TODO: add argument description\r
100 dw1 - TODO: add argument description\r
101 dw2 - TODO: add argument description\r
102\r
103Returns:\r
104\r
105 TODO: add return values\r
106\r
107--*/\r
108{\r
109 EFI_TPL OriginalTPL;\r
110 UINT32 CurrentTick;\r
111 UINT32 Delta;\r
112 EFI_TIMER_NOTIFY CallbackFunction;\r
113 BOOLEAN InterruptState;\r
114\r
115 if (!mCancelTimerThread) {\r
116 \r
117 //\r
118 // Suspend the main thread until we are done\r
119 //\r
120\r
121 gWinNt->SuspendThread (mNtMainThreadHandle);\r
122\r
123 //\r
124 // If the timer thread is being canceled, then bail immediately.\r
125 // We check again here because there's a small window of time from when\r
126 // this thread was kicked off and when we suspended the main thread above.\r
127 //\r
128 if (mCancelTimerThread) {\r
129 gWinNt->ResumeThread (mNtMainThreadHandle);\r
130 gWinNt->timeKillEvent (wTimerID);\r
131 mMMTimerThreadID = 0;\r
132 return ;\r
133 }\r
134\r
135 mCpu->GetInterruptState (mCpu, &InterruptState);\r
136 while (!InterruptState) {\r
137 //\r
138 // Resume the main thread\r
139 //\r
140 gWinNt->ResumeThread (mNtMainThreadHandle);\r
141\r
142 //\r
143 // Wait for interrupts to be enabled.\r
144 //\r
145 mCpu->GetInterruptState (mCpu, &InterruptState);\r
146 while (!InterruptState) {\r
147 gWinNt->Sleep (0);\r
148 mCpu->GetInterruptState (mCpu, &InterruptState);\r
149 }\r
150 \r
151 //\r
152 // Suspend the main thread until we are done\r
153 //\r
154 gWinNt->SuspendThread (mNtMainThreadHandle);\r
155 mCpu->GetInterruptState (mCpu, &InterruptState);\r
156 }\r
157\r
158 //\r
159 // Get the current system tick\r
160 //\r
161 CurrentTick = gWinNt->GetTickCount ();\r
162 Delta = CurrentTick - mNtLastTick;\r
163 mNtLastTick = CurrentTick;\r
164\r
165 //\r
166 // If delay was more then 1 second, ignore it (probably debugging case)\r
167 //\r
168 if (Delta < 1000) {\r
169\r
170 OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);\r
171\r
172 //\r
173 // Inform the firmware of an "timer interrupt". The time\r
174 // expired since the last call is 10,000 times the number\r
175 // of ms. (or 100ns units)\r
176 //\r
177 gWinNt->EnterCriticalSection (&mNtCriticalSection);\r
178 CallbackFunction = mTimerNotifyFunction;\r
179 gWinNt->LeaveCriticalSection (&mNtCriticalSection);\r
180\r
181 //\r
182 // Only invoke the callback function if a Non-NULL handler has been\r
183 // registered. Assume all other handlers are legal.\r
184 //\r
185 if (CallbackFunction != NULL) {\r
186 CallbackFunction ((UINT64) (Delta * 10000));\r
187 }\r
188\r
189 gBS->RestoreTPL (OriginalTPL);\r
190\r
191 }\r
192\r
193 //\r
194 // Resume the main thread\r
195 //\r
196 gWinNt->ResumeThread (mNtMainThreadHandle);\r
197 } else {\r
198 gWinNt->timeKillEvent (wTimerID);\r
199 mMMTimerThreadID = 0;\r
200 }\r
201\r
202}\r
203\r
204UINT\r
205CreateNtTimer (\r
206 VOID\r
207 )\r
208/*++\r
209\r
210Routine Description:\r
211\r
212 It is used to emulate a platform \r
213 timer-driver interrupt handler. \r
214\r
215Returns:\r
216\r
217 Timer ID\r
218\r
219--*/\r
220// TODO: function comment is missing 'Arguments:'\r
221{\r
222 UINT32 SleepCount;\r
223\r
224 //\r
225 // Set our thread priority higher than the "main" thread.\r
226 //\r
227 gWinNt->SetThreadPriority (\r
228 gWinNt->GetCurrentThread (),\r
229 THREAD_PRIORITY_HIGHEST\r
230 );\r
231\r
232 //\r
233 // Calc the appropriate interval\r
234 //\r
235 gWinNt->EnterCriticalSection (&mNtCriticalSection);\r
236 SleepCount = (UINT32) (mTimerPeriod + 5000) / 10000;\r
237 gWinNt->LeaveCriticalSection (&mNtCriticalSection);\r
238\r
239 return gWinNt->timeSetEvent (\r
240 SleepCount,\r
241 0,\r
242 MMTimerThread,\r
243 (DWORD_PTR) NULL,\r
244 TIME_PERIODIC | TIME_KILL_SYNCHRONOUS | TIME_CALLBACK_FUNCTION\r
245 );\r
246\r
247}\r
248\r
249EFI_STATUS\r
250EFIAPI\r
251WinNtTimerDriverRegisterHandler (\r
252 IN EFI_TIMER_ARCH_PROTOCOL *This,\r
253 IN EFI_TIMER_NOTIFY NotifyFunction\r
254 )\r
255/*++\r
256\r
257Routine Description:\r
258\r
259 This function registers the handler NotifyFunction so it is called every time \r
260 the timer interrupt fires. It also passes the amount of time since the last \r
261 handler call to the NotifyFunction. If NotifyFunction is NULL, then the \r
262 handler is unregistered. If the handler is registered, then EFI_SUCCESS is \r
263 returned. If the CPU does not support registering a timer interrupt handler, \r
264 then EFI_UNSUPPORTED is returned. If an attempt is made to register a handler \r
265 when a handler is already registered, then EFI_ALREADY_STARTED is returned. \r
266 If an attempt is made to unregister a handler when a handler is not registered, \r
267 then EFI_INVALID_PARAMETER is returned. If an error occurs attempting to \r
268 register the NotifyFunction with the timer interrupt, then EFI_DEVICE_ERROR \r
269 is returned.\r
270\r
271Arguments:\r
272\r
273 This - The EFI_TIMER_ARCH_PROTOCOL instance.\r
274\r
275 NotifyFunction - The function to call when a timer interrupt fires. This \r
276 function executes at TPL_HIGH_LEVEL. The DXE Core will \r
277 register a handler for the timer interrupt, so it can know \r
278 how much time has passed. This information is used to \r
279 signal timer based events. NULL will unregister the handler.\r
280\r
281Returns: \r
282\r
283 EFI_SUCCESS - The timer handler was registered.\r
284\r
285 EFI_UNSUPPORTED - The platform does not support timer interrupts.\r
286\r
287 EFI_ALREADY_STARTED - NotifyFunction is not NULL, and a handler is already \r
288 registered.\r
289\r
290 EFI_INVALID_PARAMETER - NotifyFunction is NULL, and a handler was not \r
291 previously registered.\r
292\r
293 EFI_DEVICE_ERROR - The timer handler could not be registered.\r
294\r
295--*/\r
296{\r
297 //\r
298 // Check for invalid parameters\r
299 //\r
300 if (NotifyFunction == NULL && mTimerNotifyFunction == NULL) {\r
301 return EFI_INVALID_PARAMETER;\r
302 }\r
303\r
304 if (NotifyFunction != NULL && mTimerNotifyFunction != NULL) {\r
305 return EFI_ALREADY_STARTED;\r
306 }\r
307\r
308 //\r
309 // Use Critical Section to update the notification function that is\r
310 // used from the timer interrupt thread.\r
311 //\r
312 gWinNt->EnterCriticalSection (&mNtCriticalSection);\r
313\r
314 mTimerNotifyFunction = NotifyFunction;\r
315\r
316 gWinNt->LeaveCriticalSection (&mNtCriticalSection);\r
317\r
318 return EFI_SUCCESS;\r
319}\r
320\r
321EFI_STATUS\r
322EFIAPI\r
323WinNtTimerDriverSetTimerPeriod (\r
324 IN EFI_TIMER_ARCH_PROTOCOL *This,\r
325 IN UINT64 TimerPeriod\r
326 )\r
327/*++\r
328\r
329Routine Description:\r
330\r
331 This function adjusts the period of timer interrupts to the value specified \r
332 by TimerPeriod. If the timer period is updated, then the selected timer \r
333 period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned. If \r
334 the timer hardware is not programmable, then EFI_UNSUPPORTED is returned. \r
335 If an error occurs while attempting to update the timer period, then the \r
336 timer hardware will be put back in its state prior to this call, and \r
337 EFI_DEVICE_ERROR is returned. If TimerPeriod is 0, then the timer interrupt \r
338 is disabled. This is not the same as disabling the CPU's interrupts. \r
339 Instead, it must either turn off the timer hardware, or it must adjust the \r
340 interrupt controller so that a CPU interrupt is not generated when the timer \r
341 interrupt fires. \r
342\r
343Arguments:\r
344\r
345 This - The EFI_TIMER_ARCH_PROTOCOL instance.\r
346\r
347 TimerPeriod - The rate to program the timer interrupt in 100 nS units. If \r
348 the timer hardware is not programmable, then EFI_UNSUPPORTED is \r
349 returned. If the timer is programmable, then the timer period \r
350 will be rounded up to the nearest timer period that is supported \r
351 by the timer hardware. If TimerPeriod is set to 0, then the \r
352 timer interrupts will be disabled.\r
353\r
354Returns: \r
355\r
356 EFI_SUCCESS - The timer period was changed.\r
357\r
358 EFI_UNSUPPORTED - The platform cannot change the period of the timer interrupt.\r
359\r
360 EFI_DEVICE_ERROR - The timer period could not be changed due to a device error.\r
361\r
362--*/\r
363{\r
364\r
365 //\r
366 // If TimerPeriod is 0, then the timer thread should be canceled\r
367 //\r
368 if (TimerPeriod == 0) {\r
369 //\r
370 // Cancel the timer thread\r
371 //\r
372 gWinNt->EnterCriticalSection (&mNtCriticalSection);\r
373\r
374 mCancelTimerThread = TRUE;\r
375\r
376 gWinNt->LeaveCriticalSection (&mNtCriticalSection);\r
377\r
378 //\r
379 // Wait for the timer thread to exit\r
380 //\r
381\r
382 if (mMMTimerThreadID) {\r
383 gWinNt->timeKillEvent (mMMTimerThreadID);\r
384 }\r
385\r
386 mMMTimerThreadID = 0;\r
387\r
388 //\r
389 // Update the timer period\r
390 //\r
391 gWinNt->EnterCriticalSection (&mNtCriticalSection);\r
392\r
393 mTimerPeriod = TimerPeriod;\r
394\r
395 gWinNt->LeaveCriticalSection (&mNtCriticalSection);\r
396\r
397 //\r
398 // NULL out the thread handle so it will be re-created if the timer is enabled again\r
399 //\r
400\r
401 } else if ((TimerPeriod > TIMER_MINIMUM_VALUE) && (TimerPeriod < TIMER_MAXIMUM_VALUE)) {\r
402 //\r
403 // If the TimerPeriod is valid, then create and/or adjust the period of the timer thread\r
404 //\r
405 gWinNt->EnterCriticalSection (&mNtCriticalSection);\r
406\r
407 mTimerPeriod = TimerPeriod;\r
408\r
409 mCancelTimerThread = FALSE;\r
410\r
411 gWinNt->LeaveCriticalSection (&mNtCriticalSection);\r
412\r
413 //\r
414 // Get the starting tick location if we are just starting the timer thread\r
415 //\r
416 mNtLastTick = gWinNt->GetTickCount ();\r
417\r
418 if (mMMTimerThreadID) {\r
419 gWinNt->timeKillEvent (mMMTimerThreadID);\r
420 }\r
421\r
422 mMMTimerThreadID = 0;\r
423\r
424 mMMTimerThreadID = CreateNtTimer ();\r
425\r
426 }\r
427\r
428 return EFI_SUCCESS;\r
429}\r
430\r
431EFI_STATUS\r
432EFIAPI\r
433WinNtTimerDriverGetTimerPeriod (\r
434 IN EFI_TIMER_ARCH_PROTOCOL *This,\r
435 OUT UINT64 *TimerPeriod\r
436 )\r
437/*++\r
438\r
439Routine Description:\r
440\r
441 This function retrieves the period of timer interrupts in 100 ns units, \r
442 returns that value in TimerPeriod, and returns EFI_SUCCESS. If TimerPeriod \r
443 is NULL, then EFI_INVALID_PARAMETER is returned. If a TimerPeriod of 0 is \r
444 returned, then the timer is currently disabled.\r
445\r
446Arguments:\r
447\r
448 This - The EFI_TIMER_ARCH_PROTOCOL instance.\r
449\r
450 TimerPeriod - A pointer to the timer period to retrieve in 100 ns units. If \r
451 0 is returned, then the timer is currently disabled.\r
452\r
453Returns: \r
454\r
455 EFI_SUCCESS - The timer period was returned in TimerPeriod.\r
456\r
457 EFI_INVALID_PARAMETER - TimerPeriod is NULL.\r
458\r
459--*/\r
460{\r
461 if (TimerPeriod == NULL) {\r
462 return EFI_INVALID_PARAMETER;\r
463 }\r
464\r
465 *TimerPeriod = mTimerPeriod;\r
466\r
467 return EFI_SUCCESS;\r
468}\r
469\r
470EFI_STATUS\r
471EFIAPI\r
472WinNtTimerDriverGenerateSoftInterrupt (\r
473 IN EFI_TIMER_ARCH_PROTOCOL *This\r
474 )\r
475/*++\r
476\r
477Routine Description:\r
478\r
479 This function generates a soft timer interrupt. If the platform does not support soft \r
480 timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned. \r
481 If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler() \r
482 service, then a soft timer interrupt will be generated. If the timer interrupt is \r
483 enabled when this service is called, then the registered handler will be invoked. The \r
484 registered handler should not be able to distinguish a hardware-generated timer \r
485 interrupt from a software-generated timer interrupt.\r
486\r
487Arguments:\r
488\r
489 This - The EFI_TIMER_ARCH_PROTOCOL instance.\r
490\r
491Returns: \r
492\r
493 EFI_SUCCESS - The soft timer interrupt was generated.\r
494\r
495 EFI_UNSUPPORTEDT - The platform does not support the generation of soft timer interrupts.\r
496\r
497--*/\r
498{\r
499 return EFI_UNSUPPORTED;\r
500}\r
501\r
502\r
503EFI_STATUS\r
504EFIAPI\r
505WinNtTimerDriverInitialize (\r
506 IN EFI_HANDLE ImageHandle,\r
507 IN EFI_SYSTEM_TABLE *SystemTable\r
508 )\r
509/*++\r
510\r
511Routine Description:\r
512\r
513 Initialize the Timer Architectural Protocol driver\r
514\r
515Arguments:\r
516\r
517 ImageHandle - ImageHandle of the loaded driver\r
518\r
519 SystemTable - Pointer to the System Table\r
520\r
521Returns:\r
522\r
523 EFI_SUCCESS - Timer Architectural Protocol created\r
524\r
525 EFI_OUT_OF_RESOURCES - Not enough resources available to initialize driver.\r
526 \r
527 EFI_DEVICE_ERROR - A device error occured attempting to initialize the driver.\r
528\r
529--*/\r
530{\r
531 EFI_STATUS Status;\r
532 UINTN Result;\r
533 EFI_HANDLE Handle;\r
534\r
535 //\r
536 // Make sure the Timer Architectural Protocol is not already installed in the system\r
537 //\r
538 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiTimerArchProtocolGuid);\r
539\r
540 //\r
541 // Get the CPU Architectural Protocol instance\r
542 //\r
543 Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, &mCpu);\r
544 ASSERT_EFI_ERROR (Status);\r
545\r
546 //\r
547 // Get our handle so the timer tick thread can suspend\r
548 //\r
549 Result = gWinNt->DuplicateHandle (\r
550 gWinNt->GetCurrentProcess (),\r
551 gWinNt->GetCurrentThread (),\r
552 gWinNt->GetCurrentProcess (),\r
553 &mNtMainThreadHandle,\r
554 0,\r
555 FALSE,\r
556 DUPLICATE_SAME_ACCESS\r
557 );\r
558 if (Result == 0) {\r
559 return EFI_DEVICE_ERROR;\r
560 }\r
561\r
562 //\r
563 // Initialize Critical Section used to update variables shared between the main\r
564 // thread and the timer interrupt thread.\r
565 //\r
566 gWinNt->InitializeCriticalSection (&mNtCriticalSection);\r
567\r
568 //\r
569 // Start the timer thread at the default timer period\r
570 //\r
571 Status = mTimer.SetTimerPeriod (&mTimer, DEFAULT_TIMER_TICK_DURATION);\r
572 if (EFI_ERROR (Status)) {\r
573 gWinNt->DeleteCriticalSection (&mNtCriticalSection);\r
574 return Status;\r
575 }\r
576\r
577 //\r
578 // Install the Timer Architectural Protocol onto a new handle\r
579 //\r
580 Handle = NULL;\r
581 Status = gBS->InstallProtocolInterface (\r
582 &Handle,\r
583 &gEfiTimerArchProtocolGuid,\r
584 EFI_NATIVE_INTERFACE,\r
585 &mTimer\r
586 );\r
587 if (EFI_ERROR (Status)) {\r
588 //\r
589 // Cancel the timer\r
590 //\r
591 mTimer.SetTimerPeriod (&mTimer, 0);\r
592 gWinNt->DeleteCriticalSection (&mNtCriticalSection);\r
593 return Status;\r
594 }\r
595\r
596 return EFI_SUCCESS;\r
597}\r