;*DDK*************************************************************************/
;
; COPYRIGHT    Copyright (C) 1995 IBM Corporation
;
;    The following IBM OS/2 WARP source code is provided to you solely for
;    the purpose of assisting you in your development of OS/2 WARP device
;    drivers. You may use this code in accordance with the IBM License
;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
;    Copyright statement may not be removed.;
;*****************************************************************************/
        PAGE     55,132
        TITLE    ENTER.ASM
        SUBTITLE Header
;/*****************************************************************************
;*
;* SOURCE FILE NAME = ENTER.ASM
;*
;* DESCRIPTIVE NAME = Driver Entry routines
;*
;*
;* VERSION      V2.0
;*
;* DATE         10/01/91
;*
;* DESCRIPTION  Contains routines called on entering and leaving the driver.
;*
;* FUNCTIONS    enter_driver_sem
;*              enter_driver_sem_only
;*              enter_driver
;*              leave_driver
;*              leave_driver_sem_only
;*              req_controller
;*              free_controller
;*              double_enter_driver
;*              check_sem
;*              SeamlessHeartbeat
;*              SeamlessRecover
;*              SEAMLESSTERMINATE
;*              EnterDriverTemp
;*              CheckPVRAM
;*              ring3_WinVisRegionNotify
;*              LockDevice
;*              UnlockDevice
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   09/01/91                     SEL  Original
;*   12/03/92              57589  32-Bit Semaphore Support
;*   11/17/93  Changeteam  75466  Added a debug logger to be used in the
;*                                debug version only.  See the DBGDATA.CMD
;*                                file for info on how to view the debug
;*                                data.
;*
;*****************************************************************************/

        .386

        .MODEL FLAT,SYSCALL

        ASSUME  SS:FLAT, DS:FLAT, CS:FLAT, ES:FLAT

        .xlist

GreSemHandle EQU 14

ifdef   SEAMLESS

INCL_DOSINFOSEG         EQU     1

LINFOSEG        STRUC
lis_pidCurrent  DW      ?
lis_pidParent   DW      ?
lis_prtyCurrent DW      ?
lis_tidCurrent  DW      ?
lis_sgCurrent   DW      ?
lis_rfProcStatus        DB      ?
lis_dummy1      DB      ?
lis_fForeground DW      ?
lis_typeProcess DB      ?
lis_dummy2      DB      ?
lis_selEnvironment      DW      ?
lis_offCmdLine  DW      ?
lis_cbDataSegment       DW      ?
lis_cbStack     DW      ?
lis_cbHeap      DW      ?
lis_hmod        DW      ?
lis_selDS       DW      ?
LINFOSEG        ENDS

endif   ;SEAMLESS

        INCL_DOSSEMAPHORES      equ     1
        INCL_DOSMVDM            equ     1
        INCL_AVIOP              equ     1
        INCL_DOSPROCESS         equ     1

        include pmgre.inc
        include bsedos.inc

        DINCL_ENABLE            equ     1
        DINCL_BITMAP            equ     1

        include driver.inc
        include protos.inc
        include extern.inc
        include assert.mac

ifdef SEAMLESS

        include seamless.inc
endif

        .list

IFDEF DEKKO_BUILD

_Dekko32Trace PROTO SYSCALL,
        usMajor         :WORD,
        usMinor         :WORD,
        usLength        :WORD,
        fpData          :DWORD
ENDIF

;/*
;** Data global to functions in this module
;*/

        .DATA

ifdef   SEAMLESS

        HeartbeatPIDTID DWORD   0

endif   ;SEAMLESS

ifdef FIREWALLS

        enter_flags     DWORD   ?
endif

SEM_MAX_WAIT    EQU     -1

        .CODE

        SUBTITLE enter_driver_sem
        PAGE +
;/***************************************************************************
;*
;* FUNCTION NAME = enter_driver_sem
;*
;* DESCRIPTION   = Waits on the driver's semaphore to make sure that only
;*                 one thread is in the driver at a time.  (We may want to
;*                 optimize this later by gating access to the display hardware
;*                 and driver data structures separately.)
;*                 enter_driver_sem should be called if we are only interested in
;*                 acquiring screen lock.
;*                 leave_driver() is used to free the screen lock acquired by
;*                 enter_driver() and enter_driver_sem().
;*
;*                 Registers Preserved:
;*                     ESI,EDI,BX
;*                 Registers Destroyed:
;*                     None                                                                 *
;*
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = EAX = 0
;* RETURN-ERROR  = EAX != 0
;*
;* C Prototype:                                                               *
;* enter_driver_sem()
;* {
;*       SemEnter(Device);
;*       return(SUCCESS);
;* }
;*
;**************************************************************************/

        ALIGN 4

enter_driver_sem PROC SYSCALL USES EBX

        GET_SEM

        INVOKE  req_controller          ; acquire video controller lock

        ret

enter_driver_sem ENDP

        ALIGN 4

