| [354] | 1 | /* $Id: timer.cpp,v 1.3 2001/04/30 21:07:59 sandervl Exp $ */
|
|---|
| 2 |
|
|---|
| 3 | /* SCCSID = %W% %E% */
|
|---|
| 4 | /****************************************************************************
|
|---|
| 5 | * *
|
|---|
| 6 | * Copyright (c) IBM Corporation 1994 - 1997. *
|
|---|
| 7 | * *
|
|---|
| 8 | * The following IBM OS/2 source code is provided to you solely for the *
|
|---|
| 9 | * the purpose of assisting you in your development of OS/2 device drivers. *
|
|---|
| 10 | * You may use this code in accordance with the IBM License Agreement *
|
|---|
| 11 | * provided in the IBM Device Driver Source Kit for OS/2. *
|
|---|
| 12 | * *
|
|---|
| 13 | ****************************************************************************/
|
|---|
| 14 | /**@internal %W%
|
|---|
| 15 | * TIMER object implementation.
|
|---|
| 16 | * @version %I%
|
|---|
| 17 | * @context
|
|---|
| 18 | * Unless otherwise noted, all interfaces are Ring-0, 16-bit, kernel stack.
|
|---|
| 19 | * Many of these functions run in interrupt context, this is noted in each
|
|---|
| 20 | * function header.
|
|---|
| 21 | * @notes Suggestions for future work
|
|---|
| 22 | * 1.) Should have a diagnostic in the constructor to ensure the Timer works.
|
|---|
| 23 | * 2.) Should respect the Clock Select on the CS 4232.
|
|---|
| 24 | * @history
|
|---|
| 25 | */
|
|---|
| 26 |
|
|---|
| 27 | #include <devhelp.h>
|
|---|
| 28 | #include <include.h>
|
|---|
| 29 | #include <string.h> // memset()
|
|---|
| 30 | #include "parse.h" // fNoHWTimer
|
|---|
| 31 | #include "irq.hpp" // Object definition.
|
|---|
| 32 | #include "timer.hpp" // Object definition.
|
|---|
| 33 | #include "midistrm.hpp" // Object definition.
|
|---|
| 34 |
|
|---|
| 35 | #include "tmr0_idc.h" // IDC to TIMER0.SYS
|
|---|
| 36 |
|
|---|
| 37 |
|
|---|
| 38 |
|
|---|
| 39 | // Enumerate the technologies from which we can generate a timer tick.
|
|---|
| 40 | enum { TIMER_TechNotIdentified = 0, // Haven't yet identified the technology.
|
|---|
| 41 | TIMER_AdapterTimerInt, // Adapter onboard HW timer interrupt.
|
|---|
| 42 | TIMER_SysTimer, // OS/2 system timer (31 msec).
|
|---|
| 43 | TIMER_Timer0, // Rudi: TIMER0 IDC
|
|---|
| 44 | TIMER_Timer0Fallback }; // Rudi: TIMER0 temporary unavailable
|
|---|
| 45 |
|
|---|
| 46 |
|
|---|
| 47 | // Force use of System timer.
|
|---|
| 48 | //int fNoHWTimer; // Set in parse.c when "/O:NoHWTimer"
|
|---|
| 49 | // is included on DEVICE= config.sys line.
|
|---|
| 50 |
|
|---|
| 51 | // Provide a global reference so interrupt handler can find all Timers.
|
|---|
| 52 | PQUEUEHEAD pTimerList;
|
|---|
| 53 |
|
|---|
| 54 |
|
|---|
| 55 | //Rudi: TIMER0
|
|---|
| 56 | static IDCTABLE TMR0IDCTable;
|
|---|
| 57 |
|
|---|
| 58 |
|
|---|
| 59 | // Definitions for static data members of the Timer class. Ref timer.hpp
|
|---|
| 60 | // for comments on each vbl. All these are initialized by TIMER::TIMER.
|
|---|
| 61 |
|
|---|
| 62 | //IRQ* TIMER::_pIRQ = 0;
|
|---|
| 63 | USHORT TIMER::_usTimerCount;
|
|---|
| 64 | USHORT TIMER::_usInterval_mSec;
|
|---|
| 65 | USHORT TIMER::_usIntervalErr_uSec;
|
|---|
| 66 | UCHAR TIMER::_eTechnology = TIMER_TechNotIdentified;
|
|---|
| 67 |
|
|---|
| 68 |
|
|---|
| 69 |
|
|---|
| 70 | #if 0
|
|---|
| 71 | // Countdown timer resolution of the CS4232, in nano-seconds.
|
|---|
| 72 | //### The countdown timer resolution is 9.969 uSec when C2SL = 0,
|
|---|
| 73 | //### and 9.92 uSec when C2SL = 1. The C2SL can change on the fly,
|
|---|
| 74 | //### depending on what the Wave objects are doing.
|
|---|
| 75 | //### We pick a value that is about 1/2 way between the two possible
|
|---|
| 76 | //### actual values; introduces about a 0.25% error in actual time rates,
|
|---|
| 77 | //### or 1 second off after 400 seconds of play.
|
|---|
| 78 | //### It might be better to periodically read and respect the current
|
|---|
| 79 | //### value of the C2SL select register on the fly (more overhead, but
|
|---|
| 80 | //### more accurate timing)
|
|---|
| 81 | const ULONG ulCS4232_Timer_nSec = 9945;
|
|---|
| 82 | #endif
|
|---|
| 83 |
|
|---|
| 84 |
|
|---|
| 85 | /**@internal TIMER::_vTimerHook
|
|---|
| 86 | * Handle timer tick. Runs in Interrupt context.
|
|---|
| 87 | * @param None.
|
|---|
| 88 | * @return BOOL - TRUE if interrupt handled, FALSE otherwise.
|
|---|
| 89 | * @notes First checks if this interrupt was intended for us. If using
|
|---|
| 90 | * interrupt from adapter, we share this Int with Wave. Then, if our
|
|---|
| 91 | * interrupt, walks the AudioHW list looking for all Timers, and gives
|
|---|
| 92 | * each instantiated timer object an opportunity to perform tasks.
|
|---|
| 93 | */
|
|---|
| 94 | static void __far __loadds __saveregs TIMER::_vTimerHook(void)
|
|---|
| 95 | {
|
|---|
| 96 | TIMER* pTimer; // Used to find Timers in AudioHW list.
|
|---|
| 97 |
|
|---|
| 98 | // Walk through all running timers, perform the per-tick services.
|
|---|
| 99 | pTimer = (TIMER*) pTimerList->Head();
|
|---|
| 100 | while (pTimer != NULL) {
|
|---|
| 101 | if ( pTimer->_eState == TIMER_Running )
|
|---|
| 102 | pTimer->_vPerTickTasks();
|
|---|
| 103 | pTimer = (TIMER*) pTimer->pNext;
|
|---|
| 104 | }
|
|---|
| 105 | }
|
|---|
| 106 |
|
|---|
| 107 |
|
|---|
| 108 | /**@internal TIMER__iCtxHook
|
|---|
| 109 | * Context hook for timer object. Used to call the Process() method
|
|---|
| 110 | * on the currently active MIDI stream.
|
|---|
| 111 | * @param (register EAX) - Data provided by the timer interrupt handler
|
|---|
| 112 | * when the context hook is armed. The interrupt handler should have
|
|---|
| 113 | * supplied a valid stream type (ref. stream.hpp).
|
|---|
| 114 | * @return int 0 - per DevHelp_* definitions on Context Hook operation.
|
|---|
| 115 | * @notes The Context hook function is called by the OS/2 kernel out of a
|
|---|
| 116 | * ring0, task context, _not_ in interrupt context. The function is called
|
|---|
| 117 | * as result of the context hook being armed. The timer interrupt handler
|
|---|
| 118 | * arms the context hook in interrupt context; this routine handles it (in
|
|---|
| 119 | * task context).
|
|---|
| 120 | * @notes We apply the Process() method only to the 1st stream that we find;
|
|---|
| 121 | * that is, we don't search for more than one active MPU or FMSYNTH stream.
|
|---|
| 122 | */
|
|---|
| 123 |
|
|---|
| 124 | //Rudi: original code "forgot" to set DS !!
|
|---|
| 125 | void __far __loadds __saveregs TIMER__iCtxHook(USHORT usStreamType);
|
|---|
| 126 | #pragma aux TIMER__iCtxHook parm [ax];
|
|---|
| 127 |
|
|---|
| 128 | void __far __loadds __saveregs TIMER__iCtxHook(USHORT usStreamType)
|
|---|
| 129 | {
|
|---|
| 130 | MIDISTREAM* pStream;
|
|---|
| 131 |
|
|---|
| 132 | // The OS/2 kernel supplies us with the Ctx Hook data in the EAX
|
|---|
| 133 | // register. We take advantage of this, by stuffing the stream
|
|---|
| 134 | // type into EAX when we arm the contex hook. Here, we make the
|
|---|
| 135 | // dangerous assumption here that the compiler has not yet clobbered
|
|---|
| 136 | // EAX. The _EAX() function is a pragma that we define in include.h.
|
|---|
| 137 |
|
|---|
| 138 | // usStreamType = _AX();
|
|---|
| 139 |
|
|---|
| 140 | pStream = (MIDISTREAM*) FindActiveStream( usStreamType );
|
|---|
| 141 |
|
|---|
| 142 | if (pStream) // Should always be a valid fn adr when timer is running.
|
|---|
| 143 | pStream->Process();
|
|---|
| 144 | }
|
|---|
| 145 |
|
|---|
| 146 | /**@external TIMER::TIMER
|
|---|
| 147 | * Constructor for TIMER object.
|
|---|
| 148 | * @param IRQ* pIRQ - pointer to IRQ object, NULL if none.
|
|---|
| 149 | * @param USHORT uTargetMSec - target resolution in milliseconds.
|
|---|
| 150 | * @param USHORT usStreamType - type of MIDISTREAM that this Timer is
|
|---|
| 151 | * associated with. This param controls the stream lookup when the
|
|---|
| 152 | * timer goes off.
|
|---|
| 153 | * @notes Constructs timer based on interrupt if possible, otherwise
|
|---|
| 154 | * uses 31.25 mSec kernel timer. The timer interrupt handler is a
|
|---|
| 155 | * static function: no matter how many Timer objects are defined, they
|
|---|
| 156 | * all use the same timer interrupt handler. The interrupt handler
|
|---|
| 157 | * makes a callout to process the unique instance information of all
|
|---|
| 158 | * Timer objects in existance.
|
|---|
| 159 | * @notes New Timer instance is added to the global AUDIOHW object list
|
|---|
| 160 | * as part of its construction.
|
|---|
| 161 | * @notes Timer is left in TIMER_Stopped state on good creation,
|
|---|
| 162 | * TIMER_Disabled state on problem.
|
|---|
| 163 | */
|
|---|
| 164 | TIMER::TIMER( IRQ* pIRQ, USHORT uTargetMSec, USHORT usStreamType ) :
|
|---|
| 165 | _usStreamType ( usStreamType )
|
|---|
| 166 | {
|
|---|
| 167 | USHORT rc;
|
|---|
| 168 |
|
|---|
| 169 | // Set initial state - "not functional".
|
|---|
| 170 | _eState = TIMER_Disabled;
|
|---|
| 171 |
|
|---|
| 172 | // Setup the new context hook.
|
|---|
| 173 | rc = DevHelp_AllocateCtxHook( (NPFN) TIMER__iCtxHook, &_ctxHookHandle );
|
|---|
| 174 | if (rc)
|
|---|
| 175 | return;
|
|---|
| 176 |
|
|---|
| 177 | // If this is the first Timer we've created, do the "first time only"
|
|---|
| 178 | // work, such as selecting the interrupt technology & programming the
|
|---|
| 179 | // timer interrupt.
|
|---|
| 180 |
|
|---|
| 181 | if (_eTechnology != TIMER_TechNotIdentified) // Will be 0 if 1st time through.
|
|---|
| 182 | _eState = TIMER_Stopped; // Not the first Timer.
|
|---|
| 183 | else { // First time through, set up static vbls.
|
|---|
| 184 |
|
|---|
| 185 | #if 0
|
|---|
| 186 | // Try using timer feature on the audio adapter. The fNoHWTimer
|
|---|
| 187 | // flag is set from the DEVICE= config.sys linne, and can be used to
|
|---|
| 188 | // force system timer.
|
|---|
| 189 | if (! fNoHWTimer) {
|
|---|
| 190 |
|
|---|
| 191 | _pIRQ = pIRQ;
|
|---|
| 192 | if ( _pIRQ )
|
|---|
| 193 | bGoodReturn = _pIRQ->bAddHandler( TIMER::_vTimerHook );
|
|---|
| 194 | else
|
|---|
| 195 | bGoodReturn = FALSE;
|
|---|
| 196 |
|
|---|
| 197 | // We got an interrupt slot, now figure out the values for HW timer setup.
|
|---|
| 198 | if ( bGoodReturn )
|
|---|
| 199 | {
|
|---|
| 200 | _usTimerCount =
|
|---|
| 201 | (USHORT) (((ULONG) uTargetMSec * 1000000L) / ulCS4232_Timer_nSec);
|
|---|
| 202 |
|
|---|
| 203 | // Figure out what this timer count equates to in mSec and uSec.
|
|---|
| 204 | ULONG ulInterval_nSec = (((ULONG) _usTimerCount) * ulCS4232_Timer_nSec);
|
|---|
| 205 | _usInterval_mSec = ulInterval_nSec / 1000000L;
|
|---|
| 206 | _usIntervalErr_uSec =
|
|---|
| 207 | (ulInterval_nSec - (_usInterval_mSec * 1000000L)) / 1000;
|
|---|
| 208 | // Should always be positive, in range of 0..1000.
|
|---|
| 209 |
|
|---|
| 210 | // All set to enable timer interrupt on the chip. Log status.
|
|---|
| 211 | //### Would be very good to check that it works, so we know to go
|
|---|
| 212 | //### to alternate strategy if necessary.
|
|---|
| 213 | _eTechnology = TIMER_AdapterTimerInt;
|
|---|
| 214 | _eState = TIMER_Stopped;
|
|---|
| 215 | }
|
|---|
| 216 | }
|
|---|
| 217 |
|
|---|
| 218 | #else
|
|---|
| 219 | (void)pIRQ; (void)uTargetMSec;
|
|---|
| 220 |
|
|---|
| 221 | if( DevHelp_AttachDD((NPSZ)"TIMER0$ ", (NPBYTE)&TMR0IDCTable) == 0 &&
|
|---|
| 222 | TMR0IDCTable.ProtIDCEntry && TMR0IDCTable.ProtIDC_DS ) {
|
|---|
| 223 | _eTechnology = TIMER_Timer0;
|
|---|
| 224 | _eState = TIMER_Stopped;
|
|---|
| 225 | }
|
|---|
| 226 |
|
|---|
| 227 | #endif
|
|---|
| 228 |
|
|---|
| 229 | // Timer IRQ hook didn't work for some reason, use system timer.
|
|---|
| 230 | if ( _eState == TIMER_Disabled ) {
|
|---|
| 231 | _eTechnology = TIMER_SysTimer;
|
|---|
| 232 | _eState = TIMER_Stopped;
|
|---|
| 233 | }
|
|---|
| 234 | } // End of setup for interrupt operation, executed for 1st Timer only.
|
|---|
| 235 |
|
|---|
| 236 |
|
|---|
| 237 | // If good creation, add Timer to global timer list & reset all time vbls.
|
|---|
| 238 | if ( _eState != TIMER_Disabled ) {
|
|---|
| 239 | _ulTime = _ulSchedTime = 0;
|
|---|
| 240 | _usCumulativeError = 0;
|
|---|
| 241 | pTimerList->PushOnTail( this );
|
|---|
| 242 | }
|
|---|
| 243 |
|
|---|
| 244 | // TIMER is in TIMER_Stopped state upon normal return, TIMER_Disabled
|
|---|
| 245 | // state on error.
|
|---|
| 246 | }
|
|---|
| 247 |
|
|---|
| 248 |
|
|---|
| 249 | /**@external TIMER::vSchedule
|
|---|
| 250 | * Schedule the next Context hook invocation, 0 for next tick.
|
|---|
| 251 | * @param ULONG ulTime - Absolute time (mSec) at which to schedule next Ctx hook.
|
|---|
| 252 | * @return VOID
|
|---|
| 253 | * @notes
|
|---|
| 254 | */
|
|---|
| 255 | VOID TIMER::vSchedule ( ULONG ulTime )
|
|---|
| 256 | {
|
|---|
| 257 | cli();
|
|---|
| 258 | _ulSchedTime = ulTime;
|
|---|
| 259 | sti();
|
|---|
| 260 | }
|
|---|
| 261 |
|
|---|
| 262 |
|
|---|
| 263 | // Get current stream time (milliseconds). Runs in task context.
|
|---|
| 264 | ULONG TIMER::ulGetTime( void )
|
|---|
| 265 | {
|
|---|
| 266 | ULONG ulResult; // Return value.
|
|---|
| 267 |
|
|---|
| 268 | cli();
|
|---|
| 269 | ulResult = _ulTime;
|
|---|
| 270 | sti();
|
|---|
| 271 |
|
|---|
| 272 | return ulResult;
|
|---|
| 273 | }
|
|---|
| 274 |
|
|---|
| 275 | // Set current stream time (milliseconds). Runs in task context.
|
|---|
| 276 | VOID TIMER::vSetTime( ULONG ulTime )
|
|---|
| 277 | {
|
|---|
| 278 | cli();
|
|---|
| 279 | _ulTime = ulTime;
|
|---|
| 280 | sti();
|
|---|
| 281 | }
|
|---|
| 282 |
|
|---|
| 283 |
|
|---|
| 284 |
|
|---|
| 285 | /**@internal TIMER::_isAnyRunning()
|
|---|
| 286 | * Predicate that determines whether any Timer is in Running state.
|
|---|
| 287 | * @param None.
|
|---|
| 288 | * @return BOOL TRUE iff at least one Timer is in Running state.
|
|---|
| 289 | * @notes Normally called in Task context by Start and Stop routines.
|
|---|
| 290 | */
|
|---|
| 291 | static BOOL TIMER::_isAnyRunning( void )
|
|---|
| 292 | {
|
|---|
| 293 | TIMER* pTimer; // Used to find Timers in AudioHW list.
|
|---|
| 294 |
|
|---|
| 295 | // Walk through all Timers, seeking one that is running.
|
|---|
| 296 | pTimer = (TIMER*) pTimerList->Head();
|
|---|
| 297 | while (pTimer != NULL) {
|
|---|
| 298 | if ( pTimer->_eState == TIMER_Running )
|
|---|
| 299 | return TRUE;
|
|---|
| 300 | pTimer = (TIMER*) pTimer->pNext;
|
|---|
| 301 | }
|
|---|
| 302 | return FALSE;
|
|---|
| 303 | }
|
|---|
| 304 |
|
|---|
| 305 |
|
|---|
| 306 | /**@internal TIMER::_iStart
|
|---|
| 307 | * Internal worker to start the timer operation. If we have a HW timer,
|
|---|
| 308 | * will start the interrupt generation. If kernel timer, start it.
|
|---|
| 309 | * @param None
|
|---|
| 310 | * @return int 1 (Boolean TRUE) if timer starts properly
|
|---|
| 311 | * @return int 0 (Boolean FALSE) if problem starting timer
|
|---|
| 312 | * @notes Will force Ctx hook to be scheduled on next tick.
|
|---|
| 313 | */
|
|---|
| 314 | int TIMER::_iStart( void )
|
|---|
| 315 | {
|
|---|
| 316 | USHORT rc;
|
|---|
| 317 |
|
|---|
| 318 | // Disable any any pre-existing timer and reset state variables.
|
|---|
| 319 | if ( _eState != TIMER_Stopped )
|
|---|
| 320 | Stop(); // Stop() method on this Timer.
|
|---|
| 321 |
|
|---|
| 322 | // _eState now equals TIMER_Stopped.
|
|---|
| 323 |
|
|---|
| 324 | // Reset Timer vbls & start Timer interrupt if it's not already running.
|
|---|
| 325 | // MMPM/2 will reset the stream time when the user rewinds, etc.
|
|---|
| 326 |
|
|---|
| 327 | _ulSchedTime = 0; // Force arming ctx hook on next tick.
|
|---|
| 328 | _usCumulativeError = 0; // Zero out any fractional time.
|
|---|
| 329 |
|
|---|
| 330 | if ( _isAnyRunning() ) // If timer hardware already running
|
|---|
| 331 | _eState = TIMER_Running; // Then just flip our state.
|
|---|
| 332 | else { // Otherwise start interrupts.
|
|---|
| 333 | switch ( _eTechnology ) {
|
|---|
| 334 | #if 0
|
|---|
| 335 | case TIMER_AdapterTimerInt:
|
|---|
| 336 | bGoodReturn = _pIRQ->bEnableHandler( TIMER::_vTimerHook );
|
|---|
| 337 | /*** Our ISR can be called at any point after this, so all our state
|
|---|
| 338 | * variables must be consistent. Even though we haven't enabled
|
|---|
| 339 | * the Timer on the chip, we could get an Int from Wave operations
|
|---|
| 340 | * (which should be ignored).
|
|---|
| 341 | */
|
|---|
| 342 | // Everything is setup for the interrupt, now enable it on the chip.
|
|---|
| 343 | if (bGoodReturn) {
|
|---|
| 344 | _vStartHWTicks();
|
|---|
| 345 | _eState = TIMER_Running;
|
|---|
| 346 | }
|
|---|
| 347 | break;
|
|---|
| 348 | #endif
|
|---|
| 349 |
|
|---|
| 350 | case TIMER_Timer0:
|
|---|
| 351 | _usInterval_mSec = 4;
|
|---|
| 352 | _usIntervalErr_uSec = 0;
|
|---|
| 353 |
|
|---|
| 354 | rc = (*(PTMRFN)TMR0IDCTable.ProtIDCEntry)
|
|---|
| 355 | (TMR0_REG, (ULONG)TIMER::_vTimerHook, _usInterval_mSec);
|
|---|
| 356 | if (! rc) {
|
|---|
| 357 | _eState = TIMER_Running;
|
|---|
| 358 | break;
|
|---|
| 359 | }
|
|---|
| 360 |
|
|---|
| 361 | _eTechnology = TIMER_Timer0Fallback;
|
|---|
| 362 |
|
|---|
| 363 | case TIMER_SysTimer:
|
|---|
| 364 | _usInterval_mSec = 31;
|
|---|
| 365 | _usIntervalErr_uSec = 250;
|
|---|
| 366 |
|
|---|
| 367 | rc = DevHelp_SetTimer( (NPFN) TIMER::_vTimerHook );
|
|---|
| 368 | if (! rc)
|
|---|
| 369 | _eState = TIMER_Running;
|
|---|
| 370 | break;
|
|---|
| 371 | }
|
|---|
| 372 | }
|
|---|
| 373 |
|
|---|
| 374 | if ( _eState != TIMER_Running ) { // Set error condition & log problem.
|
|---|
| 375 | _eState = TIMER_Disabled;
|
|---|
| 376 | return 0;
|
|---|
| 377 | }
|
|---|
| 378 |
|
|---|
| 379 | return 1;
|
|---|
| 380 | }
|
|---|
| 381 |
|
|---|
| 382 |
|
|---|
| 383 | /**@internal TIMER::_iStop
|
|---|
| 384 | * Internal worker to shutdown the timer interrupt and the timer clock.
|
|---|
| 385 | * @param None.
|
|---|
| 386 | * @return int 0.
|
|---|
| 387 | * @notes Will not destroy "next scheduled ctx hook" information.
|
|---|
| 388 | * @notes Leaves timer in TIMER_Stopped state. Timer interrupts are stopped
|
|---|
| 389 | * as well if no other Timer is running.
|
|---|
| 390 | */
|
|---|
| 391 | int TIMER::_iStop( void )
|
|---|
| 392 | {
|
|---|
| 393 | _eState = TIMER_Stopped; // Stop this Timer.
|
|---|
| 394 |
|
|---|
| 395 | if (!_isAnyRunning()) { // If no other Timers are running...
|
|---|
| 396 | switch( _eTechnology ) { // Then shutdown the interrupt.
|
|---|
| 397 | #if 0
|
|---|
| 398 | case TIMER_AdapterTimerInt:
|
|---|
| 399 | // Disable the interrupt on the chip and in the IRQ object.
|
|---|
| 400 | _vStopHWTicks();
|
|---|
| 401 | break;
|
|---|
| 402 | #endif
|
|---|
| 403 |
|
|---|
| 404 | case TIMER_Timer0Fallback:
|
|---|
| 405 | _eTechnology = TIMER_Timer0;
|
|---|
| 406 | case TIMER_SysTimer:
|
|---|
| 407 | DevHelp_ResetTimer( (NPFN) TIMER::_vTimerHook );
|
|---|
| 408 | break;
|
|---|
| 409 |
|
|---|
| 410 | case TIMER_Timer0:
|
|---|
| 411 | (*(PTMRFN)TMR0IDCTable.ProtIDCEntry)
|
|---|
| 412 | (TMR0_DEREG, (ULONG)TIMER::_vTimerHook, 0);
|
|---|
| 413 | break;
|
|---|
| 414 | }
|
|---|
| 415 | }
|
|---|
| 416 |
|
|---|
| 417 | return 0;
|
|---|
| 418 | }
|
|---|
| 419 |
|
|---|
| 420 |
|
|---|
| 421 | // Perform the per tick, per timer object tasks: maintain time,
|
|---|
| 422 | // arm a context hook if it's time to run the MIDI parser. Runs
|
|---|
| 423 | // in interrupt context.
|
|---|
| 424 | VOID TIMER::_vPerTickTasks( void )
|
|---|
| 425 | {
|
|---|
| 426 | USHORT uMSec;
|
|---|
| 427 |
|
|---|
| 428 | if ( _eState == TIMER_Running ) {
|
|---|
| 429 | // Update our clock.
|
|---|
| 430 | uMSec = _usInterval_mSec;
|
|---|
| 431 | _usCumulativeError += _usIntervalErr_uSec;
|
|---|
| 432 | if (_usCumulativeError >= 1000) {
|
|---|
| 433 | _usCumulativeError -= 1000;
|
|---|
| 434 | ++uMSec;
|
|---|
| 435 | }
|
|---|
| 436 | _ulTime += uMSec;
|
|---|
| 437 |
|
|---|
| 438 | // Set the context hook if it's time.
|
|---|
| 439 | if ((_ulSchedTime == 0) || (_ulTime >= _ulSchedTime))
|
|---|
| 440 | {
|
|---|
| 441 | // Arm ctx hook, pass stream type to the ctx hook handler.
|
|---|
| 442 | DevHelp_ArmCtxHook( _usStreamType, _ctxHookHandle );
|
|---|
| 443 | _ulSchedTime = (ULONG)-1;
|
|---|
| 444 | }
|
|---|
| 445 | }
|
|---|
| 446 | }
|
|---|
| 447 |
|
|---|
| 448 | #if 0
|
|---|
| 449 |
|
|---|
| 450 | // Start the generation of HW timer ticks on the chip.
|
|---|
| 451 | static VOID TIMER::_vStartHWTicks( void )
|
|---|
| 452 | {
|
|---|
| 453 | }
|
|---|
| 454 |
|
|---|
| 455 |
|
|---|
| 456 | // Start the generation of HW timer ticks on the chip.
|
|---|
| 457 | static VOID TIMER::_vStopHWTicks( void )
|
|---|
| 458 | {
|
|---|
| 459 | }
|
|---|
| 460 |
|
|---|
| 461 | #endif
|
|---|
| 462 |
|
|---|