;       SCCSID = @(#)idc.asm        1.0 95/01/20
title   clock device driver -- main  [clockdd.asm]
; ****************************************************************************
; *                                                                          *
; *                       IBM/Microsoft Confidential                         *
; *                                                                          *
; *                 Copyright (c) IBM Corporation  1987, 1991                *
; *                 Copyright (c) Microsoft Corp.  1987, 1991                *
; *                           All Rights Reserved                            *
; *                                                                          *
; ****************************************************************************
PAGE    60,132

.xlist
page

        INCLUDE basemaca.inc	      ;macros for MASM
        INCLUDE devhlp.inc              ; the devhlp function eqs
        INCLUDE clkseg.inc              ; segment definitions
        INCLUDE vtdptd.inc

.386p
        extrn   DevHlp:DWORD
        extrn   PTInt:far            ;** Added 8\23\94 AN *****
        extrn   fpfnVTProc:FWORD        ; vtimer entry point
        extrn   IRQ0_Owner:BYTE
        extrn   VTimerRegister:BYTE
        extrn   PddDS:WORD

ClkSwap SEGMENT

      ASSUME      CS:ClkSwap,DS:ClkData,ES:NOTHING,SS:NOTHING

; Jump table for IDCHandler
IDCFuncTable label word
   dw offset ClkSwap:IDCGiveIRQ0
   dw offset ClkSwap:IDCTakeIRQ0
IDCMaxFunc   EQU  ($-IDCFuncTable)/2-1

;********************** START OF SPECIFICATIONS *********************
;*
;* MODULE NAME:  IDCHandler  created 8/23/94 by Anurag Nigam, MultiMedia-DD
;*
;* DESCRIPTIVE NAME:  IDC routine for Clock Device Driver
;*
;* FUNCTION:    This routine is the task-time entry point to the Clock
;*              Device driver from PDDs.  Requested functions are 
;*              dispatched to worker routines.
;* NOTES:
;*      IDCHandler provides two functions:  ReleaseIRQ0(0) and 
;*      TakeIRQ0(1).    
;*
;*   Equivalent Command dispatch table (Highest legal function is 1):
;*
;*   ClkSwap:IDCGiveIRQ0     ; 0 Give up control of IRQ 0
;*   ClkSwap:IDCTakeIRQ0     ; 1 Take back contol of IRQ 0
;*  
;*
;*  ENTRY POINT: IDCHandler
;*    LINKAGE:  FAR
;*
;* USES: AX, EBX, DX, SI
;*
;* INPUT: On stack by C calling convention and when the PDD calls this
;*         IDC the convention should be like below
;*
;*      USHORT  (FAR  *ClockIDC)(USHORT, ULONG, ULONG)
;*
;*      DS    = Points to Clock Device Driver Data Segment
;*
;*
;* OUTPUT:
;*       Return value is the passed back value from the worker routines.
;*      
;*
;* EXIT-NORMAL:
;*      Exit-Normal is when ax = 0.
;*
;* EXIT-ERROR:
;*      Exit-Error is when ax is greater than 0
;*             ax = 1 means invalid function
;*             the returned functions can return other values.
;*
;* INTERNAL REFERENCES:
;*    ROUTINES:     IDCTakeIRQ0, IDCGiveIRQ0
;*
;* EXTERNAL REFERENCES:
;*    STRUCTURES:  none
;*    ROUTINES:  none
;*
;*********************** END OF SPECIFICATIONS **********************

;********************** START OF PSEUDO-CODE   **********************
;*
;* If (Command > MaxIDCFunctions)
;*     return ERROR_INVALID_FUNCTION    ; which is 1
;*
;* return(IDCFuncTable[Command](ulParm1, ulParm2));
;*********************** END OF PSEUDO-CODE    **********************

        public  IDCHandler
IDCHandler      PROC    FAR

        push    bp                    ; Setup stack frame
        mov     bp, sp
 
        push    si
        push    ds
        mov     ax, ClkData
        mov     ds,ax

        mov     si,WORD PTR [bp+6]    ; Command


        cmp     si, IDCMaxFunc         ;is it a valid function?
        ja      short IDCError         ;NO, goto error exit

        shl     si,1                   ;adjust si as word index
        call    cs:IDCFuncTable[si]    ;process requested function
        
IDCExit:pop     ds                     ; the returned value
        pop     si
        pop     bp                     ; will be passed by in ax
        ret           

IDCError:mov    ax,1                   ;ERROR_INVALID_FUNCTION
         jmp    short IDCExit

IDCHandler  ENDP



;********************** START OF SPECIFICATIONS *********************
;*
;* MODULE NAME:  IDCGIveIRQ0  created 8/23/94 by Anurag Nigam, MultiMedia-DD
;*
;* DESCRIPTIVE NAME:  IDC routine to give up contorl of IRQ0
;*
;* FUNCTION:    This routine is the IDC function that will give up control
;*              of IRQ0.  It will preempt VTimer and prevent old OS/2 
;*              functions that use the old ISR from working
;* NOTES:
;*      The Flag IRQ0_Owner is set high to signify that Clock owns IRQ0.
;*      It will ensure that the older OS/2 functions like
;*      Perfview will fail.  The flag also insures that only one current
;*      OS/2 function can gain control.  The calling PDD's DS is used to 
;*      ensure that that PDD will be the only one to call IDCTakeIRQ0.  
;*      VTimer give up control by the VDD/PDD IDC with the parameter 
;*      PreemptT0. VTimerRegister is low initially and goes high when VTimer 
;*      does a VDHOpenPDD.  This way we can assure that a PDD can only take 
;*      IRQ0 during task time, since VDDs load after all PDDs initialized
;*      including the InitComplete Stage of PDDs.  Also, RIPL with 
;*      Ethernet/PC-NET use IRQ0 during PDD Init time, so we want to be sure 
;*      nothing gets control of IRQ0.
;*
;*  ENTRY POINT: IDCHandler
;*    LINKAGE:  NEAR
;*
;* USES: AX, EBX, DL, DX
;*
;* INPUT: On stack by C calling convention and when IDCHandler calls this
;*         normal C calling convention is used.  The parms are not used
;*         yet, they are there for future use.
;*
;*      USHORT NEAR IDCGiveIRQ0(ULONG, ULONG)
;*
;*      DS    = Points to Clock Device Driver Data Segment
;*
;*
;* OUTPUT:
;*       Return value is dependent is function succeeds.
;*
;*
;* EXIT-NORMAL:
;*      Exit-Normal is when ax = 0.
;*
;* EXIT-ERROR:
;*      Exit-   99 - ERROR_DEVICE_IN_USE
;*             733 - ERROR_DevHlp_UnsetIRQ_FAILED
;*             734 - ERROR_NOT_TASK_TIME
;*
;* INTERNAL REFERENCES:
;*    ROUTINES:  fpfnVTProc, VDD/PDD IDC
;*
;* EXTERNAL REFERENCES:
;*    STRUCTURES:  none
;*    ROUTINES:  none
;*
;*********************** END OF SPECIFICATIONS **********************
page
;********************** START OF PSEUDO-CODE   **********************
;
; If ( in task time)
;    return(ERROR_NOT_REGISTER_IN_INIT
; else{
;     If ( does clock not own IRQ0)
;        return(ERROR_DEVICE_IN_USE)
;     else {
;        clock does not own IRQ0 -> IRQ_Owner = FALSE
;        OwnerName = calling PDD's DS;
;        fpfnVTPROC(VTDCMD_PREEMPTT0, 0, 0);
;        if(DevHlp_UnSetIRQ)
;           return(DevHlp_UnsetIRQ_FAILED)
;        else
;           return SUCCESS
;        }
;     }
;
;*********************** END OF PSEUDO-CODE    **********************


      ASSUME      CS:ClkSwap,DS:ClkData,ES:NOTHING,SS:NOTHING
IDCGiveIRQ0  PROC    NEAR
        public   IDCGiveIRQ0

        ; Check that Clock0X owns IRQ0
        cmp     IRQ0_Owner,1        ; Is the clock the owner of IRQ0?
        jnz     AlreadyGone         ; NO, exit

        mov     IRQ0_Owner, 0        ; Clock0X does not owns IRQ0 now

        ; save DS
        mov     bx, WORD PTR [bp-4] ;Get DS of calling PDD off stack
        mov     PddDS, bx

        ; Check if there is DOS support (i.e. VTimer has registered)
        cmp     VTimerRegister,1    ; when VTimer registers, it is after
        jnz     @f                  ; all the PDDs are done.  This allows
                                    ; us to say that a PDD can only reg.
                                    ; during task time.

        ; Tell VTimer to release control of IRQ0
        xor     ebx,ebx
        push    DWORD PTR VTDCMD_PREEMPTT0   ;(TOS+8)=VTDCMD_PREEMPTT0
        push    ebx                          ;(TOS+4)=0
        push    ebx                          ;(TOS)=0
        call    FWORD PTR [fpfnVTProc]

        ;No popping stack since function uses pascal calling convention
        ;fpfnVTProc always returns true

@@:
        ; Release control of IRQ0
        sub     bx,bx
        mov     dl,DevHlp_UnSetIRQ
        call    [DevHlp]
        jc      UnsetProb

        mov     ax,0       ;return value
 
GiveExit: ret

NotTaskTime: mov  ax, 734
             jmp  short GiveExit
 
AlreadyGone: mov  ax, 99
             jmp  short GiveExit

UnsetProb:  mov   ax, 733
            jmp   short GiveExit


IDCGiveIRQ0 ENDP


;********************** START OF SPECIFICATIONS *********************
;*
;* MODULE NAME:  IDCTakeIRQ0 created on 8/23/94 by Anurag Nigam. MultiMedia-DD
;*
;* DESCRIPTIVE NAME:  IDC routine to get back contorl of IRQ0
;*
;* FUNCTION:    This routine is the IDC function that will give back control
;*              of IRQ0.  It will allow VTimer to get HW timer ticks if it
;*              needs them and allow old OS/2 functions to use IRQ0 since 
;*              the old ISR, PTInt, is reenabled.
;*               
;* NOTES:
;*      The Flag IRQ0_Owner is set low to signify that a PDD owns IRQ0.
;*      We check to be sure that a PDD does own IRQ0, if Clock0X owns then
;*      we return an error. The calling PDD's DS is checked to
;*      ensure that the PDD who has control of IRQ0 is the only one who can 
;*      give control back to Clock0X.  VTimer is allowed control of IRQ0 with
;*      the VDD/PDD IDC with the parameter ReleaseT0.  This function is 
;*      guaranteed to fail when it is called in task time because IRQ0_Owner 
;*      is initially high, meaning Clock0X owns IRQ0 so the first test would
;*      fail.  IDCGiveIRQ0 will fail, so the flag IRQ0_Owner could not be 
;*      turned low, so IRQ0_Owner would still be high, so IDCTakeIRQ0 will
;*      fail.
;*
;*  ENTRY POINT: IDCHandler
;*    LINKAGE:  NEAR
;*
;* USES: AX, EBX, DX, 
;*
;* INPUT: On stack by C calling convention and when IDCHandler calls this
;*         normal C calling convention is used.  The parms are not used
;*         yet, they are there for future use.
;*
;*      USHORT NEAR IDCGiveIRQ0(ULONG, ULONG)
;*
;*      DS    = Points to Clock Device Driver Data Segment
;*
;*
;* OUTPUT:
;*       Return value is dependent is function succeeds.
;*
;*
;* EXIT-NORMAL:
;*      Exit-Normal is when ax = 0.
;*
;* EXIT-ERROR: ax equals
;*      Exit-   12 - ERROR_INVALID_ACCESS
;*              17 - NOT_SAME_DEVICE
;*             735 - DevHlp_UnsetIRQ_FAILED
;*
;* INTERNAL REFERENCES:
;*    ROUTINES:  fpfnVTProc, VDD/PDD IDC
;*
;* EXTERNAL REFERENCES:
;*    STRUCTURES:  none
;*    ROUTINES:  none
;*
;*********************** END OF SPECIFICATIONS **********************
page
;********************** START OF PSEUDO-CODE   **********************
;
; If (clock  owns IRQ0)
;     return(ERROR_INVALID_ACCESS)
; else {
;     if ( OwnerDS ! = PddDS)
;        return (NOT_SAME_DEVICE)
;     else {
;        if(DevHlp_UnSetIRQ)
;           return(DevHlp_SetIRQ_FAILED)
;        else {
;           clock owns IRQ0 -> IRQ_Owner = TRUE
;           fpfnVTPROC(VTDCMD_RELEASET0, 0, 0);
;           return SUCCESS
;             }
;          }
;       }
;*********************** END OF PSEUDO-CODE    **********************


      ASSUME      CS:ClkSwap,DS:ClkData,ES:NOTHING,SS:NOTHING
IDCTakeIRQ0  PROC    NEAR
        public  IDCTakeIRQ0
 
        cmp     IRQ0_Owner,0        ; does PDD  own IRQ0?
        jnz     NotTaken            ; NO, exit

        ;Test DS's
        mov     bx,WORD PTR [bp-4]
        cmp     PddDS,bx            ;Are the numbers the same
        jnz     NotOwner            ;No, exit
        
        mov     IRQ0_Owner, 1       ; Clock0X now owns IRQ0

        ; Reset the Clock back to 55ms interrupts
        mov     al,34h              ; Set clock to countdown mode on Counter0 in 16Bits
        out     43h,al
        IODelay
        xor     al,al               ; put in largest number = ffff + 1 = 0
        out     40h,al
        IODelay
        out     40h,al


        ;Set the interrupt handler back to what it was
        mov     ax, offset ClkCode:PTInt
        sub     bx,bx
        mov     dx,DevHlp_SetIRQ
        call    [DevHlp]
        jc      SetProb

        ; Check if there is DOS support (i.e. VTimer has registered)
        cmp     VTimerRegister,1    ; when VTimer registers, it is after
        jnz     @f           ; all the PDDs are done.  This allows

        ; Give control back to VTimer

        sub     ebx,ebx
        push    DWORD PTR VTDCMD_RELEASET0   ;(TOS+8)=VTDCMD_RELEASET0
        push    ebx                          ;(TOS+4)=0
        push    ebx                          ;(TOS)=0
        call    FWORD PTR [fpfnVTProc]
        ;No popping stack since function uses pascal calling convention
        ;Always returns true

@@:     sub     ax,ax               ;return value

TakeExit: ret

NotTaken: mov   ax, 12
          jmp   short TakeExit

SetProb:  mov   ax, 735
          jmp   short TakeExit

NotOwner: mov   ax, 17
          jmp   short TakeExit


IDCTakeIRQ0 ENDP

ClkSwap ENDS

ClkCode SEGMENT
    ASSUME      CS:ClkCode,DS:ClkData,ES:NOTHING,SS:NOTHING

; Created by Anurag Nigam on 8/23/94 MultiMedia-DD
;
; The REAL IDC routine is in the swappable code segment
; This routine is put here because the device header only stores
; the offset of the IDC entry point.  The segment is assumed to be
; the 1st code segment, which is ClkCode in this case.  Therefore,
; we need to put the main entry point in ClkCode, and then do a far
; jump from here to ClkSwap:IDCHandler

    ASSUME      CS:ClkCode,DS:ClkData,ES:NOTHING,SS:NOTHING
ClkIDC          PROC FAR
        public  ClkIDC
        jmp     IDCHandler
ClkIDC          ENDP


ClkCode ENDS


        END