enter_driver_sem_only PROC SYSCALL USES EBX

        GET_SEM

        ret

enter_driver_sem_only ENDP

           SUBTITLE enter_driver
           PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = enter_driver
;*
;* DESCRIPTION
;*
;* Takes care of common tasks that must be performed when entering the
;* driver through a major function handler.  Presently takes care of the
;* following:
;*
;*  1) Waits on the driver's semaphore to make sure that only one thread
;*     is in the driver at a time.  (We may want to optimize this later
;*     by gating access to the display hardware and driver data structures
;*     separately.)
;*
;*  2) Examines the DDC to make sure that the task noted as the DDC owner
;*     is the task presently trying to use it.  If not, we need to update
;*     the screen and private segment selectors in the DDC, and note the
;*     new owner task as well.
;*
;*  3) If the vis region is dirty, call pmwin to get new vis region.
;*     (DCR 24139).
;*
;* enter_driver_sem should be called if we are only interested in
;* acquiring screen lock.
;*
;* leave_driver() is used to free the screen lock acquired by enter_driver()
;* and enter_driver_sem().
;*
;*  Registers Preserved:
;*      ESI,EDI,EBX
;*  Registers Destroyed:
;*      NONE
;*
;* INPUT         = EDX = PDDC (formerly hddc) or 0
;* OUTPUT        = CF = 0, EAX=0
;*
;* RETURN-NORMAL = EAX = 0
;* RETURN-ERROR  = CF = 1, EAX != 0 (PMERR_INV_HDC logged)
;*
;* C Prototype:                                                               *
;* enter_driver()
;* {
;*       do {
;*           SemEnter(Device);
;*           if (bad dc) {
;*               save_error_code(PMERR_INV_HDC);
;*               SemLeave(Device);
;*               return(ERROR);  /* CF = 1; DX:AX = 0 */
;*           }
;*           if (dc is not dirty)
;*               return(SUCCESS);
;*           SemLeave(Device);
;*           WinVisRegionNotify(hdc);    /* call the pmwin function */
;*       } while (TRUE);
;* }
;*
;**************************************************************************/

        ALIGN 4

enter_driver PROC SYSCALL USES EBX

enter_driver_begin:

        push    edx

        GET_SEM

        pop     edx
        mov     pdcGlobal,edx

ifdef JFIX                                                              ;IBMJ
enter_driver_success:                                                   ;IBMJ
endif;JFIX                                                              ;IBMJ

;/*
;** return success if the ddc is not dirty
;*/

        mov     ebx,edx
        cmp     (DDC PTR [ebx]).ddc_usId,DDC_IDENT
        jz      enter_driver_good_ddc   ; good ddc

;/*
;** there is a collision error
;*/

ifdef FIREWALLS

        or      ebx,ebx
        jz      null_ddc
        rip     text,<enter_driver: Bad DDC - possible thread collision>
        jmp     @F

        ALIGN 4

null_ddc:

        rip     text,<enter_driver: Null DDC - can ignore>
@@:

endif
        mov     eax,PMERR_INV_HDC

        save_error_code

        RELEASE_SEM

        mov     eax,0
        stc
        jmp     enter_driver_exit       ; return error (CF = 1, EAX = 0)

        ALIGN 4

enter_driver_good_ddc:

        ddc?    ebx

        test    (DDC PTR [ebx]).ddc_fbAbove,DDC_DIRTY_VISRGN
        jz      enter_driver_exit       ; return success (CF = 0)

;/*
;** the ddc is dirty, we have to call pmwin to get new vis region.
;*/

ifdef JFIX                                                              ;IBMJ
        mov     cl,fGrimReaper                                          ;IBMJ
        push    ecx                     ; We'll use this later.         ;IBMJ
endif;JFIX                                                              ;IBMJ

        push    ebx                     ; remember hddc
        push    ebx

;/*
;** free semaphore before calling pmwin
;*/

        RELEASE_SEM

;/*
;** call pmwin to update vis region
;*/

        pop     ebx
ifdef  JFIX        ;IBMJ
 push edx ;@           <- (*)
endif ;JFIX        ;IBMJ
        PUSH    ESI
        MOV     ESI,EBX

        INVOKE  Vga32CallBack,
                OFFSET ring3_WinVisRegionNotify

        POP     ESI

ifdef JFIX                                                              ;IBMJ

;@           push edx  -> (*)

        GET_SEM

        pop     edx
        mov     pdcGlobal,edx

        pop     ebx                     ; EBX -> ddc
        pop     ecx                     ; CL = fGrimReaper before callback.
        cmp     fGrimReaper,WE_BE_DEAD  ; Are we dead ?
        jnz     ed_no_need_callback     ; No,we don't have to worry anything.
ed_we_are_dead:
        cmp     cl,WE_BE_DEAD           ; Then,WERE we dead before callback ?
        jz      ed_no_need_callback     ; Yes, no problem.

        ; Here, we can recognize the different 'fGrimReaper' before and after
        ; calling back pmwin. This must be because that Death() process had
        ; been finished during the moment just from end of VisRegionCallBack
        ; to reentering pmdisp, when semDriver was leaved.
        ; So, we need to call back VisRegionCallBack once again so that
        ; pmdisp cannnot draw this ddc. She will recalculate visible region
        ; and the answer will be NULL.

        or      (DDC PTR [ebx]).ddc_fbAbove,DDC_DIRTY_VISRGN
       ; This forces callback.
ed_no_need_callback:
        mov     edx,ebx                                 ; DX -> ddc
        jmp     enter_driver_success
else ;JFIX                                                              ;IBMJ
;/*
;** got to do it over again
;*/

        pop     edx                     ; EDX = hddc
        jmp     enter_driver_begin      ; let's re-enter again
endif;JFIX                                                              ;IBMJ

        ALIGN 4

enter_driver_exit:

        jc      enter_driver_error

        INVOKE  req_controller          ; acquire video controller lock

ifdef JFIX                                                              ;IBMJ
        cmp     fGrimReaper,WE_BE_DEAD  ; Are we in background ?
        jnz     @f
 test (DDC PTR [ebx]).ddc_fb,DDC_DEVICE
 jz @f
        cmp     (DDC PTR [ebx]).ddc_crcsClip,0     ; Any clip rects exist ?
        ja      ed_inconsistent_ddc     ; Yes, let it be invisible.
        test    (DDC PTR [ebx]).ddc_fb,DDC_VISIBLE ; Is this ddc visible ?
        jnz     ed_inconsistent_ddc     ; Yes..
        jmp     short @f
 public ed_inconsistent_ddc
ed_inconsistent_ddc::
        mov     (DDC PTR [ebx]).ddc_crcsClip,0
        and     (DDC PTR [ebx]).ddc_fb,NOT DDC_VISIBLE
@@:
        clc                             ; Show success.
endif;JFIX        ;IBMJ

        push    edx

        INVOKE  device_enter_driver

        pop     edx
        clc

enter_driver_error:

        ret

enter_driver  ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = leave_driver
;*
;* DESCRIPTION   = Frees up the driver semaphore as we leave, as well as
;*                 freeing the video controoler.
;*
;*                 Registers Preserved:
;*                     EAX,EDX,ESI,EDI,EDS,EBP
;*                 Registers Destroyed:
;*                     ECX
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        ALIGN 4

leave_driver PROC SYSCALL USES EAX

ifdef FIREWALLS

;/*
;** The driver IS allowed to clear the direction flag, or to leave it
;** unchanged, but the driver is NOT allowed to SET it if it was clear
;** initially.  So if the direction flag in enter_flags is 0 and it is
;** 1 in the cpu flag register, then we have a bug.
;*/

        pushfd
        pop     eax
        not     eax
        or      eax,enter_flags
        test    eax,0400h       ; direction flag
        jnz     dirflag_okay

        rip     text,<leave_driver - bad direction flag>

dirflag_okay:

endif
        INVOKE  free_controller         ; release video controller first

        RELEASE_SEM

        ret

leave_driver ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = leave_driver_sem_only
;*
;* DESCRIPTION   = Frees up the driver semaphore as we leave.
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        ALIGN 4

leave_driver_sem_only PROC SYSCALL USES EAX

        RELEASE_SEM

        ret

leave_driver_sem_only ENDP

           SUBTITLE req_controller
           PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = req_controller
;*
;* DESCRIPTION   = Requests video controller ownership, from Video VDD if necessary
;*
;*                 Registers Destroyed:
;*                     EAX
;*
;* INPUT         = NONE
;* OUTPUT        = Carry Clear
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        ALIGN 4

req_controller PROC SYSCALL USES EBX EDI

ifdef FIREWALLS                                                         ;75466
        push    eax                                                     ;75466
        mov     eax,1                                                   ;75466
        INVOKE  debuglogger                                             ;75466
        pop     eax                                                     ;75466
endif                                                                   ;75466

        test    fbOffScreen,OFFSCR_VDD

        jz      req_owned

        cmp     fGrimReaper,0           ; is screen access currently allowed?
        jne     req_exit1               ; no

        cmp     fControllerMine,0       ; do we already have it?

        jne     req_owned               ; yes
        mov     al,1

        xchg    fControllerOwned,al     ; no, so try to take it

        or      al,al                   ; was it already taken?
        jz      req_owned               ; no

;******************************************************************************
; DANGER DANGER DANGER DANGER DANGER DANGER DANGER DANGER DANGER DANGER DANGER
;
; The following jump was inserted as a TEMPORARY workaround to defect 75466.
; It was NOT a permanent solution and is included here only for documentation
; purposes.
;******************************************************************************
;       jmp     req_owned                                               ;75446

        INVOKE  DosRequestVDD,
                hVideoVDD,              ;hvdd
                0,
                VVDSYSREQ_REQCTRL,
                0,                      ;cbInput
                0,                      ;pInput
                0,                      ;cbOutput
                0                       ;pOutput

ifdef FIREWALLS

        cmp     fControllerOwned,0
        jne     @F

        rip     text,<req_controller - unowned>

 @@:

endif

req_owned:
ifdef  JFIX        ;IBMJ
 mov di,cs
 and di,3
 cmp di,3  ; ring-3 ?
 je req_exit
endif ;JFIX        ;IBMJ

        test    fbShadowFlags,SHADOW_DIRTYREGS

        jz      req_exit
        push    ecx
        push    edx
        stc                             ; don't init any memory locations

        INVOKE  init_hw_regs            ; just initialize default driver state

        pop     edx
        pop     ecx
        and     fbShadowFlags,NOT SHADOW_DIRTYREGS


req_exit:

        inc     fControllerMine         ; make a note that we own it now


req_exit1:

ifdef FIREWALLS                                                         ;75466
        push    eax                                                     ;75466
        mov     eax,2                                                   ;75466
        INVOKE  debuglogger                                             ;75466
        pop     eax                                                     ;75466
endif                                                                   ;75466

       ret

req_controller ENDP


           SUBTITLE free_controller
           PAGE +

;/***************************************************************************
;*
;* FUNCTION NAME = free_controller
;*
;* DESCRIPTION   = Releases video controller ownership, notifying Video VDD
;*                 if necessary
;*
;*                 Registers Destroyed:
;*                     EAX
;*
;* INPUT         = NONE
;* OUTPUT        = Carry Clear
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        ALIGN 4

free_controller PROC SYSCALL USES EDI

ifdef FIREWALLS                                                         ;75466
        push    eax                                                     ;75466
        mov     eax,3                                                   ;75466
        INVOKE  debuglogger                                             ;75466
        pop     eax                                                     ;75466
endif                                                                   ;75466

        cmp     fControllerMine,0
        jle     free_exit
        dec     fControllerMine         ; decrement controller ownership
        jnz     free_exit               ; not done with it yet, though
        test    fbOffScreen,OFFSCR_VDD
        jz      free_exit


        mov     al,0
        mov     fControllerOwned,al
        xchg    fControllerNotify,al    ; do we need to notify Video VDD?

        or      al,al
        jz      free_exit               ; no

        INVOKE  DosRequestVDD,
                hVideoVDD,              ;hvdd
                0,
                VVDSYSREQ_FREECTRL,
                0,                      ;cbInput
                0,                      ;pInput
                0,                      ;cbOutput
                0                       ;pOutput

free_exit:

ifdef FIREWALLS                                                         ;75466
        push    eax                                                     ;75466
        mov     eax,4                                                   ;75466
        INVOKE  debuglogger                                             ;75466
        pop     eax                                                     ;75466
endif                                                                   ;75466

        ret

free_controller ENDP

ifdef FIREWALLS                                                         ;75466
;/***************************************************************************
;*
;* FUNCTION NAME = debuglogger
;*
;* DESCRIPTION   = Logs data for debug analysis, called from req_controller
;*                 and free_controller at their entry and exit.  Stores the
;*                 values of fbOffScreen, fCntlerMine, fCntlerNotify,
;*                 fGrimReaper, and fCntlerOwned, as well as the pid and tid
;*                 of the executing process.
;*
;*                 To view the output, see the DBGDATA.CMD file.
;*
;*                   Registers Destroyed:
;*                         none
;*
;* INPUT         = AX indicates when it was called:
;*                    1 - Entering req_controller
;*                    2 - Leaving  req_controller
;*                    3 - Entering free_controller
;*                    4 - Leaving  free_controller
;* OUTPUT        = writes to the vvddebuginfo data area
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  =
;*
;* Pseudo - Code
;*
;* vvdebuglog()
;* {
;*      load address of save area;
;*      get number of entries;
;*      if ( number of entries > maximum allowed )
;*      {
;*              number of entries = 0;
;*              get, increment, and store number of times buffer filled;
;*      }
;*      increment and store number of entries;
;*      calculate offset of record number to use (32 bytes per record area);
;*      add offset of record number to base address;
;*      get pid and tid;
;*      store pid, tid, flags into data area;
;* }
;*
;***************************************************************************/

        ALIGN 4                                                         ;75466
                                                                        ;75466
debuglogger PROC SYSCALL USES EAX EBX ECX EDX EDI                       ;75466
        LOCAL   myppib            :DWORD,                               ;75466
                myptib            :DWORD                                ;75466
                                                                        ;75466
        lea     edi,debugdataarea               ; address of save area  ;75466
        mov     ecx,dword ptr [edi]             ; first word is # of entries  ;75466
        cmp     ecx,[numdebugrecords]           ; filled up the buffer? ;75466
        jb      @f                              ; no                    ;75466
                                                                        ;75466
        mov     ecx,dword ptr [debugdataarea+4] ; get "wrapped" counter ;75466
        inc     ecx                             ; increment             ;75466
        mov     dword ptr [debugdataarea+4],ecx ;   and store it        ;75466
        mov     ecx,0                           ; start back at the beginning ;75466
                                                                        ;75466
@@:     inc     ecx                             ; increment # of entries;75466
        mov     dword ptr [debugdataarea],ecx    ; store updated counter;75466
                                                                        ;75466
        shl     ecx,5                           ; offset = record num * 32 ;75466
                                                ; (32 bytes per entry)  ;75466
        add     edi,ecx                         ; add offset to base    ;75466
                                                                        ;75466
        mov     dword ptr [edi],eax             ; store caller id       ;75466
        add     edi,4                                                   ;75466
                                                                        ;75466
        INVOKE  DosGetInfoBlocks,               ; get pid and tid pointers ;75466
                ADDR myptib,                                            ;75466
                ADDR myppib                                             ;75466
                                                                        ;75466
        or      eax,eax                                                 ;75466
        jnz     no_pid                          ; why not???            ;75466
                                                                        ;75466
        mov     ebx, myppib                                             ;75466
        mov     eax,[ebx].pib_s.pib_ulpid       ;pid                    ;75466
        mov     dword ptr [edi],eax                                     ;75466
        add     edi,4                                                   ;75466
                                                                        ;75466
        mov     ebx, myptib                                             ;75466
        mov     eax,[ebx].tib_s.tib_ordinal                             ;75466
        mov     ebx,[ebx].tib_s.tib_ptib2       ; address of TIB        ;75466
        mov     eax,[ebx].tib2_s.tib2_ultid     ; tid                   ;75466
        mov     dword ptr [edi],eax                                     ;75466
        add     edi,4                                                   ;75466
        xor     eax,eax                                                 ;75466
                                                                        ;75466
        jmp     @f                                                      ;75466
                                                                        ;75466
no_pid:                                                                 ;75466
        xor     eax,eax                         ; put in zeros instead  ;75466
        mov     dword ptr [edi],eax                                     ;75466
        add     edi,4                                                   ;75466
        mov     dword ptr [edi],eax                                     ;75466
        add     edi,4                                                   ;75466
                                                                        ;75466
@@:                                                                     ;75466
        mov     al,fbOffScreen                  ; fbOffScreen           ;75466
        mov     dword ptr [edi],eax                                     ;75466
        add     edi,4                                                   ;75466
                                                                        ;75466
        mov     al,fGrimReaper                  ; fGrimReaper           ;75466
        mov     dword ptr [edi],eax                                     ;75466
        add     edi,4                                                   ;75466
                                                                        ;75466
        mov     al,fControllerMine              ; fControllerMine       ;75466
        mov     dword ptr [edi],eax                                     ;75466
        add     edi,4                                                   ;75466
                                                                        ;75466
        mov     al,fControllerOwned             ; fControllerOwned      ;75466
        mov     word ptr [edi],ax                                       ;75466
        add     edi,2                                                   ;75466
                                                                        ;75466
        mov     al,fVDMControllerMine           ; fVDMControllerMine    ;75466
        mov     word ptr [edi],ax                                       ;75466
        add     edi,2                                                   ;75466
                                                                        ;75466
        mov     al,fControllerNotify            ; fControllerNotify     ;75466
        mov     dword ptr [edi],eax                                     ;75466
        add     edi,4                                                   ;75466
                                                                        ;75466
        ret                                                             ;75466
                                                                        ;75466
debuglogger ENDP                                                        ;75466
                                                                        ;75466
endif                                                                   ;75466



;/***************************************************************************
;*
;* FUNCTION NAME = double_enter_driver
;*
;* DESCRIPTION   = Performs the same function as enter_driver except we have
;*                 to ensure two DCs are both clean in this function.
;*                 The screen lock is acquired ONCE only.
;*                 The DCs can be the same but should not be null.
;*                 leave_driver() is used to free the screen lock acquired by
;*                 double_enter_driver().
;*
;*                   Registers Preserved:
;*                         SI,DI,DS,BP
;*                   Registers Destroyed:
;*                         AX,BX,CX,DX,ES,FLAGS
;*
;* INPUT         = SI,DI = hDDCs
;* OUTPUT        = CF = 0
;*
;* RETURN-NORMAL = CF = 0
;* RETURN-ERROR  = CF = 1, DX:AX = 0       (PMERR_INV_HDC logged)
;*
;* Pseudo - Code
;*
;* double_enter_driver()
;* {
;*       do {
;*           SemEnter(Device);
;*           if (bad dcs) {
;*               save_error_code(PMERR_INV_HDC);
;*               SemLeave(Device);
;*               return(ERROR);  /* CF = 1; DX:AX = 0 */
;*           }
;*           if (both dcs are clean)
;*               return(SUCCESS);
;*           SemLeave(Device);
;*           WinVisRegionNotify(dirty dc);       /* call the pmwin function */
;*       } while (TRUE);
;* }
;*
;***************************************************************************/

        ALIGN 4

