MMPM/2 Device Driver Reference:Using the High-Resolution Timer

From EDM2
Jump to: navigation, search
MMPM/2 Device Driver Reference
  1. Adding Support for Audio and Video Adapters
  2. Audio Physical Device Driver Template
  3. Audio Virtual Device Driver Template
  4. MAD16 PDD and VDD Sample Device Drivers
  5. PDD Sample for Video Capture Adapters
  6. PDD Sample for MPEG Video Playback Devices
  7. Audio Sample for Vendor-Specific Drivers
  8. Using the High-Resolution Timer
  9. Real-Time MIDI Subsystem
  10. Audio Device Driver Exerciser (PMADDE) Tool
  11. AP2/P2STRING Tool
  12. Ultimotion Data Stream Specification
  13. DDCMD Messages
  14. SHD Messages
  15. Vendor-Specific Driver Commands
  16. IOCtl Functions
  17. Data Types
  18. Types of MIDI Messages
  19. Notices
  20. Glossary

Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation

The high-resolution timer (HRT) is used to provide millisecond-accuracy timing services to device drivers and applications.

Introduction

The high-resolution timer package includes the following files:

TIMER0.SYS the high-resolution timer driver
CLOCK01.SYS a modified CLOCK01.SYS for high-resolution timer support
CLOCK02.SYS modified CLOCK02.SYS for high-resolution timer support
TMR0_IDC.H the header file used to communicate with the high-resolution timer from another PDD
TMR0_IOC.H the header file for IOCtl interface to the high-resolution timer
TIMER0.DDP the DDINSTAL installation script

Installation

To install the high-resolution timer device driver, follow these steps:

1. Remove the readonly attribute from \OS2\BOOT\CLOCK*.SYS with the attrib command, e.g.:

-r \os2\boot\clock*.sys

where * is the number in the name of your existing clock-system file.

2.Make a backup of \OS2\BOOT\CLOCK*.SYS.

3.Use DDINSTAL to install the driver.

4.Reboot.

Note: The high-resolution timer is intended for use with OS/2 Warp 3.0 and later. It has not been tested and is not supported under any previous version of OS/2. If you do use this timer with any previous version of OS/2, you do so at your own risk.

Information for Microsoft C 6.0 Users

The header files were designed for Watcom C/C++ 10.0. If you are using Microsoft C 6.0, the only change that should be needed is to change the __far, __cdecl, and __loadds keywords to _far, _cdecl, and _loadds - i.e., replace the double underscores with single underscores. To date, however, this has not been verified.

Ring-0 Usage

The high-resolution timer provides the following two services:

callback every n milliseconds 
where n is an integer >= 1. This first option allows your device driver to register itself with the high-resolution timer . The device driver provides a 16:16 address of a function that the high-resolution timer calls every n milliseconds. Registration (and deregistration) cannot occur during interrupt time.
If a device driver re-registers itself-by calling the registration function with the same 16:16 pointer-the high-resolution timer simply changes the count. This can occur at interrupt time.
query the current time 
This second option is used to obtain a 16:16 pointer to the current count variable. This variable can then be read by the device driver to obtain the current time. Note that this variable is only updated if the high-resolution timer is active-which is only true if at least one driver or application has registered itself with the high-resolution timer. Also note that this variable can be synchronized to another timeclock.

To call the high-resolution timer, call DevHelp_AttachDD with the first parameter as "TIMER0$". Remember, DDTable must be allocated in the default data segment. If you make it a local non-static variable (i.e., it is on the stack), the DevHelp_AttachDD call will fail. For example:

TIMER0_ATTACH_DD DDTable;
PTMRFN ptmrfn;

DDTable.pfn=NULL;
if( DevHelp_AttachDD((NPSZ) "TIMER0$ ", (NPBYTE) &DDTable))
{
    // TIMER0.SYS not loaded
}

if( !DDTable.pfn )
{
    // Something wrong with TIMER0.SYS
}

ptmrfn=DDTable.pfn;

To register your driver with TIMER0.SYS, do something like this:

#define N    10                            // Call me every N milliseconds

void __far __loadds InterruptHandler( void )
{
   // This function is called every N milliseconds
}

if( ptmrfn( TMR0_REG, (ULONG)InterruptHandler, N))
{
   // Registration failed
}

A ptmrfn() call must occur at ring 0. This means that it cannot be made from your strategy INIT routine. The DevHelp_AttachDD call can be made from the INIT routine.

The ptrmfn() call cannot be made from INIT COMPLETE either. Attempts to do so will result in a return code of 6.

Ring-3 Usage

The IOCtl interface provides the ability to:

obtain a pointer to the clock counter
The pointer returned is a 16:16 pointer, which should be converted to a 0:32 pointer for use with 32-bit applications.
query and set the resolution 
Currently, the resolution is always 1 millisecond. Attempts to set it to another value will be ignored, and querying the driver will always return 1 millisecond. You should set the resolution anyway-in the future, the driver may actually use a lower resolution to conserve resources.
block until a certain time has elapsed

