The following IOCtls are defined and supported by the TIMER0$ device driver by way of the DosDevIOCtl call. The IOCtl category code is 80h (defined as HRT_IOCTL_CATEGORY).
The function codes within the HRT_IOCTL_CATEGORY are:
┌──────────┬──────────────────────────────────────────────────┐ │Function │Description │ ├──────────┼──────────────────────────────────────────────────┤ │00h │Get Version │ ├──────────┼──────────────────────────────────────────────────┤ │01h │Get Resolution │ ├──────────┼──────────────────────────────────────────────────┤ │02h │Set Resolution │ ├──────────┼──────────────────────────────────────────────────┤ │03h │Get Pointer to Clock Timer │ ├──────────┼──────────────────────────────────────────────────┤ │04h │Free Pointer to Clock Timer │ ├──────────┼──────────────────────────────────────────────────┤ │05h │Block Until Time Elapses │ ├──────────┼──────────────────────────────────────────────────┤ │06h-7Fh │Reserved │ └──────────┴──────────────────────────────────────────────────┘
An example of the DosDevIOCtl calling convention for reading the current time follows:
#include "tmr0_ioc.h"
APIRET rc = NO_ERROR;
HFILE hfile = NULLHANDLE;
ULONG ulAction = 0L;
ULONG ulResolution = 1;
ULONG ulSize1 = sizeof(ulResolution);
ULONG * _Seg16 pulTimer16; // defines a 16:16 pointer
ULONG ulSize2 = sizeof(pulTimer16);
ULONG *pulTimer;
rc=DosOpen( "TIMER0$ ",
&hfile,
&ulAction,
0,0,
OPEN_ACTION_OPEN_IF_EXISTS,
OPEN_FLAGS_FAIL_ON_ERROR | OPEN_SHARE_DENYNONE |
OPEN_ACCESS_READWRITE,
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);
The DosOpen of TIMER0$ registers the application as a client. At this point, the high-resolution timer (HRT) is taking interrupts, so only open the driver when timer services are needed. The pointer is valid for the life of the process, and each call to HRT_GET_POINTER allocates another selector, so this call should only be made once.
While the HRT is taking interrupts, DOS sessions (VDMs) will not receive INT 8 or INT 1Ch calls. The reason for this is that OS/2 only allows one device driver to receive interrupts on a given IRQ, and VTIMER.SYS (the VDD that provides INT 8 and INT 1Ch services to VDM's) expects CLOCK0x.SYS to deliver IRQ 0 interrupts. When the HRT is active, it receives the IRQ 0 interrupts instead of CLOCK0x.SYS, and thus VTIMER.SYS never gets them either.
Note: Performance tools, such as C Set's DDE4XTRA.SYS and VisualAge's CPPOPA3.SYS, are incompatible with this driver. Performance analysis tools cannot be run while the high-resolution timer is active, and vice versa.
The following code segment illustrates how to block a time-critical thread until a certain time has eleapsed. Return code checking has been omitted for brevity. A time-critical thread should be used for this function.
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);