double_enter_driver PROC SYSCALL USES ESI EDI

double_enter_driver_begin:

        push    esi
        push    edi

        GET_SEM

        pop     edi
        pop     esi

ifdef JFIX                                                              ;IBMJ
double_enter_driver_success:                                            ;IBMJ
endif;JFIX                                                              ;IBMJ

;/*
;** return success if the ddc is not dirty
;*/

        assert  esi,NE,0
        assert  edi,NE,0

        cmp     [esi].DDC.ddc_usId,DDC_IDENT
        jnz     @F                      ;     ddc
        cmp     [edi].DDC.ddc_usId,DDC_IDENT
        jz      good_ddcs               ; both ddcs are good

;/*
;** there is a collision error
;*/

@@:
        rip     text,<double_enter_driver: Bad DDC - possible thread collision>

        mov     eax,PMERR_INV_HDC

        save_error_code

        RELEASE_SEM

        xor     eax,eax
        stc
        jmp     double_enter_driver_exit        ; return error (CF = 1, DX:AX = 0)

        ALIGN 4

;/*
;** we move the dirty ddc into SI, update its vis region, and then retry
;** double_enter_driver again.  we only clean one ddc at a time.
;*/

good_ddcs:

        ddc?    esi
        ddc?    edi
        mov     pdcGlobal,edi ;JMW
        test    [esi].DDC.ddc_fbAbove,DDC_DIRTY_VISRGN
        jnz     clean_this_ddc
        test    [edi].DDC.ddc_fbAbove,DDC_DIRTY_VISRGN
        jz      double_enter_driver_exit        ; return success (CF = 0)
        xchg    esi,edi                         ; we want the dirty ddc in SI!

;/*
;** the ddc in SI is dirty, we call pmwin to get its updated vis region.
;** free semaphore before calling pmwin
;*/

clean_this_ddc:
ifdef JFIX                                                              ;IBMJ
        mov     cl,fGrimReaper                                          ;IBMJ
        push    ecx                     ; We'll use this later.         ;IBMJ
endif;JFIX                                                              ;IBMJ

        RELEASE_SEM

;/*
;** call pmwin to update vis region
;*/

        INVOKE  Vga32CallBack,
                OFFSET ring3_WinVisRegionNotify

ifdef JFIX                                                              ;IBMJ
double_enter_driver_retry2:

        push    esi
        push    edi

        GET_SEM

        pop     edi
        pop     esi

        pop     ecx                     ; CL = fGrimReaper before callback.
        cmp     fGrimReaper,WE_BE_DEAD  ; Are we dead ?
        jnz     ded_no_need_callback    ; No,we don't have to worry anything.
ded_we_are_dead:
        cmp     cl,WE_BE_DEAD           ; Then,WERE we dead before callback ?
        jz      ded_no_need_callback    ; Yes, no problem.

        ; Here, we can recognize the different 'fGrimReaper' before and after
        ; calling back pmwin. This must be because that Death() process had
        ; been finished during the moment just from end of VisRegionCallBack
        ; to reentering pmdisp, when semDriver was leaved.
        ; So, we need to call back VisRegionCallBack once again so that
        ; pmdisp cannnot draw this ddc. She will recalculate visible region
        ; and the answer will be NULL.

        or      [esi].DDC.ddc_fbAbove,DDC_DIRTY_VISRGN
       ; This forces callback.
ded_no_need_callback:
        jmp     double_enter_driver_success
else ;JFIX                                                              ;IBMJ
;/*
;** got to do it over again
;*/

        jmp     double_enter_driver_begin       ; let's re-enter again
endif;JFIX                                                              ;IBMJ

        ALIGN 4

double_enter_driver_exit:

        jc      double_enter_driver_error

        INVOKE  req_controller          ; acquire video controller lock

ifdef  JFIX        ;IBMJ
        cmp     fGrimReaper,WE_BE_DEAD  ; Are we in background ?
        jnz     @f
 test [esi].DDC.ddc_fb,DDC_DEVICE
 jz ded_see_di
        cmp     [esi].DDC.ddc_crcsClip,0     ; Any clip rects exist ?
        ja      ded_inconsistent_si     ; Yes, let it be invisible.
        test    [esi].DDC.ddc_fb,DDC_VISIBLE ; Is this ddc visible ?
        jnz     ded_inconsistent_si     ; Yes..
        jmp     short ded_see_di        ; No problem.
 public ded_inconsistent_si
ded_inconsistent_si::
        mov     [esi].DDC.ddc_crcsClip,0
        and     [esi].DDC.ddc_fb,NOT DDC_VISIBLE