Use the following code as an example of reading the current time. The compiler used is C-Set++ 2.1. If you use a different compiler, you may need another technique for handling 16:16 pointers.

#include "tmr0_ioc.h"

APIRET rc;
HFILE  hfile;
ULONG  ulAction;
ULONG  ulOpenFlag = OPEN_ACTION_OPEN_IF_EXISTS;
ULONG  ulOpenMode = OPEN_FLAGS_FAIL_ON_ERROR |
                    OPEN_SHARE_DENYNONE |
                    OPEN_ACCESS_READWRITE;

ULONG  ulResolution = 1;
ULONG  ulSize1=sizeof( ulResolution );

ULONG  * _Seg16 pulTimer16;
ULONG  ulSize2=sizeof( pulTimer16 );
ULONG  *pulTimer;


rc=DosOpen( "TIMER0$ ", &hfile, &ulAction, 0, 0, ulOpenFlag, ulOpenMode, NULL );
if( rc )
{
   printf( "Couldn't open TIMER0$.\n" );
   return;
}

printf( "TIMER0$ opened.  File Handle is %lu\n",hfile );


rc=DosDevIOCtl( hfile, HRT_IOCTL_CATEGORY, HRT_SET_RESOLUTION,
                &ulResolution, ulSize1, &ulSize1, NULL, 0, NULL );
if( rc )
{
   printf( "Couldn't set resolution.\n" );
   DosClose( hfile );
   return;
}


rc=DosDevIOCtl( hfile, HRT_IOCTL_CATEGORY, HRT_GET_POINTER,
                NULL, 0, NULL, &pulTimer16, ulSize2, &ulSize2 );
if( rc )
{
   printf( "Couldn't get pointer.\n" );
   DosClose( hfile );
   return;
}

pulTimer=pulTimer16;                // Converts a 16:16 pointer to a 0:32 pointer
if( !pulTimer )
{
   printf( "NULL pointer.\n" );
   DosClose( hfile );
   return;
}

// At this point, pulTimer is now a pointer to the timer 0 counter variable

rc=DosClose( hfile );

DosOpen( "TIMER0$" ) registers the application as a client. At this point, the high-resolution timer driver is taking interrupts; so make sure the driver is open only when you need timer services. The pointer is valid for the life of the process; and each call to HRT_GET_POINTER allocates another selector-therefore, this call should be made only once.

To block until a certain time has elapsed, use the following code as an example. For brevity, the details (such as checking return codes) have been omitted.

ULONG  ulDelay=1;                     // Number of milliseconds to wait
ULONG  ulSize2=sizeof( ulDelay );

DosOpen( "TIMER0$  ", &hfile, &ulAction, 0, 0, ulOpenFlag, ulOpenMode, NULL );
DosSetPriority( 0, PRTYC_TIMECRITICAL, 0, 0 );
DosDevIOCtl( hfile, HRT_IOCTL_CATEGORY, HRT_BLOCKUNTIL,
             &ulDelay, ulSize2, &ulSize2, NULL, 0, NULL );

Using a time-critical thread is important.

Common Problems

The following problems might occur due to the nature of the high-resolution driver.

trap D
This is usually caused by using the wrong version of CLOCK0x.SYS. If the old CLOCK0x.SYS is in \OS2, for example, and the new one is in \OS2\ BOOT, the kernel will load the old driver. A quick way to check is to use the command "dir \clock0?.sy? /s" on the boot partition. This will search the entire hard drive for the clock drivers.
clock driver will not load
Either some performance tool is running, or you are using the Warp GA version of CLOCK0x.SYS.

Caveats

The following should be considered when using the high-resolution timer:

  • Only the first parameter to PTMRFN is checked; no other parameters are checked.
  • It is possible to write to the timer count variable from another PDD and disrupt the high-resolution timer.
  • While TIMER0 is active (i.e., while it is taking interrupts), DOS sessions do not have access to timer 0.
  • Performance tools such as C Set's DDE4XTRA.SYS and VisualAge's CPPOPA3.SYS are incompatible with this driver.
  • Device drivers should not depend on having the first tick occur at any particular time.

If a driver registers for 1 millisecond, for example, the first tick might occur less than 1 millisecond after the registration.

Anomalies

The following anomalies currently exist in the use of the high-resolution timer:

  • Having multiple device driver's attached to the high-resolution timer has not been tested.
  • The high-resolution timer sets interrupts at 1 millisecond even if a slower rate would suffice. If one driver wants callbacks every 2 milliseconds and another wants them at 4 milliseconds, the driver could program IRQ 0 for 2-millisecond ticks, but it does not.
  • HRT_FREE_POINTER does not currently do anything.
  • While the high-resolution timer is taking interrupts, DOS sessions (VDMs) will not receive INT 8 or INT 1Ch calls. Only one device driver can receive interrupts on a given IRQ in OS/2; and VTIMER.SYS - the VDD that provides INT 8 and INT 1Ch services to VDMs - expects CLOCK0x.SYS to deliver IRQ 0 interrupts. Because the high-resolution timer gets IRQ 0 interrupts, CLOCK0x.SYS - and therefore VTIMER.SYS - cannot get them.

Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation