226 lines
6.4 KiB
C
226 lines
6.4 KiB
C
|
/*================================================================\
|
||
|
| |
|
||
|
| OS/2 Physical Device Driver Example Code |
|
||
|
| for Open Watcom C/C++ |
|
||
|
| |
|
||
|
| STRATEGY.C (Resident portion of driver) |
|
||
|
| |
|
||
|
| This device driver provides a high-resolution timer for OS/2 |
|
||
|
| |
|
||
|
| The source code was adapted from the Fall 1991 issue of |
|
||
|
| IBM Personal Systems Developer magazine. |
|
||
|
| |
|
||
|
| |
|
||
|
| Adapted for Open Watcom C/C++ |
|
||
|
| Original Author: Rick Fishman |
|
||
|
| Code Blazers, Inc. |
|
||
|
| 4113 Apricot |
|
||
|
| Irvine, CA 92720 |
|
||
|
| |
|
||
|
\================================================================*/
|
||
|
|
||
|
#include "devdefs.h"
|
||
|
#include <i86.h>
|
||
|
#include <conio.h>
|
||
|
#include "devreqp.h"
|
||
|
#include "devaux.h"
|
||
|
#include "hrtimer.h"
|
||
|
#include "hrdev.h"
|
||
|
|
||
|
|
||
|
typedef union {
|
||
|
USHORT w;
|
||
|
char b[2];
|
||
|
} tick_mask;
|
||
|
|
||
|
ULONG DevHlp; // DevHelp Interface Address
|
||
|
|
||
|
static TIMESTAMP ReadDataBuf = {
|
||
|
0, // millisecs
|
||
|
0, // nanosecs
|
||
|
1, // version
|
||
|
0 // revision
|
||
|
};
|
||
|
static USHORT UserCount;
|
||
|
static USHORT Last8253;
|
||
|
|
||
|
#define NANOS_IN_TIC 840
|
||
|
#define AMILL 1000000
|
||
|
|
||
|
void pushf( void );
|
||
|
#pragma aux pushf = "pushf" // save flags
|
||
|
|
||
|
void popf( void );
|
||
|
#pragma aux popf = "popf" // restore flags
|
||
|
|
||
|
// want to do a 32 = 16*16 multiply
|
||
|
// without getting a runtime call
|
||
|
#pragma aux mul32 = \
|
||
|
"mul dx" \
|
||
|
modify [dx] \
|
||
|
parm [ax] [dx] \
|
||
|
value [ax dx]
|
||
|
ULONG mul32( USHORT a, USHORT b );
|
||
|
|
||
|
/* ====================== start of interrupt handler ====================== */
|
||
|
|
||
|
static VOID UpdateTimeStamp( USHORT new8253 )
|
||
|
{
|
||
|
USHORT delta;
|
||
|
ULONG nanos;
|
||
|
USHORT mills;
|
||
|
if( Last8253 >= new8253 ) {
|
||
|
delta = Last8253-new8253;
|
||
|
} else { // wrapped
|
||
|
delta = 0xFFFF - new8253 + Last8253;
|
||
|
}
|
||
|
nanos = mul32( delta, NANOS_IN_TIC );
|
||
|
Last8253 = new8253;
|
||
|
nanos += ReadDataBuf.nanosecs;
|
||
|
if( nanos >= AMILL ) { // overflow to millsecs
|
||
|
mills = 1;
|
||
|
nanos -= AMILL; // the most we need to do this is 5 times anyways
|
||
|
while( nanos >= AMILL ) { // overflow into millisecs
|
||
|
++mills; // try and avoid a runtime divide
|
||
|
nanos -= AMILL;
|
||
|
}
|
||
|
ReadDataBuf.millisecs += mills;
|
||
|
}
|
||
|
ReadDataBuf.nanosecs = nanos;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Called on each OS/2 clock tick (MC146818 chip)
|
||
|
* via the SetTimer DevHlp
|
||
|
*/
|
||
|
|
||
|
extern VOID ClkInter( VOID )
|
||
|
{
|
||
|
if( UserCount != 0 ) {
|
||
|
tick_mask ticks;
|
||
|
|
||
|
_disable();
|
||
|
outp( i8253CtrlZeroOrTwo, i8253CmdReadCtrZero );
|
||
|
ticks.b[0] = inp(i8253CounterZero );
|
||
|
ticks.b[1] = inp(i8253CounterZero );
|
||
|
_enable();
|
||
|
UpdateTimeStamp( ticks.w ); // Update running time stamp
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Wrapper for ClkInter that saves/restores the flags, just in case
|
||
|
// (don't add any code to this routine). This routine must do a far
|
||
|
// return.
|
||
|
|
||
|
extern VOID far ClkInter_( VOID )
|
||
|
{
|
||
|
pushf();
|
||
|
ClkInter();
|
||
|
popf();
|
||
|
}
|
||
|
|
||
|
/* ====================== end of interrupt handler ====================== */
|
||
|
|
||
|
static VOID StratNoOp( REQP_HEADER FAR *rp )
|
||
|
{
|
||
|
rp->status = RPDONE;
|
||
|
}
|
||
|
|
||
|
static VOID StratRead( REQP_RWV FAR *rp )
|
||
|
{
|
||
|
tick_mask ticks;
|
||
|
PVOID virt;
|
||
|
|
||
|
_disable();
|
||
|
outp( i8253CtrlZeroOrTwo, i8253CmdReadCtrZero );
|
||
|
ticks.b[0] = inp(i8253CounterZero );
|
||
|
ticks.b[1] = inp(i8253CounterZero );
|
||
|
UpdateTimeStamp( ticks.w ); // Update the running time stamp
|
||
|
if( rp->count < sizeof( TIMESTAMP ) ) {
|
||
|
rp->count = 0; // Caller's buffer is too small
|
||
|
goto Exit;
|
||
|
}
|
||
|
if( DevPhysToVirt( rp->transaddr, rp->count, &virt ) != 0 ) {
|
||
|
rp->count = 0; // DevHlp failed
|
||
|
goto Exit;
|
||
|
}
|
||
|
rp->count = sizeof( TIMESTAMP );
|
||
|
(*(TIMESTAMP FAR*)virt) = ReadDataBuf;
|
||
|
Exit:
|
||
|
_enable();
|
||
|
rp->header.status |= RPDONE;
|
||
|
}
|
||
|
|
||
|
static VOID StratOpen( REQP_OPENCLOSE FAR *rp )
|
||
|
{
|
||
|
tick_mask ticks;
|
||
|
|
||
|
_disable();
|
||
|
if( UserCount == 0 ) {
|
||
|
ReadDataBuf.millisecs = 0;
|
||
|
ReadDataBuf.nanosecs = 0;
|
||
|
outp( i8253CtrlZeroOrTwo, i8253CmdReadCtrZero );
|
||
|
ticks.b[0] = inp(i8253CounterZero );
|
||
|
ticks.b[1] = inp(i8253CounterZero );
|
||
|
Last8253 = ticks.w;
|
||
|
}
|
||
|
++UserCount;
|
||
|
_enable();
|
||
|
rp->header.status |= RPDONE;
|
||
|
}
|
||
|
|
||
|
static VOID StratClose( REQP_OPENCLOSE FAR *rp )
|
||
|
{
|
||
|
_disable();
|
||
|
if( UserCount > 0 ) {
|
||
|
--UserCount;
|
||
|
}
|
||
|
_enable();
|
||
|
rp->header.status |= RPDONE;
|
||
|
}
|
||
|
|
||
|
// Strategy entry point
|
||
|
//
|
||
|
// The strategy entry point must be declared according to the STRATEGY
|
||
|
// calling convention, which fetches arguments from the correct registers.
|
||
|
|
||
|
|
||
|
#pragma aux STRATEGY far parm [es bx];
|
||
|
#pragma aux (STRATEGY) Strategy;
|
||
|
|
||
|
VOID Strategy( REQP_ANY FAR *rp )
|
||
|
{
|
||
|
|
||
|
// Strategy routine for device set in header.c
|
||
|
|
||
|
if( rp->header.command < RP_END ) {
|
||
|
switch( rp->header.command ) {
|
||
|
case RP_INIT:
|
||
|
StratInit( (REQP_INIT FAR *)rp );
|
||
|
break;
|
||
|
case RP_READ:
|
||
|
StratRead( (REQP_RWV FAR *)rp );
|
||
|
break;
|
||
|
case RP_READ_NO_WAIT:
|
||
|
case RP_INPUT_STATUS:
|
||
|
case RP_INPUT_FLUSH:
|
||
|
case RP_WRITE:
|
||
|
case RP_WRITE_VERIFY:
|
||
|
case RP_OUTPUT_STATUS:
|
||
|
case RP_OUTPUT_FLUSH:
|
||
|
StratNoOp( (REQP_HEADER FAR *)rp );
|
||
|
break;
|
||
|
case RP_OPEN:
|
||
|
StratOpen( (REQP_OPENCLOSE FAR *)rp );
|
||
|
break;
|
||
|
case RP_CLOSE:
|
||
|
StratClose( (REQP_OPENCLOSE FAR *)rp );
|
||
|
break;
|
||
|
default:
|
||
|
rp->header.status = RPERR_COMMAND | RPDONE;
|
||
|
}
|
||
|
} else {
|
||
|
rp->header.status = RPERR_COMMAND | RPDONE;
|
||
|
}
|
||
|
}
|