ded_see_di:
 test [edi].DDC.ddc_fb,DDC_DEVICE
 jz @f
        cmp     [edi].DDC.ddc_crcsClip,0     ; Any clip rects exist ?
        ja      ded_inconsistent_di     ; Yes, let it be invisible.
        test    [edi].DDC.ddc_fb,DDC_VISIBLE ; Is this ddc visible ?
        jnz     ded_inconsistent_di     ; Yes..
        jmp     short @f
 public ded_inconsistent_di
ded_inconsistent_di::
        mov     [edi].DDC.ddc_crcsClip,0
        and     [edi].DDC.ddc_fb,NOT DDC_VISIBLE
@@:
        clc                             ; Indicates success.
endif ;JFIX        ;IBMJ

double_enter_driver_error:

        ret

double_enter_driver ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = check_sem
;*
;* DESCRIPTION   = Checks to see if the calling Thread owns the semaphore
;*
;* INPUT         =
;* OUTPUT        =
;*
;* RETURN-NORMAL = Returns 0 if the calling thread owns the semaphore.
;* RETURN-ERROR  = Returns Non Zero if the calling thread does NOT own the semaphore.
;*
;**************************************************************************/

        ALIGN 4

check_sem PROC SYSCALL USES EAX ECX EDX

        xor     eax,eax
        mov     dx,DOS16LOCALINFO
        movzx   edx,dx

        shr     edx,3                            ; Convert to FLAT 32
        shl     edx,16

        mov     cx,[EdX].LINFOSEG.lis_tidCurrent ; Set current thread ID
        shl     ecx,16                           ; tidCurrent in highword
        mov     cx,[EdX].LINFOSEG.lis_pidCurrent ; and current process

        cmp     dword ptr [semDriver].fsrs_ProcID,ecx

        je      @F
        dec     eax
@@:

        ret

check_sem ENDP

ifdef   SEAMLESS

        ALIGN 4

;/***************************************************************************
;*
;* FUNCTION NAME = SeamlessHeartbeat
;*
;* DESCRIPTION   = Checks to see if a seamless session has died while
;*                 it owned the drawing semaphore. If it has, VWIN and
;*                 SeamlessRecover are called to cleanup,
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

SeamlessHeartbeat PROC SYSCALL

        test    fSeamless,SEAMLESS_ACTIVE       ;are we seamless?
        jne     @f                              ;Yes
        jmp     SeamlessHeartbeatExit           ;No, don't bother

        ALIGN 4
@@:
        IFDEF   FIREWALLS

        DebugMsg2 <PMVGA: Semaphore Timeout.>

        ENDIF   ;FIREWALLS

        mov     eax,HeartbeatPIDTID; 
        or      eax,eax
        jnz     CheckHeartbeat

        mov     eax,DWORD PTR semDriver.fsrs_ProcID
        mov     HeartbeatPIDTID,eax             ;Save PID & TID,
        mov     wHeartbeat,0                    ;  reset heartbeat,
        jmp     SeamlessHeartbeatExit           ;  and return.

        ALIGN 4

CheckHeartbeat:

        lea     ebx,semDriver.fsrs_ProcID
        cmp     [ebx],eax
        jne     SeamlessHeartbeatCleanup

        IFDEF   FIREWALLS

        push    eax

        DebugMsg2 <PMVGA: Same PID & TID second time around.>

        pop     eax

        ENDIF   ;FIREWALLS

;/*
;** We timed out on the same PID & TID as the last timeout
;*/

        cmp     wHeartbeat,0
        jne     SeamlessHeartbeatCleanup
                                                ;must call VWIN to cleanup
        IFDEF   FIREWALLS

        push    eax

        DebugMsg2 <PMVGA: Requesting kill of VDM.>

        pop     eax

        ENDIF   ;FIREWALLS

        shld    ecx,eax,16                      ;Put TID in cx

        INVOKE  DosRequestVDD,
                hWinVDD,                ;hvdd
                0,                      ; 
                VWIN_SIGHEARTBEAT,      ; 
                EAX,                    ;cbInput
                0,                      ;pInput
                ECX,                    ;cbOutput
                0                       ;pOutput

        cmp     eax,1
        je      @f
        cmp     eax,2
        ja      @f
        call    SeamlessRecover
@@:

SeamlessHeartbeatCleanup:

        mov     HeartbeatPIDTID,0

SeamlessHeartbeatExit:

        ret

SeamlessHeartbeat ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = SeamlessRecover
;*
;* DESCRIPTION   = A seamless session has died while it had hardware access.
;*                 We need to free up and reset the appropriate resources.
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/
EXTERNDEF   DOS16LOCALINFO:ABS

        ALIGN 4

SeamlessRecover PROC SYSCALL

        cmp     fVDMControllerMine,0             ; does the VDM own the controller?
        je      @f                               ; no
        mov     fVDMControllerMine,0             ; free the controller
        mov     fControllerOwned,0
        or      fbShadowFlags,SHADOW_DIRTYREGS   ;Force the init_hw to take
                                                 ; place at req_controller
        test    fbOffScreen,OFFSCR_VDD

        jz      @f

        xor     al,al
        mov     fControllerOwned,al
        xchg    fControllerNotify,al             ; do we need to notify Video VDD?

        or      al,al
        jz      @f                               ; no

        INVOKE  DosRequestVDD,
                hVideoVDD,                       ;hvdd
                0,                               ; 
                VVDSYSREQ_FREECTRL,              ; 
                0,                               ;cbInput
                0,                               ;pInput
                0,                               ;cbOutput
                0                                ;pOutput

@@:
        PUSH    EDX
        mov     ax,DOS16LOCALINFO
        movzx   edx,ax

        shr     edx,3                            
        shl     edx,16                           

        mov     ax,[EdX].LINFOSEG.lis_tidCurrent ; Set current thread ID
        shl     eax,16                           ; tidCurrent in highword
        mov     ax,[EdX].LINFOSEG.lis_pidCurrent ; and current process
        POP     EDX

        mov     dword ptr [semDriver].fsrs_ProcID,eax

@@:
        RELEASE_SEM

        cmp     eax,0                            ; 61760 error condition
        jne     exit_sr                          ; 61760 error condition

        cmp     [semDriver].fsrs_Usage,0
        jne     @B

        IFDEF   FIREWALLS

        DebugMsg2 <PMVGA: Cleared the semaphore.>

        ENDIF   ;FIREWALLS

exit_sr:
        ret

SeamlessRecover ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = SEAMLESSTERMINATE
;*
;* DESCRIPTION   = A seamless session has died, and we have been called
;*                 by the PM Shield to clean up.
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        ALIGN 4

SEAMLESSTERMINATE PROC SYSCALL,
        ulPid:DWORD

        IFDEF   FIREWALLS

        DebugMsg2 <PMVGA: Called by SHIELD because a Seamless VDM went down.>

        ENDIF   ;FIREWALLS

        mov     eax,ulPid                        ;Get the PID
        cmp     word ptr [semDriver].fsrs_ProcID,ax
        jne     @f

        call   SeamlessRecover

        mov     HeartbeatPIDTID,0

        IFDEF   FIREWALLS

        DebugMsg2 <PMVGA: Cleaned up from termination of an errant Seamless VDM.>

        ENDIF   ;FIREWALLS
@@:
        ret

SEAMLESSTERMINATE ENDP

endif   ;SEAMLESS


;/***************************************************************************
;*
;* FUNCTION NAME = ring3_WinVisRegionNotify
;*
;* DESCRIPTION   = Calls WinVisRegionNotify at Ring 3
;*
;* INPUT         = ESI = pDDC
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        ALIGN 4

ring3_WinVisRegionNotify PROC SYSCALL

        INVOKE  WinVisRegionNotify,
                [esi].DDC.ddc_hdc

        RET

ring3_WinVisRegionNotify ENDP
;/***************************************************************************
;*
;* FUNCTION NAME = LockDevice
;*
;* DESCRIPTION
;*
;* This function is used to synchronize use and update of VisRegion.
;* It allows all current and pending drawing to complete
;* and blocks any further requests by other threads to draw.
;*
;* Upon exit, the only thread that is allowed to continue screen I/O
;* is the one that acquires the lock.
;*
;* All screen I/O operations by other threads will be blocked until
;* UnlockDevice is called.
;*
;* LockDevice and UnlockDevice are used mainly in the critical sections
;* of visible region calculations.
;*
;* To prevent deadlock, it is guaranteed that no Death or Resurrection
;* will be called by the window manager while the VisRegion is locked.
;*
;* INPUT         =  NONE
;* OUTPUT        =  NONE
;*
;* RETURN-NORMAL =  AX = 1 for success
;* RETURN-ERROR  =  AX = 0
;*
;**************************************************************************/

        ALIGN 4

LockDevice  PROC SYSCALL,
        hdc     :DWORD,
        pddc    :DWORD,
        FunN    :DWORD

        mov     edx,pddc

        INVOKE  enter_driver

        jc      ld_exit_no_lock         ; DX:AX = 0 on error


        mov     eax,1

ld_exit_no_lock:

        fw_zero <ecx>

        ret

LockDevice ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = UnlockDevice
;*
;* DESCRIPTION   = This function is used to synchronize use and update of VisRegion.
;*                 It allows all pending screen I/O operations blocked by LockDevice
;*                 to continue. LockDevice and UnlockDevice are used mainly in
;*                 the critical sections of visible region calculations.
;*
;*
;*
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = AX = 1 for success
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        ALIGN 4

UnlockDevice    PROC SYSCALL,
        hdc     :DWORD,
        pddc    :DWORD,
        FunN    :DWORD

;/*
;** free DriverSem see LockDevice
;*/

        INVOKE  leave_driver

        mov     eax,1

        fw_zero <ecx>

        ret

UnlockDevice    ENDP

end
