
#define SCCSID "@(#)vdmmisc.c   6.8 92/02/29"

/***    VDMMISC.C
 *
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 *      Title:
 *
 *      VDMMISC  Exported Procedures
 *
 *      Description:
 *
 *      Exported VDH services to support misscellaneous functions
 *
 *      History:
;***********************************************************************;BN001;
; 01Jul91 - G.L.Fulk                                                    ;BN001;
; Entering a DOS Setting of simulated version can hang the system.      ;BN001;
; If you enter a major version, comma, minor version, comma, enter;     ;BN001;
; no count value, it will hang the system with a Trap E.                ;BN001;
;***********************************************************************;BN001;
 * 1.3  09/28/92 r206 f053758 WW Madden Added VPM begin/end user hooks
 *      11/30/92 r206 57942   Remove perfstrict for vdhregisterperfctrs
 *                            correct the order that parameters are passed
 *                            to pvwregisterperfctrs
 *      03/20/93 r206 63248   Add MMIO perf hooks
 *      12/15/93 r207 76699   Make Perfview Optional with flag PERFVIEW
 *      11/21/94 r207 104925  Rolled in defect 73080 from r206 to r207
 *                            Added VDHVMDetach routine
 */
#define FNO_NUM  FNO_VDMMISC_C
#include <fnomvdm.h>

#define                 VDDSTRICT
#define                 INCL_NONE
#define                 INCL_VDH

#include                <vdmvdh.h>

#ifdef PERFVIEW
#include                <pvwxport.h>
#include                <perfvw.h>
#endif

#include                <string.h>
#include                <beep.h>
#ifdef MMIOPH
  #include <mmph.h>
#endif

extern   LSYSVAL        vdmid_aLSV[];
extern   ULONG          vdmid_sig;
extern   PHEAP          vdmid_phpFixed;
extern   PHEAP          vdmid_phpSwap;
extern   CHAR           vdmid_achTitle[];
extern   ULONG          vdmid_flVDM;

extern   EH             aeh[];
extern   ULONG          nevents;
extern   HEAP           ahp[];
extern   PLOCKENTRYHEAD plockentryhead;
extern   PHDLVDD        phdlVddHead;
extern   PPDDEP         ppddepHead;
extern   PVDDEP         pvddepHead;
extern   GSYSVAL        aGSV[];
extern   HMAPID         hmapid;
extern   SEL            selGDT_PTDA;
extern   SEL            selGDT_PTDA;
extern   USHORT         hmteCur;
extern   ULONG          aErrorCode[];
extern   HKSEM          hksemHVDD;
extern   HKSEM          hksemHLock;

ULONG       PRIVENTRY QueryDataType(PVOID);
HVDM        PRIVENTRY GetVDMHandle(USHORT,BOOL,BOOL);
PHDLVDD     PRIVENTRY OpenVDD(PSZ,BOOL);
BOOL        PRIVENTRY CloseVDD(HVDD);
BOOL        PRIVENTRY RequestVDD(HVDD,HVDM,ULONG,PVOID,PVOID);
PLOCKENTRY  PRIVENTRY vdhLockEntry(PLOCKENTRY);
BOOL        PRIVENTRY vdmmStrcmpi(PSZ,PSZ);
PSZ         PRIVENTRY vdmmGetFileFromPath (PSZ,ULONG);
BOOL        PRIVENTRY vdmmTitleEvent (PSZ);
PSZ         PRIVENTRY vdmmDecodeString (PSZ,PCHAR,PCHAR,PULONG);
ULONG       PRIVENTRY vdmmGetValueFromString (PSZ,ULONG);
ULONG       PRIVENTRY vdmmValidateString (PSZ,ULONG);

#pragma BEGIN_MVDM_INIT_CODE

/***EP+ VDHOpenPDD - Open a PDD
 *
 *      Get the address of the PDD routine which the VDD can use
 *      to communicate with the PDD.  The device name and entry point
 *      must have been registered by the PDD with the dh_RegisterPDD
 *      service.
 *
 *      ENTRY
 *          pszName - Device name
 *          fpfnVDDProc - VDD entry point for use by the PDD.
 *                        Note that this is a 16:32 point, since PDDs
 *                        are 16:16 modules.
 *
 *              ENTRY
 *                  ULONG ulFunc - function code
 *                      All function codes are private to a PDD-VDD pair
 *                  ULONG ul1 - function-specific;
 *                      If a pointer, it is 16:16, and will generally
 *                      be an input packet
 *                  ULONG ul2 - function-specific;
 *                      If a pointer, it is 16:16, and will generally
 *                      be an output packet
 *
 *              EXIT-SUCCESS
 *                  returns !0
 *
 *              EXIT-FAILURE
 *                  returns 0
 *
 *      EXIT-SUCCESS
 *          returns non-zero pointer to the PDD function.
 *          [See dh_RegisterPDD for description of the PDD function.]
 *
 *      EXIT-FAILURE
 *          returns FALSE
 *          VDHGetError may return:
 *              ERROR_FILE_NOT_FOUND
 *              ERROR_GEN_FAILURE
 *
 *      VDM TERMINATION IMPLICATIONS
 *          The VDD should communicate with the PDD to ensure that any
 *          PDD resources for the terminating VDM are freed.
 *
 *      CONTEXT
 *          Init
 *
 *      PCODE
 *          scan list of PDD entry points for pszName
 *          If not found
 *                  return FAILURE
 *          call PDD entry point to register VDD entry point
 *          return PDD entry point to calling VDD
 */

FPFNPDD VDHENTRY VDHOpenPDD(pszName,fpfnVDDProc)
PSZ              pszName;
FPFNVDD          fpfnVDDProc;
/*end*/
{
PPDDEP           ppddep;
ULONG            ul1,ul2;
static FPFNPDD fpfnPDD;                 // temporary compiler bug workaround -JTP


    // Validate calling context
    ASSERTINITONLY;

#ifdef MMIOPH
    MMPHPreVDHOpenPDD()
#endif

    // Check if this PDD is registered
    ppddep=ppddepHead;
    while(ppddep != NULL && vdmmStrcmpi(pszName,ppddep->pddep_szPDD) != TRUE)
        ppddep=ppddep->pddep_ppddep;

    // If a PDD is registered with this name call its entry point, passing
    // VDD entry point.
    if(ppddep != NULL){
        OFFSETOF32(fpfnPDD) = OFFSETOF32(ppddep->pddep_fpfn);

        ul1=SEGMENTOF32(fpfnVDDProc);   // Selector part of VDD entry point
        ul2=OFFSETOF32(fpfnVDDProc);    // Offset part of VDD entry point

        // If PDD entry point fails return error
        if((*ppddep->pddep_fpfn)(PDDCMD_REGISTER,(F16PVOID)ul1,(F16PVOID)ul2) == 0){
            VDHSetError(ERROR_GEN_FAILURE);
#ifdef MMIOPH
            MMPHPostVDHOpenPDD()
#endif
            return FALSE;
        }

        SEGMENTOF32(fpfnPDD) = SEGMENTOF32(ppddep->pddep_fpfn);
#ifdef MMIOPH
        MMPHPostVDHOpenPDD()
#endif
        return(fpfnPDD);                // return back to VDD the PDD entry point
    }
    VDHSetError(ERROR_FILE_NOT_FOUND);
#ifdef MMIOPH
    MMPHPostVDHOpenPDD()
#endif
    return FALSE;
}

#pragma code_seg()
#pragma code_seg("LOAD_TUNEDOS4_SWP","CODE")

/***EP+ VDHRegisterVDD - Register VDD entry points
 *
 *      This routine registers VDD entry points for use by other VDDs
 *      (via VDHOpenVDD/VDHRequestVDD) and by OS/2 applications (via
 *      DOSOpenVDD/DosRequestVDD).
 *
 *      Two or more VDDs may register the same name (pszName).  In this
 *      case, the VDM manager will call each VDDs routine in turn until
 *      one of them returns TRUE.
 *
 *      There is no guaranteed order to the calling sequence.
 *
 *      ENTRY
 *          pszName - Name of the VDD; maximum length is MAXLENXDD.
 *          pfnDosReq - function for use by DosRequestVDD,
 *                          NULL if none
 *          pfnVddReq - function for use by VDHRequestVDD,
 *                          NULL if none
 *
 *              RETCODE pfnDosReq(ulCmd,SGid,nbIn,pReqIn,nbOut,pReqOut)
 *              ENTRY
 *                  SGID  SGid    - Screen group id
 *                  ULONG ulCmd   - Command
 *                  ULONG cbIn    - Input buffer size
 *                  PVOID pReqIn  - Input packet
 *                  ULONG cbOut   - Output buffer size
 *                  PVOID pReqOut - output packet
 *              EXIT
 *                  VDDREQ_PASS - VDM manager will call the next routine
 *                                registered under same name.
 *                  TRUE        - return failure (For APIs 0 is success)
 *                  FALSE       - return the caller success
 *
 *              BOOL pfnVddReq(hvdm,ulCmd,pReqIn,pReqOut)
 *              ENTRY
 *                  HVDM    hvdm    - VDM handle
 *                  ULONG   ulCmd   - Command
 *                  PVOID   pReqIn  - Input packet
 *                  PVOID   pReqOut - output packet
 *              EXIT
 *                  VDDREQ_PASS - VDM manager will call the next routine
 *                                registered under same name.
 *                  TRUE        - return success (For VDH services 1 is success)
 *                  FALSE       - return the caller failure
 *
 *      EXIT-SUCCESS
 *          returns TRUE
 *
 *      EXIT-FAILURE
 *          returns FALSE
 *          System Halt.
 *              If called with invalid parameters, or in incorrect context.
 *
 *      VDM TERMINATION IMPLICATIONS
 *          The VDD should communicate with any VDD and/or OS/2 clients
 *          to ensure that any resources for the terminating VDM are freed.
 *
 *      NOTE
 *          If a VDD fails in its init routine after registering via
 *          VDHRegisterVDD, the deregistration will be done automatically
 *          at the time of VDD unloading.
 *
 *      CONTEXT
 *          Init
 */

BOOL VDHENTRY VDHRegisterVDD(pszName,pfnDosReq,pfnVddReq)
PSZ           pszName;
PFNSYSREQ     pfnDosReq;
PFNDEVREQ     pfnVddReq;
/*end*/
{


PVDDEP        pvddep;
PVOID         p;
PVDDPROC      pvddproc;

#ifdef  VDHSTRICT
    // Validate calling context
    ASSERTINITONLY;

    // Validate parameters
    if((pfnDosReq == NULL && pfnVddReq == NULL)
                   || (strlen(pszName) > MAXLENXDD))
        Panic(("VDHRegisterVDD: Invalid Parameter"));
#endif

#ifdef MMIOPH
    MMPHPreVDHRegisterVDD()
#endif

    // If a VDD already exists with this name link these entry points
    // to previous ones.
    pvddep=pvddepHead;
    while(pvddep != NULL && vdmmStrcmpi(pvddep->vddep_szVDD,pszName) == FALSE)
        pvddep=pvddep->vddep_pvddep;

    if(pvddep == NULL){ // No VDD Registered with this name

        // Allocate VDD entry point structure
        if(VMAllocKHB(VM_HKH_PUB_RESRW, sizeof(VDDEP), (VMHOB)VDDEPOWNER, NA,
                      SSToDS(&p)) != NO_ERROR)
            Panic(("VDHRegisterVDD: Heap Overflow"));

        // Initialize its fields
        pvddep=(PVDDEP)p;
        strcpy(pvddep->vddep_szVDD,pszName);
        pvddep->vddep_hmte = hmteCur;
        pvddep->vddep_vddp=NULL;

        // Put it on list
        pvddep->vddep_pvddep=pvddepHead;
        pvddepHead=pvddep;
    }

    // Allocate space for VDDPROC which will store the OS2/VDD entry points
    if(VMAllocKHB(VM_HKH_PUB_RESRW, sizeof(VDDPROC), (VMHOB)VDDPROCOWNER, NA,
                  SSToDS(&p)) != NO_ERROR)
        Panic(("VDHRegisterVDD: Heap Overflow"));

    // Initialize fields
    pvddproc=(PVDDPROC)p;
    pvddproc->vddproc_pfnvdd=pfnVddReq;
    pvddproc->vddproc_pfnos2=pfnDosReq;

    // Put this record at the head (No order guaranteed)
    pvddproc->pvddproc=pvddep->vddep_vddp;
    pvddep->vddep_vddp=pvddproc;

#ifdef MMIOPH
    MMPHPostVDHRegisterVDD()
#endif

    return TRUE;
}

/***EP+ VDHInstallUserHook - Set a handler for a VDM event.
 *
 *      Set a handler for a specific VDM event.
 *
 *      ENTRY
 *          iEvent - VDM event
 *              VDM_CREATE           - VDM Creation
 *              VDM_TERMINATE        - VDM Termination
 *              VDM_FOREGROUND       - VDM To Foreground
 *              VDM_BACKGROUND       - VDM to Background
 *              VDM_VDD_CREATE_DONE  - VDM VDD Creation Completed
 *              VDM_PDB_DESTROY      - A PDB destroyed in VDM
 *              VDM_PDB_CHANGE       - PDB Changed in VDM
 *              VDM_CODEPAGE_CHANGE  - Code page change event
 *              VDM_TITLE_CHANGE     - VDM Title Change event
 *              VDM_MEMORY_MAPPED_IN - Pages mapped in
 *              VDM_MEMORY_UN_MAPPED - Pages unmapped
 *              VDM_CREATE_DONE      - VDM Creation Completed Successfully
 *              VDM_BEGIN_VPM_TASK   - VDM task started protected mode
 *              VDM_END_VPM_TASK     - VDM DPMI task exited
 *          pfn - handler for this event
 *              RETCODE HOOKENTRY pfnCreate(hvdm)
 *                  ENTRY   hvdm
 *                  EXIT-SUCCESS
 *                      return TRUE
 *                  EXIT-FAILURE
 *                      return FALSE
 *                          VDM creation fails; the VDM Manager calls
 *                          all of the VDM_TERMINATE hooks for all VDDs
 *                          that returned success on this VDM creation.
 *                  CONTEXT VDM Task
 *
 *              RETCODE HOOKENTRY pfnTerminate(hvdm)
 *                  ENTRY   hvdm - VDM being terminated
 *                  EXIT    None
 *                  CONTEXT VDM Task
 *
 *              RETCODE HOOKENTRY pfnForeground(hvdm)
 *                  ENTRY   hvdm - VDM coming foreground
 *                  EXIT    None
 *                  CONTEXT Task
 *
 *              RETCODE HOOKENTRY pfnBackground(hvdm)
 *                  ENTRY   hvdm - VDM going background
 *                  EXIT    None
 *                  CONTEXT Task
 *
 *              RETCODE HOOKENTRY pfnExit()
 *                  ENTRY   None
 *                  EXIT    None
 *                  CONTEXT Init
 *                          Task
 *
 *              RETCODE HOOKENTRY pfnVDDCreateDone(hvdm)
 *                  ENTRY   hvdm
 *                  EXIT    None
 *                  CONTEXT VDM Creation
 *
 *              RETCODE HOOKENTRY pfnPDBDestroyed(hvdm,segPDB)
 *                  ENTRY   hvdm
 *                          segPDB  V86 segment of terminating PDB
 *                  EXIT    None
 *                  CONTEXT VDM TASK
 *
 *              RETCODE HOOKENTRY pfnPDBSwitched(hvdm,segPDB)
 *                  ENTRY   hvdm
 *                          segPDB  V86 segment of new PDB
 *                  EXIT    None
 *                  CONTEXT VDM TASK
 *
 *              VOID HOOKENTRY pfnCodePageChanged(ulCodePage)
 *                  ENTRY   ulCodePage  new code page
 *                  EXIT-SUCCESS
 *                      return TRUE
 *                  EXIT-FAILURE
 *                      return FALSE
 *                          An error is returned on the set code page
 *                          operation, but the rest of the device code
 *                          page handlers are called.
 *                  CONTEXT VDM TASK
 *
 *              VOID HOOKENTRY pfnVDMTitleChange (pszTitle)
 *                  ENTRY   pszTitle  new VDM Title
 *                  EXIT-SUCCESS
 *                      return TRUE
 *                  EXIT-FAILURE
 *                      return FALSE
 *                  CONTEXT VDM Task
 *                  NOTES   1. This event will be called for both full
 *                             screen and windowed VDM.
 *                          2. If pszTitle is NULL, VDD is suppose to treat
 *                             it as VDM's default and original title.
 *                          3. Idealy there should be only one VDD
 *                             for this hook, but this is not a
 *                             restriction. One of the VDD registered
 *                             is responsible for putting the title. Only
 *                             this VDD should return TRUE, all others should
 *                             return FALSE.
 *
 *              RETCODE HOOKENTRY pfnMemoryMappedIn (hvdm,page,cpages,fl)
 *                  ENTRY   hvdm
 *                          page     page address
 *                          cpages   # of pages mapped in from the starting
 *                                   page address
 *                          fl       type of mapping, (see VDHMapPages)
 *                  EXIT-SUCCESS
 *                      return TRUE
 *                  EXIT-FAILURE
 *                      return FALSE
 *                  CONTEXT VDM Task
 *
 *              RETCODE HOOKENTRY pfnMemoryUnMapped (hvdm,page,cpages,fl)
 *                  ENTRY   hvdm
 *                          page     page address
 *                          cpages   # of pages unmapped from the starting
 *                                   page address
 *                          fl       type of mapping, (see VDHMapPages)
 *
 *                  EXIT-SUCCESS
 *                      return TRUE
 *                  EXIT-FAILURE
 *                      return FALSE
 *                  CONTEXT VDM Task
 *
 *              RETCODE HOOKENTRY pfnCreateDone(hvdm)
 *                  ENTRY   hvdm
 *                  EXIT    None
 *                  CONTEXT VDM Creation
 *
 *              RETCODE HOOKENTRY pfnBeginVPMTask(hvdm)
 *                  ENTRY   hvdm
 *                  EXIT    None
 *                  CONTEXT VDM Task
 *
 *              RETCODE HOOKENTRY pfnEndVPMTask(hvdm)
 *                  ENTRY   hvdm
 *                  EXIT    None
 *                  CONTEXT VDM Task
 *
 *      EXIT-SUCCESS
 *          returns TRUE
 *
 *      EXIT-FAILURE
 *          System Halt.
 *              Invalid iEvent or pfn.
 *              Kernel Heap overflow
 *
 *      VDM TERMINATION IMPLICATIONS
 *          The VDM_TERMINATE hook will be called. In case of a partially
 *          created VDM VDM_TERMINATE handler will be called only if all
 *          the registered VDM_CREATE handlers were called successfully.
 *          Partially created VDM may for example be the  case when VDD
 *          A's create handle returned successfully but next VDD B's
 *          create handler failed.iN such a case VDD A's terminate handler
 *          will not be called. VDDs should take care of this point in
 *          deciding what to do in Create handler and what in Create_Done
 *          handler.
 *
 *      CONTEXT
 *          Initialization
 *
 *      PCODE
 *          validate parameters
 *          add the event handler appropriatly in Event handler table
 */

BOOL VDHENTRY VDHInstallUserHook(iEvent,pfn)
ULONG         iEvent;
PUSERHOOK     pfn;
/*end*/
{
PEL           pel;

#ifdef VDHSTRICT
    // Validate calling context
    ASSERTINITONLY;

    // Validate parameters
    if(iEvent >= nevents)
        Panic(("VDHInstallUserHook: Invalid Parameter"));
#endif

#ifdef MMIOPH
    MMPHPreVDHInstallUserHook()
#endif

    // Allocate new event handler
    if(VMAllocKHB(VM_HKH_PUB_RESRW, sizeof(EL), (VMHOB)VDDHOOKOWNER, NA,
                  SSToDS(&pel)) != NO_ERROR)
        Panic(("VDHInstallUserHook: Heap Overflow"));

    // Initialize the fields. Note that  module handle for this event handler
    // is in global variable hmteCur put their by VDMMLoadingVDD.
    pel->el_pfn = (PFAULTHOOK)pfn;
    pel->el_hmte = hmteCur;
    pel->el_pel = NULL;

    // Link it to the list in LIFO/FIFO order as per iEvent
    if(aeh[iEvent].eh_pelFirst == NULL){
        aeh[iEvent].eh_pelFirst = aeh[iEvent].eh_pelLast = pel;
#ifdef MMIOPH
        MMPHPostVDHInstallUserHook()
#endif
        return TRUE;
    }

    if(aeh[iEvent].eh_fOrder == FIFO_ORDER){
        aeh[iEvent].eh_pelLast->el_pel = pel;
        aeh[iEvent].eh_pelLast = pel;
    }
    else{
        pel->el_pel = aeh[iEvent].eh_pelFirst;
        aeh[iEvent].eh_pelFirst = pel;
    }
#ifdef MMIOPH
    MMPHPostVDHInstallUserHook()
#endif
    return TRUE;
}

/***LP+ vdmmFreeVDDEP - Release VDD entry points
 *
 *  Deregister VDD's communication routines registerd by VDHRegisterVDD.
 *
 *  ENTRY
 *      hmte - hmte of the VDD
 *
 *  EXIT
 *      None
 *
 *  CONTEXT
 *      Init
 */

VOID PRIVENTRY  vdmmFreeVDDEP (hmte)
ULONG   hmte;
{
PVDDEP      pv,pvLast,pvFree;
PVDDPROC    pvddproc,pvddprocNext;

    pvLast = pvFree = NULL;
    pv = pvddepHead;
    while(pv != NULL){
        if(pv->vddep_hmte == hmte){
            pvFree = pv;
            pv = pv->vddep_pvddep;
            if(pvLast == NULL)
                pvddepHead = pv;
            else
                pvLast->vddep_pvddep = pv;
            // Relesae chained entry points
            pvddproc = pvFree->vddep_vddp;
            while (pvddproc != NULL){
                pvddprocNext = pvddproc->pvddproc;
                VMFreeKHB(VM_HKH_PUB_RESRW,pvddproc);
                pvddproc = pvddprocNext;
            }
            VMFreeKHB(VM_HKH_PUB_RESRW,pvFree);
        }
        else{
            pvLast = pv;
            pv = pv->vddep_pvddep;
        }
    }
    return;
}

#pragma END_MVDM_INIT_CODE

#pragma BEGIN_MVDM_SWAP_CODE

// seamless start
/***EP+ VDHValidatePID - Is this a valid PID?
 *
 *      Returns a BOOL indicating the validity of a passed in PID.
 *
 *      ENTRY
 *          pid - process ID
 *
 *      EXIT-SUCCESS
 *          returns TRUE
 *
 *      EXIT-FAILURE
 *          returns FALSE
 *
 *      VDM TERMINATION IMPLICATIONS
 *          none
 *
 *      CONTEXT
 *          Task
 *
 *      NOTES
 */

extern int TKPidToPTDA( USHORT Pid, PPTDA *ppPTDA );

BOOL VDHENTRY VDHValidatePID(pid)
PID      pid;
/*end*/
{
PPTDA    pptda;

    // Validate calling context
    ASSERTTASK;
#ifdef MMIOPH
    MMPHPreVDHValidatePID()
#endif
#ifdef MMIOPH
    MMPHPostVDHValidatePID()
#endif
    return(!(TKPidToPTDA(pid,SSToDS(&pptda))));
}

/***EP+ VDHVMGetHandle - Get handle of memory object
 *
 *      Returns the hob of the given linear address in the specified context.
 *
 *      ENTRY   ulLinearAddr = the linear address in question
 *              hPTDA        = the handle to the PTDA in which to check for
 *                             the memory object.  0 means current context.
 *
 *      EXIT    0 = service call failed
 *              non-0 = memory object handle
 *
 *      VDM TERMINATION IMPLICATIONS
 *          none
 *
 *      CONTEXT
 *          Task
 *
 *      NOTES
 */

USHORT VDHENTRY VDHVMGetHandle(ulLinearAddress, hPTDA)
ULONG    ulLinearAddress;
USHORT    hPTDA;
/*end*/
{
    USHORT hob;

    // Validate calling context
    ASSERTTASK;

#ifdef MMIOPH
    MMPHPreVDHVMGetHandle()
#endif

    if ( VMIsPrivatized(ulLinearAddress, hPTDA) ||
         VMGetHandle( ulLinearAddress, hPTDA, SSToDS(&hob)) != NO_ERROR )
      hob = 0;

#ifdef MMIOPH
    MMPHPostVDHVMGetHandle()
#endif
    return(hob);
}

/***EP+ VDHVMAttach - attach memory object to given context
 *
 *      This service attaches the given memory object to the specified
 *      context.
 *
 *      ENTRY   hob   = the memory object to attach to
 *              hPTDA = the handle to the PTDA in which to attach the memory
 *                      object.  0 means current context.
 *              fl    = page manager access permision flags (VPMPG_*)
 *                      0 means use default permissions of target object
 *
 *      EXIT    error code - (NO_ERROR if success)
 *
 *      CONTEXT VDM Task
 *
 *      METHOD
 *
 *      NOTES
 */

INT VDHENTRY VDHVMAttach(VMHOB hob, VMHOB hPTDA, ULONG fl)
{
    ASSERTTASK;
#ifdef MMIOPH
    MMPHPreVDHVMAttach()
#endif

#ifdef MMIOPH
    MMPHPostVDHVMAttach()
#endif
    return (VMAttach( hob, hPTDA, fl ));
}
// seamless end

/***EP+ VDHVMDetach - detach memory object to given context            //73080 
 *                                                                     //73080
 *      This service detaches the given memory object to the specified //73080
 *      context.                                                       //73080
 *                                                                     //73080
 *      ENTRY   hob   = the memory object to detach                    //73080
 *              hPTDA = the handle to the PTDA in which to detach the  //73080
 *                      memory object.  0 means current context.       //73080
 *              fl    = flags for vmFreeMemob                          //73080
 *                                                                     //73080
 *      EXIT    error code - (NO_ERROR if success)                     //73080
 *                                                                     //73080
 *      CONTEXT VDM Task                                               //73080
 *                                                                     //73080
 *      METHOD                                                         //73080
 *                                                                     //73080
 *      NOTES                                                          //73080
 */                                                                    //73080
                                                                       //73080
INT VDHENTRY VDHVMDetach(VMHOB hob, VMHOB hPTDA, ULONG fl)             //73080
{                                                                      //73080
    ASSERTTASK;                                                        //73080
                                                                       //73080
    return (VMFreeMemOb( hob, hPTDA, fl ));                            //73080
}                                                                      //73080


#pragma END_MVDM_SWAP_CODE
#pragma code_seg("SWAP32_24","CODE")
/***EP+ VDHOpenVDD - Open a VDD as registered via VDHRegisterVDD
 *
 *      Returns a handle that can be used with VDHRequestVDD.  The name
 *      must be one that was previously registered via VDHRegisterVDD.
 *
 *      ENTRY
 *          pszName - VDD name
 *
 *      EXIT-SUCCESS
 *          returns non-zero handle to VDD
 *
 *      EXIT-FAILURE
 *          returns FALSE 1. If no such VDD found
 *                        2. VDD found but not registered a worker for
 *                           VDD-VDD communication.
 *          System Halt.
 *              If called with invalid parameters, or in incorrect context.
 *
 *      VDM TERMINATION IMPLICATIONS
 *          The VDD should communicate with the opened VDD to ensure
 *          that any resources for the terminating VDM are freed.
 *
 *          The VDD should communicate with any VDD and/or OS/2 clients
 *          to ensure that any resources created due to the connection are
 *          freed.
 *
 *      CONTEXT
 *          VDM Task (typically during VDM creation)
 *
 *      NOTES
 *          The recommended way for two VDDs to rendezvous with this call
 *          is for each to attempt to open the other at the first creation
 *          of a VDM.  This allows the two VDDs to work together regardless
 *          of the order in which they are initialized.
 */

HVDD VDHENTRY VDHOpenVDD(pszName)
PSZ           pszName;
/*end*/
{

    // Validate calling context
    ASSERTVDMTASK;

#ifdef MMIOPH
    MMPHPreVDHOpenVDD()
#endif

#ifdef MMIOPH
    MMPHPostVDHOpenVDD()
#endif

    return((HVDD)OpenVDD(pszName,VDDVDD));
}

/***EP+ VDHCloseVDD - Close a VDD open with VDHOpenVDD
 *
 *      Close a VDD-VDD session.
 *
 *      ENTRY
 *          hvdd - VDD handle returned by VDHOpenVDD.
 *
 *      EXIT-SUCCESS
 *          VOID
 *
 *      EXIT-FAILURE
 *          System Halt.
 *              If called with invalid hvdd.
 *
 *      VDM TERMINATION IMPLICATIONS
 *          If a VDD has VDD-VDD session for a particular VDM that session
 *          should be closed on that VDM termination.
 *
 *      CONTEXT
 *          VDM Task
 *
 *      NOTES
 *          Basic use of this service is to close those type of
 *          VDD-VDD connection which are temporary or per VDM basis.
 */

VOID VDHENTRY VDHCloseVDD(hvdd)
HVDD    hvdd;
/*end*/
{

    // Validate calling context
    ASSERTVDMTASK;

#ifdef MMIOPH
    MMPHPreVDHCloseVDD()
#endif

    if (CloseVDD(hvdd)) {
#ifdef MMIOPH
    MMPHPostVDHCloseVDD()
#endif
        return;
       }
    else
        Panic(("VDHCloseVDD: Invalid Parameter"));
#ifdef MMIOPH
    MMPHPostVDHCloseVDD()
#endif
}
#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE

/***EP+ VDHHandleFromPID - Return VDM handle for given process ID
 *
 *      Returns the VDM handle for a given process ID.
 *
 *      ENTRY
 *          pid - VDM process ID
 *
 *      EXIT-SUCCESS
 *          return non-zero hvdm
 *
 *      EXIT-FAILURE
 *          returns 0
 *          VDHGetError may return:
 *              ERROR_INVALID_PROCID
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 *
 *      CONTEXT
 *          Task
 *
 *      PCODE
 *          Scan MAPID for this PID
 *          return error if pid not found
 *          else return hvdm
 */

HVDM VDHENTRY VDHHandleFromPID(pid)
PID           pid;
/*end*/
{
HVDM          hvdm;

    // Validate calling context
    ASSERTTASKONLY;

#ifdef MMIOPH
    MMPHPreVDHHandleFromPID()
#endif

    hvdm=GetVDMHandle((USHORT)pid,1,TRUE);
    if(hvdm == NULL){
        VDHSetError(ERROR_INVALID_PROCID);
#ifdef MMIOPH
    MMPHPostVDHHandleFromPID()
#endif
        return((HVDM)0);
    }
#ifdef MMIOPH
    MMPHPostVDHHandleFromPID()
#endif
    return(hvdm);
}
#pragma END_MVDM_SWAP_CODE
#pragma code_seg("SWAP32_24","CODE")

/***EP+ VDHCreateSel - Create a GDT selector to map a linear ragne
 *
 *      Creates a GDT selector that maps a linear range.  This function is
 *      used to create a 16:16 pointer to be passed to a 16-bit PDD.
 *
 *      The lineary address MUST be in 0.1Mb+64k.
 *
 *      You must call VDHDestroySel when you are done with this pointer.
 *
 *      ENTRY
 *          paddr  - linear address of start of buffer
 *          cb     - size of buffer in bytes (1..64k)
 *                      0 interpreted as 64k for compatibility with old VDDs
 *
 *      EXIT-SUCCESS
 *          returns non-zero selector, such that sel:0 points to the
 *              first byte of the linear range.
 *
 *      EXIT-FAILURE
 *          System Halt.
 *              If cb > 64k, or if no more selectors available.
 *
 *      VDM TERMINATION IMPLICATIONS
 *          Any selectors created to map memory in the terminating VDM
 *          must be destroyed with VDHDestroySel.  The VDD must ensure
 *          that any PDD which was given these selectors is done using them.
 *
 *      CONTEXT
 *          Task
 *
 *      PCODE
 *          call SELAllocGDT
 */

SEL VDHENTRY VDHCreateSel(paddr,cb)
PVOID        paddr;
ULONG        cb;
/*end*/
{
SEL          sel;

#ifdef VDHSTRICT
    // Validate calling context
    ASSERTTASKONLY;

    // Parameter Validation
    if(cb > 64*KSIZE)
        Panic(("VDHCreateSel: Invalid Parameter"));
#endif

#ifdef MMIOPH
    MMPHPreVDHCreateSel()
#endif

    if(cb == 0)
        cb = 64*KSIZE;

    // Mark sel 0 to indicate selector allocation is requested
    sel =0;
    if(SELLdrDesc((ULONG)paddr,cb,SSToDS(&sel)) != NO_ERROR) {
#ifdef MMIOPH
        MMPHPostVDHCreateSel()
#endif
        return NULL;
    }
#ifdef MMIOPH
    MMPHPostVDHCreateSel()
#endif
    return(sel);
}

/***EP+ VDHDestroySel - Destroy a selector created by VDHCreateSel
 *
 *      Destroy a GDT selector created by VDHCreateSel.
 *
 *      ENTRY
 *          sel - GDT selector to be Destroyed
 *
 *      EXIT-SUCCESS
 *          None.
 *
 *      EXIT-FAILURE
 *          System Halt.
 *              Selector was not created by VDHCreateSel.
 *
 *      VDM TERMINATION IMPLICATIONS
 *          All selectors created with VDHCreateSel that map the terminating
 *          VDM must be destroyed.
 *
 *      CONTEXT
 *          Task
 *
 *      PCODE
 *          Call SELFreeGDT
 */

VOID VDHENTRY VDHDestroySel(sel)
SEL           sel;
/*end*/
{
    // Validate calling context
    ASSERTTASKONLY;

#ifdef MMIOPH
    MMPHPreVDHDestroySel()
#endif

    if(SELFreeGDT(sel) != NO_ERROR)
        Panic(("VDHDestroySel: Invalid Parameter"));

#ifdef MMIOPH
    MMPHPostVDHDestroySel()
#endif
    return;
}

#pragma code_seg()
#pragma code_seg("SWAP32_18","CODE")
/***EP+ VDHQuerySel - Get selector for address in VDD data or on stack
 *
 *      VDHQuerySel returns the selector part of a 16:16 far pointer
 *      from a flat 0:32 address.  The selector is in the GDT.
 *
 *      This call ONLY works on 0:32 addresses in VDD data objects and
 *      stack.
 *
 *      ENTRY
 *          laddr - virtual address (0:32)
 *
 *      EXIT-SUCCESS
 *          returns non-zero selector
 *
 *      EXIT-FAILURE
 *          System Halt.
 *              If specified linear address was not in a VDD data object.
 *
 *      VDM TERMINATION IMPLICATIONS
 *          Selectors for VDD instance data are destroyed when the
 *          VDM has terminated.  The VDD must ensure that any PDD which
 *          was given this kind of selector is done using it.
 *
 *      CONTEXT
 *          Init - Query allowed on Init and global data only
 *          Task(typically cretion time) - Global/Instance/Stack data
 *                are allowed but init data is not.
 *
 *      NOTES
 *          (1) Each VDD data object is limited to a maximum of
 *              64K in size, so that the following formula may
 *              be used.  A VDD which needs more than 64K of
 *              a particular class (init, global, instance)
 *              must use multiple objects.
 *
 *          (2) The following formula describes how the selector
 *              and the linear address are used to construct a
 *              16:16 pointer:
 *
 *                  sel = VDHQuerySel(laddr);
 *                  fp16 = (far16)(((ULONG)sel << 16) + (USHORT)laddr);
 *
 *              The key point is that the selectors for VDD data are
 *              created to map starting at a 64K linear address boundary
 *
 *          (3) A result of the above formula is that you only need to
 *              call VDHQuerySel once, and the returned selector is
 *              valid for any memory in the particular object which
 *              contains the passed address.  This is true because the
 *              VDM Manager does not allow a VDD data object to span
 *              a 64K linear address boundary.
 *
 *          (4) For VDD instance data, this call will return a different
 *              GDT selector for each VDM.
 *
 *          (5) For VDD instance data, this call may be made only in the
 *              context of a VDM.
 *
 *          (6) For querying a stack address the caller must use SSToDS
 *              on the stack address. So address in this case is formed
 *              as follows:
 *                  sel = VDHQuerySel(SSToDS(laddr));
 *                  fp16 = (far16)(((ULONG)sel << 16) + (USHORT)laddr);
 *
 *      PCODE:
 *         If context is init time and data does'nt belong to
 *            global data or init data return failure
 *         if context is not init time and data belongs to Init data
 *            return failure
 *         Find the heap this data belongs or the stack
 *         if nowhere return error
 *         get the appropriate heap entry
 *         return selector
 */

SEL VDHENTRY VDHQuerySel(laddr)
PVOID        laddr;
/*end*/
{
PHEAPBLOCK   phb;
PHEAP        php;
ULONG        rc;

    // Validate calling context
    ASSERTTASK;

#ifdef MMIOPH
    MMPHPreVDHQuerySel()
#endif

    rc = QueryDataType(laddr);
    if(!rc || (rc == DT_INIT && !ISINITTIME))
        Panic(("VDHQuerySel: Invalid Parameter"));

    if(rc == DT_STACK) {
#ifdef MMIOPH
    MMPHPostVDHQuerySel()
#endif
        return selGDT_PTDA;
       }

    if(rc == DT_INSTANCEFIX)
        php = vdmid_phpFixed;

    else if(rc == DT_INSTANCESWAP)
        php = vdmid_phpSwap;

    else
        php = &ahp[rc];

    phb = php->hp_phb;

    while(phb != NULL){
        // Reach to a block in which laddr lies
        if(phb->hb_pv <= laddr &&
           (ULONG)laddr < (ULONG)phb->hb_pv + phb->hb_cb){
            if(phb->hb_fFree)
                break;
            else {
#ifdef MMIOPH
                MMPHPostVDHQuerySel()
#endif
                return phb->hb_sel;
            }
        }
        else
            phb = phb->hb_phb;
    }

    Panic(("VDHQuerySel: Invalid Parameter"));
#ifdef MMIOPH
    MMPHPostVDHQuerySel()
#endif
}


/***LP QueryDataType - query data type Global/Init/instance/stack
 *
 *  ENTRY   laddr
 *
 *  EXIT-SUCESS returns data type
 *          DT_INIT
 *          DT_GLOBAL
 *          DT_INSTANCEFIX
 *          DT_INSTANCESWAP
 *          DT_STACK
 *
 *  EXIT-FAILURE returns 0
 *
 */

ULONG PRIVENTRY QueryDataType(laddr)
PVOID      laddr;
{
PHEAP        php;

    //Check if laddr belongs to Init data
    php = &ahp[VDMAA_OT_INITDATA];
    if((ULONG)laddr >= (ULONG)php->hp_pv &&
              (ULONG)laddr < (ULONG)php->hp_pv + php->hp_cbMax)
        return DT_INIT;

    //Check if laddr belongs to Global data (Fixed)
    php = &ahp[VDMAA_OT_GLOBALDATA];
    if((ULONG)laddr >= (ULONG)php->hp_pv && (ULONG)laddr <
              (ULONG)php->hp_pv + php->hp_cbMax)
        return DT_GLOBAL;

    //Check if laddr belongs to Global data (Swappable)
    php = &ahp[VDMAA_OT_SWAPGLOBALDATA];
    if((ULONG)laddr >= (ULONG)php->hp_pv && (ULONG)laddr <
              (ULONG)php->hp_pv + php->hp_cbMax)
        return DT_GLOBALSWAP;

    //Check if laddr belongs to stack
    if(((ULONG)laddr > (ULONG)pTSDCur->TSDUserStack) &&
       ((ULONG)laddr < (ULONG)pTSDCur->TSDUserStack + TSDStackSize*2))
        return DT_STACK;

    //Check if laddr belongs to Instance data( Fixed and swappable)
    if(ISVDMTASKTIME){
        php = vdmid_phpFixed;
        if((ULONG)laddr >= (ULONG)php->hp_pv &&
                  (ULONG)laddr < (ULONG)php->hp_pv + php->hp_cbMax)
            return DT_INSTANCEFIX;
        php = vdmid_phpSwap;
        if((ULONG)laddr >= (ULONG)php->hp_pv &&
                  (ULONG)laddr < (ULONG)php->hp_pv + php->hp_cbMax)
            return DT_INSTANCESWAP;

    }
    return 0;
}

#pragma code_seg()
#pragma code_seg("SWAP32_23","CODE")
/***EP+ VDHPutSysValue - Set a system value.
 *
 *      Set a system value.
 *
 *      This service is intended for VBIOS to export system
 *      constants such as ROM memory address and size.
 *
 *      The behavior of the system is undefined if this service
 *      is used for any other purpose.
 *
 *      ENTRY
 *          iIndex - Value index
 *                 VDHGSV_BLACKHOLEADDR
 *                 VDHGSV_BLACKHOLESIZE
 *                 VDHGSV_ARPLADDR
 *                 VDHLSV_RMSIZE
 *          ulValue - value
 *
 *      EXIT-SUCCESS
 *          None
 *
 *      EXIT-FAILURE
 *          System Halt.
 *              If iIndex is invalid.
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 *
 *      CONTEXT
 *          Init
 *          VDM Task (VDM Create)
 *
 *      NOTE
 *          If a local value is being provided by VBIOS(as RMSIZE) it will
 *          allways be placed in current context.
 */

VOID VDHENTRY VDHPutSysValue(iIndex,ulValue)
ULONG         iIndex;
ULONG         ulValue;
/*end*/
{
    ULONG ulMask;
    register PVOID p;

#ifdef VDHSTRICT
    // Validate calling context
    ASSERTVDMTASK;

#ifdef MMIOPH
    MMPHPreVDHPutSysValue()
#endif

    if (iIndex > GSVMAX && iIndex < LSVSTART || iIndex > LSVMAX)
        Panic(("VDHPutSysValue: Invalid Parameter"));
#endif

    if (iIndex >= LSVSTART) {   // Querying Local value
        p = vdmid_aLSV[iIndex - LSVSTART].lsv_pv;
        ulMask = vdmid_aLSV[iIndex - LSVSTART].lsv_ulMask;
    }
    else {
        p = aGSV[iIndex].gsv_pv;
        ulMask = aGSV[iIndex].gsv_ulMask;
    }
    if (ulMask == MASKUCHAR)
        *(PUCHAR)p = (UCHAR)ulValue;
    else if (ulMask == MASKUSHORT)
        *(PUSHORT)p = (USHORT)ulValue;
    else
        *(PULONG)p = ulValue;
#ifdef MMIOPH
    MMPHPostVDHPutSysValue()
#endif
}

#pragma code_seg()
#pragma code_seg("SWAP32_17","CODE")
/***EP+ VDHLockMem - Verify access and lock memory
 *
 *      Verify that a specified region is accessible in the requested
 *      manner, and then "lock" that memory to prevent the accessibility
 *      from changing.
 *
 *      The caller must call VDHUnlockMem to remove this "lock".
 *
 *      This function must be used prior to using any pointers passed
 *      to a VDD from an OS/2 process via DOSRequestVDD.
 *      It may also be useful to the callers of VDHCopyMem
 *      and VDHExchangeMem.
 *
 *      NOTE:  VDHMapPages, VDHReallocatePages, and VDHFreePages
 *      will all block if applied to pages that are locked.
 *      They unblock when the lock is released.
 *
 *      ENTRY
 *          paddr - starting linear address in user process
 *          cbSize - size in bytes
 *          flOptions - access flags;Read and write flags are for
 *                      verification purposes and should not be
 *                      mixed with any other flags.
 *             VDHLM_RESIDENT - lock physical pages
 *             VDHLM_READ - verify read access
 *             VDHLM_WRITE - verify write access
 *             VDHLM_CONTIGUOUS - force physical pages contiguous
 *             VDHLM_NOBLOCK - lock must not give up cpu
 *             VDHLM_16M - must reside below 16 meg. physical
 *             VDHLM_ALIGN - forces bits 12-15 physical addr to match linear
 *          ppl - Pagelist_s array return pointer, if set to VDHLM_NO_ADDR
 *                no array is returned
 *          pel - Points to count of pagelist elements
 *
 *      NOTES
 *          VDHLM_READ and VDHLM_WRITE are verify locks and they
 *          should not be mixed with other flags. Non-verify locks
 *          always make the region resident.
 *
 *          All locks are long term.
 *
 *          ppl must contain enough pagelist_s structures to handle
 *          worst case of no pages contiguous. i.e. one structure per page.
 *
 *          VDHLM_CONTIGUOUS can be used only for 64k or less.
 *
 *          For additional information see the device help routine named
 *          VMLock.
 *
 *      EXIT-SUCCESS
 *          returns non-zero lock handle;  this is a global handle that
 *          may be used in any task context.
 *
 *      EXIT-FAILURE
 *          returns zero
 *          VDHGetError may return:
 *              ERROR_ACCESS_DENIED
 *              ERROR_LOCK_FAILED
 *          System Halt
 *               If cbSize is 0.
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.  There cannot be any memory locked on behalf of a
 *          particular VDM.
 *
 *      CONTEXT
 *          Task
 *
 *      PCODE
 *          map flags
 *          create Lock Entry
 *          call VMLockMem
 */

 HLOCK VDHENTRY VDHLockMem(paddr,cbSize,flOptions,ppl,pel)
 PVOID         paddr;
 ULONG         cbSize;
 ULONG         flOptions;
 struct pagelist_s *ppl;
 ulong_t       *pel;
/*end*/
{
register ULONG         ulFlag = 0;
PLOCKENTRY    ple;

#ifdef VDHSTRICT
    // Validate calling context
    ASSERTTASKONLY;

#ifdef MMIOPH
    MMPHPreVDHLockMem()
#endif

    if(cbSize == 0 ||
       ((flOptions & VDHLM_CONTIGUOUS) && cbSize > 64 * _1K))
        Panic(("VDHLockMem: Invalid Parameter"));
#endif
    // Map flags to VMLock's flags;
    ulFlag = VML_LONG;
    if(flOptions & VDHLM_CONTIGUOUS)
            ulFlag |= VML_CONTIG;
    if(flOptions & VDHLM_16M)
            ulFlag |= VML_16M;
    if(flOptions & VDHLM_NOBLOCK)
            ulFlag |= VML_NOBLOCK;
    if(flOptions & VDHLM_READ)
            ulFlag |= VML_VERIFY;
            ulFlag |= VML_R;
    if(flOptions & VDHLM_WRITE)
            ulFlag |= VML_VERIFY;
            ulFlag |= VML_W;
    if(flOptions &  VDHLM_ALIGN)
            ulFlag |= PG_ERRATA;

    if(ple=vdhLockEntry(NULL)){
        if(VMLockMem((ULONG)paddr,
                      cbSize,ulFlag,
                      &ple->le_hlock,
                      ppl,
                      pel) !=NO_ERROR){
            vdmmAssertEQ(vdhLockEntry(ple),ple);    // Release the lock entry
            VDHSetError(ERROR_ACCESS_DENIED);
#ifdef MMIOPH
            MMPHPostVDHLockMem()
#endif
            return NULL;
        }
        else{
#ifdef MMIOPH
            MMPHPostVDHLockMem()
#endif
            return (HLOCK)ple;
       }
    }
    else{
        VDHSetError(ERROR_LOCK_FAILED);
#ifdef MMIOPH
        MMPHPostVDHLockMem()
#endif
        return NULL;
    }
#ifdef MMIOPH
    MMPHPostVDHLockMem()
#endif
}
/***EP+ VDHUnlockMem - Release memory lock
 *
 *      Reverses effect of VDHLockMem.
 *      Since lock handles are global, this call may be made in any
 *      task context.
 *
 *      ENTRY
 *          hlock - lock handle returned from VDHLockMem.
 *
 *      EXIT-SUCCESS
 *          None.
 *
 *      EXIT-FAILURE
 *          System Halt.
 *              Invalid lock handle.
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 *
 *      CONTEXT
 *          Task
 *
 *      PCODE
 *          remove lock entry from list
 *          call VMUnlock to release lock
 *          call VMFreeKHB to free up memory for lock entry
 */

VOID    VDHENTRY VDHUnlockMem(hlock)
HLOCK hlock;
{
register PLOCKENTRY ple;

    // Validate calling context
    ASSERTTASKONLY;

#ifdef MMIOPH
    MMPHPreVDHUnlockMem()
#endif

    ple = (PLOCKENTRY)hlock;

    //If hlock is invalid shutdown MVDM
    if((ple = vdhLockEntry(ple)) == NULL)
        Panic(("VDHUnlockMem: Invalid Parameter"));

    if(VMUnlock(&ple->le_hlock) != NO_ERROR)
        Panic(("VDHUnLockMem:VMUnlock broken"));

    VMFreeKHB(VM_HKH_PUB_SWAPRW, ple);

#ifdef MMIOPH
    MMPHPostVDHUnlockMem()
#endif

    return;
}

#pragma code_seg()
#pragma code_seg("SWAP32_23","CODE")
/***LP+ vdmmStrcmpi - Case insenstive string compare
 *
 *  ENTRY
 *      psz1 - String 1
 *      psz2 - String 2
 *
 *  EXIT
 *      SUCCESS - returns TRUE
 *      FAILURE - return  FALSE
 */

BOOL PRIVENTRY  vdmmStrcmpi (psz1, psz2)
PSZ psz1;
PSZ psz2;
{
CHAR    ch1,ch2;

    while (TRUE){
        ch1 = *psz1++;
        ch2 = *psz2++;
        if(ch1 == '\0' && ch2 == '\0')      // Exit condition
            return TRUE;
        if(ch1 == ch2)
            continue;
        if(ch1 >= 'A' && ch1 <= 'Z')        // check case insenstive
            ch1 = (CHAR)(ch1 + 'a' - 'A');
        else if (ch1 >= 'a' && ch1 <= 'z')
            ch1 = (CHAR)(ch1 - 'a' + 'A');
        if(ch1 == ch2)
            continue;
        else
            return FALSE;
    }
}
#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE
/***LP+ vdmmUpperCase - Convert given PSZZ string to upper case
 *
 *  ENTRY
 *      pszz - pszz String
 *
 *  EXIT
 *      None
 */

VOID PRIVENTRY  vdmmUpperCase (pszz)
PSZZ pszz;
{
CHAR    ch;
PSZZ    pszzLast;

    if(pszz == NULL)
        return;

    pszzLast = pszz++;

    while (TRUE) {
        if((ch = *pszzLast) != 0){
            if(ch >= 'a' && ch <= 'z')
                *pszzLast = (CHAR)(ch - 'a' + 'A');
        }
        else if (*pszz == 0)
            return;
        pszzLast = pszz++;
    }
    return;
}

#pragma END_MVDM_SWAP_CODE
#pragma code_seg("SWAP32_23","CODE")
/***LP  OpenVDD - Generic routine to open  VDD for VDD/VDD or VDD/OS2
 *                    Communication.
 *      ENTRY
 *          pszName - VDD Name
 *          fType - VDDVDD - VDD/VDD communication
 *                  OS2VDD - OS2/VDD communication
 *
 *      EXIT
 *          SUCCESS
 *              returns the handle (PHDLVDD)
 *          FAILURE
 *              returns FALSE
 */

PHDLVDD PRIVENTRY OpenVDD(pszName,fType)
PSZ         pszName;
BOOL        fType;
{

PHDLVDD     phdlvdd = NULL;
PVDDEP      pvddep;
PVDDPROC    pvddproc;



    // Search for pszName VDD
    pvddep=pvddepHead;
    while(pvddep != NULL && vdmmStrcmpi(pvddep->vddep_szVDD,pszName) == FALSE)
        pvddep=pvddep->vddep_pvddep;

    // Any VDD found with matching name?
    if(pvddep != NULL){

        // Check that there is a function registered for type ftype.
        // It is possible that a VDD registers fro VDD-VDD but an app
        // tries to open a session.
        pvddproc = pvddep->vddep_vddp;
        while(pvddproc != NULL){
            if(fType == VDDVDD){
                if(pvddproc->vddproc_pfnvdd != NULL)
                    break;
            }
            else if(pvddproc->vddproc_pfnos2 != NULL)
                    break;
            pvddproc = pvddproc->pvddproc;
        }

        if(pvddproc){

            // Allocate handle
            if(VMAllocKHB(VM_HKH_PUB_RESRW, sizeof(HDLVDD),
                          (VMHOB)VDDOPENOWNER,NA,
                          SSToDS(&phdlvdd)) != NO_ERROR)
                Panic(("OpenVDD: Heap Overflow"));

            // Initialize handle field
            phdlvdd->hdlvdd_pvddproc=pvddep->vddep_vddp;

            // Grab the mutex semaphore
            if (KSEMRequestMutex (hksemHVDD, -1) != NO_ERROR)
                Panic (("OpenVDD: KSEMRequestMutex"));

            // Put the handle at head of handle list
            phdlvdd->hdlvdd=phdlVddHead;
            phdlVddHead=phdlvdd;

            // Release Mutex
            KSEMReleaseMutex (hksemHVDD);
        }
    }

    // return Handle
    return phdlvdd;
}

#pragma code_seg()
#pragma code_seg("SWAP32_21","CODE")
/***LP  CloseVDD - Generic routine to Close VDD/VDD or VDD/OS2
 *                    Communication.
 *      ENTRY
 *          hvdd - VDD handle
 *
 *      EXIT
 *          SUCCESS
 *              returns TRUE
 *          FAILURE
 *              returns FALSE
 */

BOOL PRIVENTRY CloseVDD(hvdd)
HVDD hvdd;
{
PHDLVDD phdlvdd,phdlvddCur,phdlvddPrev;

    // Grab the mutex semaphore
    if (KSEMRequestMutex (hksemHVDD, -1) != NO_ERROR)
        Panic (("vdmmCloseVDD: KSEMRequestMutex"));

    phdlvddCur = phdlVddHead;
    phdlvddPrev = NULL;
    phdlvdd = (PHDLVDD)hvdd;

    // scan the handles list  for a match for hvdd
    while(phdlvddCur != NULL){
        if(phdlvddCur == phdlvdd){

            // Remove the entry from the list
            if(phdlvddPrev == NULL)
                phdlVddHead = phdlvddCur->hdlvdd;
            else
                phdlvddPrev->hdlvdd = phdlvddCur->hdlvdd;
            VMFreeKHB(VM_HKH_PUB_RESRW, phdlvddCur);
            KSEMReleaseMutex (hksemHVDD);
            return TRUE;
        }
        phdlvddPrev = phdlvddCur;
        phdlvddCur = phdlvddCur->hdlvdd;
    }
    KSEMReleaseMutex (hksemHVDD);
    return FALSE;
}


#pragma code_seg()
#pragma code_seg("SWAP32_20","CODE")
/***EP+ VDHRequestVDD - Issue VDD request to another VDD.
 *
 *      Request an operation of a VDD.
 *
 *      This function is intended for communication between two or more
 *      VDDs where dynamic linkage between them is not appropriate.
 *
 *      Every VDHReq proc registered under the VDD name associated with the
 *      given handle is called until one returns TRUE.  There is no
 *      guaranteed order to the calling sequence.
 *
 *      If the calling VDD is registered under the same name as the
 *      VDD handle, then its entry point will also be called.  Furthermore,
 *      the VDM Manager does nothing to prevent any of the called VDDs from
 *      issuing another VDHRequestVDD.
 *
 *      ENTRY
 *          hvdd - VDD handle
 *          hvdm - VDM handle
 *          ulCmd - Command
 *          pReqIn - Input Packet
 *          pReqOut - Output Packet
 *
 *      EXIT-SUCCESS
 *          returns TRUE
 *
 *      EXIT-FAILURE
 *          returns FALSE
 *          VDHGetError may return:
 *              ERROR_NOT_READY - no VDD handled request
 *          System Halt.
 *              If invalid VDD handle.
 *              OR If invalid HVDM.
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 *
 *      NOTE
 *          The VDD worker routine should set the error with VDHSetError
 *          when it returns FALSE.
 *
 *      CONTEXT
 *          Task
 *          Interrupt
 */

BOOL VDHENTRY VDHRequestVDD(hvdd,hvdm,ulcmd,pReqIn,pReqOut)
HVDD    hvdd;
HVDM    hvdm;
ULONG   ulcmd;
PVOID   pReqIn;
PVOID   pReqOut;
/*end*/
{

    //validate parameters
    CHECKHVDM(hvdm);

    return(RequestVDD(hvdd, hvdm, ulcmd, pReqIn, pReqOut));
}

/***LP RequestVDD - worker for VDHRequestVDD
 *
 *
 *      ENTRY
 *          hvdd - VDD handle
 *          ulCmd - Command
 *          pReqIn - Input Packet
 *          pReqOut - Output Packet
 *
 *      EXIT-SUCCESS
 *          returns TRUE
 *
 *      EXIT-FAILURE
 *          returns FALSE
 *          VDHGetError may return:
 *              ERROR_NOT_READY - no VDD handled request
 *          System Halt.
 *              If invalid VDD handle.
 *              OR If invalid HVDM.
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 *
 *      NOTE
 *         See VDHRequestVDD.  This routine performs the work, without
 *         checking hvdm for validity (used by initGetVDMInterruptHandler
 *         in vdmm.c)
 *
 *      CONTEXT
 *          Task
 *          Interrupt
 *          System Initialization
 */

BOOL PRIVENTRY RequestVDD(hvdd,hvdm,ulcmd,pReqIn,pReqOut)
HVDD    hvdd;
HVDM    hvdm;
ULONG   ulcmd;
PVOID   pReqIn;
PVOID   pReqOut;
{
LONG          rc;
PHDLVDD       phdlvdd;
PVDDPROC      pvddproc;


#ifdef MMIOPH
    MMPHPreVDHRequestVDD()
#endif

   // Grab the mutex semaphore
    if (KSEMRequestMutex (hksemHVDD, -1) != NO_ERROR)
        Panic (("VDHRequestVDD: KSEMRequestMutex"));

    // Search in VDD handle list a matching handle
    phdlvdd=phdlVddHead;
    while(phdlvdd != NULL && phdlvdd != (PHDLVDD)hvdd)
        phdlvdd=phdlvdd->hdlvdd;

    // Did Handle match ?
    if(phdlvdd != NULL){

        // Get the entry points
        pvddproc=phdlvdd->hdlvdd_pvddproc;
        KSEMReleaseMutex (hksemHVDD);

        VDHSetError(0);     // set no error

        // Keep calling entry points on the list till one returns !VDDREQ_PASS
        while(pvddproc != NULL){
          if(*pvddproc->vddproc_pfnvdd){
            rc=(*pvddproc->vddproc_pfnvdd)(hvdm,ulcmd,pReqIn,pReqOut);
            if (rc != VDDREQ_PASS){
#ifdef MMIOPH
               MMPHPostVDHRequestVDD()
#endif
               return rc;
            }
          }
          pvddproc=pvddproc->pvddproc;
        }

        VDHSetError(ERROR_NOT_READY);
#ifdef MMIOPH
        MMPHPostVDHRequestVDD()
#endif
        return FALSE;
    }

    // No such handle found
    Panic(("VDHRequestVDD: Invalid Parameter"));
#ifdef MMIOPH
    MMPHPostVDHRequestVDD()
#endif
}

#pragma code_seg()
#pragma code_seg("SWAP32_25","CODE")

/***LP+ vdmmSetTitle - Process Title change in a VDM.
 *
 *  VDD(s) are called to notify the change in VDM's title.
 *  The VDD changing the VDM's title is suppose to keep the
 *  default title of a VDM. VDM Manager does'nt know anything
 *  about it.
 *
 *  ENTRY
 *      pszTitle - New VDM Title
 *
 *  EXIT
 *      None
 *
 *  CONTEXT
 *      VDM Task Time
 *
 *  PCODE
 *      call VDD for this new title
 *      if a VDD was successful in putting this title
 *          store title
 *      return
 */
VOID    vdmmSetTitle (pszTitle)
PSZ     pszTitle;
{
ULONG     i;

        if (!(vdmid_flVDM & VDM_TITLECHANGE))
            return;

        // Get base title name
        pszTitle = vdmmGetFileFromPath(pszTitle,strlen(pszTitle)+1);

        // Call VDDs and store away this title if a VDD putsup the title.
        if (vdmmTitleEvent(pszTitle)){
            if (pszTitle)
                for(i=0; i < 13 && (vdmid_achTitle[i] = pszTitle[i]); i++);
            else
                vdmid_achTitle [0] = NULL;
        }
        return;
}

#pragma code_seg()
#pragma code_seg("SWAP32_26","CODE")
/***LP+ vdmmGetTitle - Get Old VDM Title
 *
 *  ENTRY
 *      pszTitle - Place where title will be returned
 *
 *  EXIT
 *      None
 *
 *  CONTEXT
 *      VDM Task Time
 *
 *  PCODE
 *      Retrieve title
 */
VOID    vdmmGetTitle (pszTitle)
PSZ     pszTitle;
{
ULONG     i;

        if (!(vdmid_flVDM & VDM_TITLECHANGE))
            return;

        for(i=0; i < 13 && (pszTitle[i] = vdmid_achTitle[i]); i++);
        return;
}

#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE

/***LP  vdmmTitleEvent - Call VDDs for Title change event
 *
 *      ENTRY
 *          pszTitle - New title
 *
 *      EXIT
 *          TRUE/FALSE
 *
 *      CONTEXT
 *          VDM Task Time
 *
 *      NOTE
 *          The VDD responsiblef for putting the title should only
 *          return TRUE.
 */

BOOL    PRIVENTRY vdmmTitleEvent (pszTitle)
PSZ     pszTitle;
{
PEL     pel;
BOOL    fSuccess = FALSE;

        pel = aeh[VDM_TITLE_CHANGE].eh_pelFirst;
        while(pel != NULL) {
            if((*pel->el_pfn)(pszTitle) == TRUE)
                fSuccess = TRUE;
            pel = pel->el_pel;
        }
        return fSuccess;
}


/***EP+ VDHRegisterPerfCtrs - Register a counter block with Perfview
 *
 *      Register a counter block with Perfview for collection.
 *
 *      ENTRY
 *          pbDataBlk -> Data Block (DBH)
 *          pbTextBlk -> Text Block (TBH)
 *          flFlags   =  RPC flags (see pvwxport.h):
 *                                      (RPC_FL_32BIT | RPC_FL_VDD)
 *
 *      EXIT-SUCCESS
 *          returns TRUE.
 *
 *      EXIT-FAILURE
 *          returns FALSE
 *              ERROR_CALL_NOT_IMPLEMENTED (if not PERFSTRICT).
 *              ERROR_INVALID_PARAMETER
 *              ERROR_PVW_INVALID_COUNTER_BLK
 *              ERROR_PVW_INVALID_TEXT_BLK
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 *
 *      CONTEXT
 *          VDM Task Time
 *
 *      PCODE
 *          call PVWRegisterPerfCtrs
 *          translate error return value
 */

BOOL VDHENTRY VDHRegisterPerfCtrs(PBYTE pbDataBlk,
                                  PBYTE pbTextBlk,
                                  ULONG flFlags)
/*end*/
{
//57942#ifdef PERFSTRICT

#ifdef PERFVIEW

    ULONG rc;

    if (rc = PVWRegisterPerfCtrs(pbDataBlk,
                                 pbTextBlk,
                                 (flFlags | RPC_FL_32BIT | RPC_FL_VDD)))
    {
            VDHSetError(rc);
    }

#endif

    return (0);

//57942#else  /* PERFSTRICT */

//57942    VDHSetError(ERROR_CALL_NOT_IMPLEMENTED);
//57942    return FALSE;

//57942#endif /* PERFSTRICT */
}

#define DP_SINGLE_FOUND   1
#define DP_RANGE_FOUND    2
#define DP_END_FOUND      4

/***EP+ VDHDecodeProperty - This service will decode the property
 *                          string of following specified format.
 *
 *      String Syntax = "N1-N2,N3-N4,N5,N6-N7,... " where N? are
 *                      valid numbers in specified base.
 *
 *      ENTRY
 *          ppszProp  - pointer to pointer to property String to be decoded
 *          pulStart  - Place where starting number will be returned
 *          pulEnd    - Place where Ending number will be returned
 *          iBase     - VDH_DP_DECIMAL
 *                      VDH_DP_HEX.
 *      EXIT
 *          SUCCESS - returns TRUE
 *                    ppsz pointer is modified to point to next range
 *                    in the property string. *pulStart and *pulEnd will
 *                    have the range values. *ppsz will be returned NULL
 *                    after processing the last range.
 *          FAILURE - returns FALSE (Invalid Parameter)
 *                    System Panic if iBase is invalid or ppszProp is NULL
 *
 *      CONTEXT
 *          Task
 *
 *      VDM TERMINATION IMPLICATION
 *          None
 *
 *      NOTES
 *          1. *pulEnd will be -1 if a single Value is found.
 *
 *          2. All the numbers will be decoded in the specified
 *             base. Negative sign should only be used as range
 *             delimeter.
 *
 *          3. Numbers cannot have more than 8 digits. Failure will
 *             be returned if so.
 *
 *          4. If this service returns failure, it is still possible that
 *             the contents of pulStart or pulEnd may have changed.
 *
 *          5. As the pointers passed are not from user but from VDDs,
 *             there is no verification of addresses. Bad pointers
 *             will result in page faults.
 */

BOOL VDHENTRY VDHDecodeProperty(ppszProp,pulStart,pulEnd,iBase)
PPSZ    ppszProp;
PULONG  pulStart;
PULONG  pulEnd;
ULONG   iBase;
{

CHAR    ach1[10],ach2[10];
PSZ     psz = *ppszProp;
ULONG   rc;

#ifdef VDHSTRICT
    // Validate calling context
    ASSERTTASKONLY;

#ifdef MMIOPH
    MMPHPreVDHDecodeProperty()
#endif

    // Validate Base
    if ((iBase != VDH_DP_DECIMAL && iBase != VDH_DP_HEX) || ppszProp == NULL){
        Panic(("VDHDecodeProperty: Invalid Parameter"));
#ifdef MMIOPH
        MMPHPostVDHDecodeProperty()
#endif
        return FALSE;
    }
#endif

    // if NULL string given, return error
    if (psz == NULL) {
#ifdef MMIOPH
        MMPHPostVDHDecodeProperty()
#endif
        return FALSE;
    }

    // Get the value(s) and return error if not valid
    psz = vdmmDecodeString (psz,SSToDS(ach1),SSToDS(ach2),SSToDS(&rc));
    if (!rc){
#ifdef MMIOPH
        MMPHPostVDHDecodeProperty()
#endif
        return FALSE;
    }

    // Get Start Value
    if((*pulStart = vdmmGetValueFromString (SSToDS(ach1),iBase)) == -1){
#ifdef MMIOPH
        MMPHPostVDHDecodeProperty()
#endif
        return FALSE;
    }

    // Check if second value was found
    if (rc & DP_RANGE_FOUND)
        *pulEnd = vdmmGetValueFromString (SSToDS(ach2),iBase);
    else
        *pulEnd = -1;

    // *ppszProp should be made NULL if end of string found
    if (rc & DP_END_FOUND)
        *ppszProp = NULL;
    else
        *ppszProp = psz;

#ifdef MMIOPH
    MMPHPostVDHDecodeProperty()
#endif
    return TRUE;
}

/***LP+ vdmmDecodeString - Decode the string
 *
 *      ENTRY
 *          pszSrc  - Source string
 *          pchDst1 - Start Value return place
 *          pchDst2 - End Value return place if found
 *          prc     - Return Code
 *                  DP_SINGLE_FOUND - Only one value found only pchDst1 filled
 *                  DP_RANGE_FOUND  - full range found, both dest. filled
 *                  DP_END_FOUND    - End of string found
 *
 *      EXIT
 *          SUCCESS
 *              returns updated string pointer and *prc != 0
 *
 *          FAILURE
 *              returns *prc = 0
 */

PSZ     PRIVENTRY vdmmDecodeString (pszSrc,pchDst1,pchDst2,prc)
PSZ     pszSrc;
PCHAR   pchDst1;
PCHAR   pchDst2;
PULONG  prc;
{
register CHAR    ch;
UCHAR    cDigits;
ULONG    i;

    // By default assume failure
    *prc = 0;

    // Process two values which form a range
    for(i=0 ; i < 2 ; i++) {

        cDigits = 0;

        // Eat all white space char in the begining
        while((ch = *pszSrc++) == ' ' || ch == '\t');

        // First Chararcter cannot be one of these.
        if (ch == ',' || ch == '-' || ch == NULL)
            return NULL;

        // Collect the value
        while (ch != ',' && ch != '-' && ch != NULL && cDigits < 8){
            if(i)
                pchDst2[cDigits++] = ch;
            else
                pchDst1[cDigits++] = ch;
            ch = *pszSrc++;

            // Discard all blank character if followed by comma,blank or NULL
            if (ch == ' ' || ch == '\t'){
                while((ch = *pszSrc++) == ' ' || ch == '\t');
                if (ch != ',' && ch != '-' && ch != NULL)
                    return NULL;
            }
        }

        // If longer than allowed return error
        if (cDigits >= 8)
            return NULL;                    // return FAILURE

        // End with NULL
        if (i)
            pchDst2[cDigits] = NULL;
        else
            pchDst1[cDigits] = NULL;

        if (ch == ','){
            if(!i){
                *prc = DP_SINGLE_FOUND;
                return pszSrc;              // return SUCCESS; One value only
            }
            else {
                *prc = DP_RANGE_FOUND;
                return pszSrc;              // return SUCCESS; Full range found
            }
        }

        if (ch == NULL){
            *prc = DP_END_FOUND;            // End of string found
            if (i)
                *prc |= DP_RANGE_FOUND;     // Full range found
            else
                *prc |= DP_SINGLE_FOUND;    // One value only
            return NULL;                    // return SUCCESS
        }

    }
    return NULL;                            // return FAILURE; NOTE *prc is 0
}

#pragma END_MVDM_SWAP_CODE
#pragma code_seg ("SWAP32_23","CODE")
/***LP+ vdmmGetValueFromString - Convert an asciiz string into integer
 *                               value.
 *
 *      ENTRY
 *          pszSrc  - Source string
 *          iBase   - Base Hex or Decimal
 *
 *      EXIT
 *          SUCCESS
 *              returns value
 *
 *          FAILURE
 *              returns -1
 */

ULONG   PRIVENTRY vdmmGetValueFromString (pszSrc,iBase)
PSZ     pszSrc;
ULONG   iBase;
{
ULONG   ulVal=0,i,expVal,ulSrcLen;
CHAR    ch;

    // Validate the string, convert to uppercase and return the length
    ulSrcLen = vdmmValidateString (pszSrc,iBase);
    if ((-1 == ulSrcLen) || (0 == ulSrcLen))                          /*BN001*/
        return -1;
    ulSrcLen--;
    expVal = 1;
    for (i = 0; i <= ulSrcLen ; i++){
        ch = pszSrc[ulSrcLen-i];
        if(ch >= 'A')
            ch -= ('A' - 10);
        else
            ch -= '0';
        ulVal += ch * expVal;
        expVal *= iBase;
    }
    return ulVal;
}

ULONG   PRIVENTRY vdmmValidateString (pszSrc,iBase)
PSZ     pszSrc;
ULONG   iBase;
{
CHAR  ch;
ULONG ulLen = 0;

    while (ch = *pszSrc++){
        ulLen++;
        if (iBase == 10){
            if (ch < '0' || ch > '9')
                return -1;
        }
        else {
            if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F'))
                continue;
            else {
                if (ch >= 'a' && ch <= 'f')
                    *(pszSrc-1) = (CHAR)(ch - 'a' + 'A');
                else
                    return -1;
            }
        }
    }
    return ulLen;
}

#pragma code_seg()
#pragma code_seg("SWAP32_17","CODE")
/***LP+ vdhLockEntry - Create/Remove a lock entry
 *
 *  Creates a lock entry and adds it to the linked list of lock entries,
 *  or removes the lock entry from the list.
 *
 *  The caller is responsible for freeing up the memory of the removed
 *  lock entry. ie VMFreeKHB(VM_HKH_PUB_SWAPRW, ple) This is to allow
 *  the caller to call VMUnlock to unlock memory before freeing up
 *  the entry.
 *
 *  ENTRY
 *      ple - lock entry if releasing else NULL
 *  EXIT
 *      SUCCESS - return lock entry
 *      FAILURE - return NULL
 */

PLOCKENTRY PRIVENTRY vdhLockEntry(ple)
PLOCKENTRY  ple;
/*end*/
{
PLOCKENTRY  plockentry,plockentrycur,plockentryprev;
    if (ple == NULL)
    {
      // Create lock entry

      // Allocate handle
      if(VMAllocKHB(VM_HKH_PUB_SWAPRW, sizeof(LOCKENTRY),
                    (VMHOB)VDMALIASOWNER,NA,
                    SSToDS(&plockentry)) != NO_ERROR)
          Panic(("vdhLockEntry: Heap Overflow"));

      // Link in the new lock entry

      // Grab the mutex semaphore, this semaphore protects the linked list
      // from updates by other threads in case this one blocks.
      if (KSEMRequestMutex (hksemHLock, -1) != NO_ERROR)
          Panic (("vdhLockEntry: KSEMRequestMutex"));

      // Is the list empty? If it is then set up first and last ptr
      if (plockentryhead->phle_leFirst==NULL)
      {
        plockentryhead->phle_leFirst=plockentry;
        plockentryhead->phle_leLast=plockentry;
      }
      // List is not empty
      else
      {
        plockentryhead->phle_leLast->pnextlockentry=plockentry;
        plockentryhead->phle_leLast=plockentry;
      }
      plockentry->pnextlockentry=NULL;

      // Release Mutex
      KSEMReleaseMutex (hksemHLock);

      return plockentry;
    }
    else
    {
      // Remove lock entry

      // Search for matching lock entry
      plockentryprev=NULL;
      plockentry=ple;

      // Grab the mutex semaphore, this semaphore protects the linked list
      // from updates by other threads in case this one blocks.
      if (KSEMRequestMutex (hksemHLock, -1) != NO_ERROR)
          Panic (("vdhLockEntry: KSEMRequestMutex"));

      plockentrycur=plockentryhead->phle_leFirst;
      while(plockentrycur != NULL){
          if(plockentrycur == plockentry)
          {

              // Remove the entry from the list
              // Was the match the first entry in the list?
              if(plockentryprev == NULL)
              {
                plockentryhead->phle_leFirst = plockentrycur->pnextlockentry;
              }
              else
              {
                plockentryprev->pnextlockentry = plockentrycur->pnextlockentry;
              }
              // Is the match the last entry in the list?
              if (plockentrycur == plockentryhead->phle_leLast)
              {
                plockentryhead->phle_leLast = plockentryprev;
              }
              // Release Mutex
              KSEMReleaseMutex (hksemHLock);
              ple = plockentrycur;
              return ple;
          }
          plockentryprev = plockentrycur;
          plockentrycur = plockentrycur->pnextlockentry;
      }
      KSEMReleaseMutex (hksemHLock);
      return NULL;
    }

}
#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE
/***EP+ VDHCreateThread - Create a Thread for VDM
 *
 *      This VDH service creates a thread for the current VDM.  This function
 *      is currently used to improve multimedia support.
 *
 *      ENTRY
 *          pfnThread = pointer to function to run for thread
 *
 *      EXIT-SUCCESS
 *          returns TRUE
 *      EXIT-FAILURE
 *          returns FALSE
 *
 *      CONTEXT
 *          VDM Task
 *
 *      PSEUDOCODE
 *          call the worker routine TKCreateVDMThread
 *          if (error)
 *              return FALSE;
 *          else
 *              return TRUE;
 */

BOOL VDHENTRY VDHCreateThread(PVDDTID pThreadID,PVDDTHREAD pfnThread)
/*end*/
{
    int rc;

#ifdef MMIOPH
    MMPHPreVDHCreateThread()
#endif

    if (rc = TKCreateVDMThread((PUSHORT)pThreadID,pfnThread)) {
      VDHSetError((ULONG)rc);
#ifdef MMIOPH
      MMPHPostVDHCreateThread()
#endif
      return FALSE;
      }
    else {
#ifdef MMIOPH
      MMPHPostVDHCreateThread()
#endif
      return TRUE;
    }
}   /* VDHCreateThread */

/***EP+ VDHExitThread - Exit Thread for VDM
 *
 *      This VDH service destroys a thread for the current VDM.  This function
 *      is currently used to improve multimedia support.
 *
 *      ENTRY
 *          ulExitCode = value to return to parent
 *
 *      EXIT
 *          VOID
 *
 *      CONTEXT
 *          Task
 *
 *      PSEUDOCODE
 *          call the worker routine TKExit
 */

VOID VDHENTRY VDHExitThread(ULONG ulExitCode)
/*end*/
{
#ifdef MMIOPH
    MMPHPreVDHExitThread()
#endif
    TKExit(FALSE, ulExitCode);
#ifdef MMIOPH
    MMPHPostVDHExitThread()
#endif
}       /* VDHExitThread */

#pragma END_MVDM_SWAP_CODE

#pragma BEGIN_MVDM_GLOBAL_CODE


/***EP+ VDHGetError - Get error code
 *
 *      Returns the error code from the last VDH service called.
 *
 *      The return value is guaranteed to be valid as long as the VDD does
 *      not block or yield between the VDH service that failed and the
 *      call to VDHGetError.
 *
 *      VDHGetError/VDHSetError work at hardware interrupt time.  Each level
 *      of interrupt nesting (where task time is level 0) has a separate
 *      error code variable.
 *
 *      ENTRY
 *          None
 *
 *      EXIT-SUCCESS
 *          returns non-zero error code
 *
 *      EXIT-FAILURE
 *          returns 0, last VDH call did not have an error.
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 *
 *      NOTES
 *          This service returns a meaningful error code, only if called
 *          after a VDH service which failed. In brief it returns the
 *          error code of last failed VDH service call.
 *
 *      CONTEXT
 *          Any
 *
 *      PCODE
 *          return the error code
 */

ULONG VDHENTRY VDHGetError()
/*end*/
{

#ifdef MMIOPH
    MMPHPreVDHGetError()
#endif

    if(InterruptLevel >= (MAX_INT_NEST-1))
        Panic(("VDHGetError:Interrupt Nesting Overflow"));
#ifdef MMIOPH
    MMPHPostVDHGetError()
#endif
    return(aErrorCode[InterruptLevel+1]);
}

/***EP+ VDHSetError - Set error code
 *
 *      Set error code for return by VDHGetError.
 *
 *      This call is intended for use by any VDD which offers dynlink
 *      services to other VDDs.
 *
 *      ENTRY
 *          ulError
 *
 *      EXIT-SUCCESS
 *          None.
 *
 *      EXIT-FAILURE
 *          None.
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 *
 *      CONTEXT
 *          Any
 *
 *      PCODE
 *          set the error code
 */

VOID VDHENTRY VDHSetError(ulErr)
ULONG   ulErr;
/*end*/
{

#ifdef MMIOPH
    MMPHPreVDHSetError()
#endif

    if(InterruptLevel >= (MAX_INT_NEST-1))
        Panic(("VDHSetError:Interrupt Nesting Overflow"));
    aErrorCode[InterruptLevel+1] = ulErr;
#ifdef MMIOPH
    MMPHPostVDHSetError()
#endif
    return;
}

/***EP+ VDHHandleFromSGID - Get VDM handle from screen group id
 *
 *      Determine the VDM handle given the screen group id.
 *
 *      ENTRY
 *          SGid - Screen group id
 *
 *      EXIT-SUCCESS
 *          returns non-zero hVDM
 *
 *      EXIT-FAILURE
 *          returns 0
 *          VDHGetError may return:
 *              ERROR_SMG_INVALID_SGID
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 *
 *      CONTEXT
 *          Task
 *          Interrupt
 *
 *
 *      PCODE
 *          Scan MAPID for this sreen group
 *          if not found return FAILURE
 *          return hVDM
 */
HVDM VDHENTRY VDHHandleFromSGID(SGid)
SGID          SGid;
/*end*/
{
HVDM          hvdm;

#ifdef MMIOPH
     MMPHPreVDHHandleFromSGID()
#endif
     hvdm=GetVDMHandle((USHORT)SGid,0,TRUE);
     if(hvdm == NULL){
         VDHSetError(ERROR_SMG_INVALID_SGID);
#ifdef MMIOPH
         MMPHPostVDHHandleFromSGID()
#endif
         return((HVDM)0);
     }

#ifdef MMIOPH
    MMPHPostVDHHandleFromSGID()
#endif
     return(hvdm);
}

/***EP+ VDHEnumerateVDMs - For each VDM run a worker function
 *
 *      For each VDM in the system run a worker function.  The worker
 *      function can stop the enumeration by returning FALSE.
 *
 *      ENTRY
 *          pfnWorker - worker function
 *              BOOL pfnWorker
 *                  ENTRY
 *                      hVDM - handle to VDM
 *                      ulData - function-specific data
 *
 *                  EXIT-SUCCESS
 *                      return TRUE, enumeration continues
 *
 *                  EXIT-FAILURE
 *                      return FALSE, stop enumeration
 *
 *          ulData - function-specific data to be passed on each call
 *
 *      EXIT-SUCCESS
 *          returns TRUE
 *
 *      EXIT-FAILURE
 *          returns FALSE, worker function stopped enumeration
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 *
 *      CONTEXT
 *          Task
 *          Interrupt
 *
 *      PCODE
 *          run lpfnWorker for each VDM until there are no more VDMs
 *          or the worker function returns FALSE
 *          return TRUE if all workers returned TRUE, else return FALSE
 */
BOOL VDHENTRY VDHEnumerateVDMs(penmWorker,ulData)
PENUMHOOK     penmWorker;
ULONG         ulData;
/*end*/
{
PMAPID      pmi;

#ifdef MMIOPH
    MMPHPreVDHEnumerateVDMs()
#endif

    pmi=hmapid.hm_pmiFirst;
    while(pmi != NULL){
        // If VDM is being created or terminating then skip this VDM
        if(!pmi->mi_fState){
            if(((*penmWorker)(pmi->mi_hvdm,ulData)) == FALSE) {
#ifdef MMIOPH
    MMPHPostVDHEnumerateVDMs()
#endif
                return FALSE;
            }
        }
        pmi=pmi->mi_pmi;
    }
#ifdef MMIOPH
    MMPHPostVDHEnumerateVDMs()
#endif
    return TRUE;
}

/***EP+ VDHQueryLin - Query linear address of a far16 address
 *
 *      Convert a 16:16 address to a 0:32 address. The 16:16 address
 *      can be a LDT or GDT based address. It is callable on stack based
 *      addresses also.
 *
 *      ENTRY
 *          f16p - 16:16 address
 *
 *      EXIT-SUCCESS
 *          returns 0:32 address
 *
 *      EXIT-FAILURE
 *          System Halt.
 *              If f16p is having an invalid selector.
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 *
 *      CONTEXT
 *          Init
 *          Task
 *          Interrupt
 *
 *      NOTE
 *          This service does a minimal checking on f16p address.
 *          The caller should make sure that f16p is not invalid.
 *          In case an invalid address is passed this service may
 *          return an invalid address.
 *
 *      PCODE
 *          Call SelVirtToLin
 */

PVOID VDHENTRY VDHQueryLin(f16p)
F16PVOID          f16p;
{
PVOID   laddr;

#ifdef MMIOPH
    MMPHPreVDHQueryLin()
#endif

    laddr = (PVOID)SELVirtToLin((ULONG)OFFSETOF16(f16p),(SEL)SEGMENTOF16(f16p));
    if((ULONG)laddr != -1) {
#ifdef MMIOPH
        MMPHPostVDHQueryLin()
#endif
        return laddr;
      }
    else
        Panic(("VDHQueryLin:Invalid Parameter"));
#ifdef MMIOPH
    MMPHPostVDHQueryLin()
#endif
}

/***EP+ VDHQuerySysValue - Query a system value
 *
 *      Query a system value.
 *
 *      Systems values are of two classes, global and per-VDM.  These
 *      are distinguished by the prefix of the index name.
 *
 *      ENTRY
 *          hvdm - VDM handle
 *                 0 => current VDM
 *          iIndex - index of the system variable
 *
 *      EXIT-SUCCESS
 *          returns value.
 *              If the value is 0, and VDHGetError returns FALSE,
 *              then 0 was indeed the correct value.
 *
 *      EXIT-FAILURE
 *          System Halt.
 *              If hvdm is invalid, or if iIndex is invalid.
 *
 *      VDM TERMINATION IMPLICATIONS
 *          None.
 *
 *      CONTEXT
 *          Any
 *
 *      NOTE
 *          In case of VDHLSV_PID, a value of 0 for hvdm will mean
 *          current process rather than current VDM. So VDDs can
 *          query the PID of current process whether it is a VDM or
 *          protect mode.
 */

ULONG VDHENTRY VDHQuerySysValue(hvdm,iIndex)
HVDM           hvdm;
ULONG          iIndex;
/*end*/
{
PULONG   pul;
ULONG    ulMask;
PLSYSVAL plsv;

#ifdef MMIOPH
    MMPHPreVDHQuerySysValue()
#endif

#ifdef VDHSTRICT
    if (iIndex > GSVMAX && iIndex < LSVSTART || iIndex > LSVMAX) {
        Panic(("VDHQuerySysValue: Invalid Parameter"));
#ifdef MMIOPH
        MMPHPostVDHQuerySysValue()
#endif
        return 0;
    }
#endif
    if (iIndex >= LSVSTART) {   // Querying Local value
        if (hvdm == CURRENT_VDM && iIndex == VDHLSV_PID)
            return pPTDACur->ptda_infoseg.LIS_CurProcID;
        CHECKHVDM(hvdm);
        plsv = pVDM(hvdm,PLSYSVAL,&vdmid_aLSV[iIndex - LSVSTART]);
        vdmmAssertNEQ(plsv->lsv_fInstance,LSV_UNUSED);
        if(plsv->lsv_fInstance == LSV_INSTANCE)
            pul = (PULONG)((ULONG)plsv->lsv_pv + (ULONG)(hvdm));
        else
            pul = (PULONG)plsv->lsv_pv;
        ulMask = plsv->lsv_ulMask;
    }
    else {
        pul = (PULONG)aGSV[iIndex].gsv_pv;
        ulMask = aGSV[iIndex].gsv_ulMask;
    }
    if (pul == NULL)
        Panic(("VDHQuerySysValue: Internal Error"));
#ifdef MMIOPH
    MMPHPostVDHQuerySysValue()
#endif
    return(*pul & ulMask);
}

/***LP  GetVDMHandle - Return VDM handle for given PID/SGID
 *
 *      Returns the VDM handle for a given PID/SGID.
 *
 *      ENTRY
 *              id - PID/SGID to be searched for
 *              fbType - 0 for SGID and 1 for PID
 *              fCheck - do we have to check for create/terminate time
 *      EXIT
 *              SUCCESS
 *                  returns hvdm
 *              FAILURE
 *                  returns NULL
 *
 *      EXIT
 *              SUCCESS
 *                  return hvdm
 *              FAILURE
 *                      returns 0
 *                      ERROR_INVALID_PROCID
 *
 *      CONTEXT
 *          Task
 *
 *      PCODE
 *              Scan MAPID for this id
 *              return NULL if pid not found
 *              else return hvdm
 */

HVDM  PRIVENTRY GetVDMHandle(id,fbType,fCheck)
USHORT          id;
BOOL            fbType;
BOOL            fCheck;
/*end*/
{
PMAPID        pmi;

    pmi=hmapid.hm_pmiFirst;
    while(pmi != NULL){
        // If entry found with matching PID or SGID then break
        if ((fbType && pmi->mi_Pid == id) ||
                (!fbType && pmi->mi_SGid == id))
            break;
        else
            pmi=pmi->mi_pmi;
    }

    // If no check required return handle
    if (!fCheck && pmi)
        return(pmi->mi_hvdm);

    // If VDM is being created or terminating then fail
    if (pmi && !pmi->mi_fState)
        return(pmi->mi_hvdm);
    else
        return(NULL);
}


/***EP+ VDHDevBeep - Device Beep VDH Service
 *
 *      This VDH service performs the preempt beep request on behalf of
 *      the requesting VDD.
 *
 *      ENTRY
 *          ulFreq = beep frequency in Hz
 *          ulDuration = beep duration in msec
 *
 *      EXIT-SUCCESS
 *          returns TRUE
 *      EXIT-FAILURE
 *          returns FALSE
 *
 *      CONTEXT
 *          Task
 *          Interrupt
 *
 *      PSEUDOCODE
 *          call the worker routine BeepPreempt;
 *          if (error)
 *              return FALSE;
 *          else
 *              return TRUE;
 */

BOOL VDHENTRY VDHDevBeep(ULONG ulFreq, ULONG ulDuration)
/*end*/
{
#ifdef MMIOPH
    MMPHPreVDHDevBeep()
#endif

    if (BeepPreempt(BEEP_TIMED, ulFreq, ulDuration)) {
#ifdef MMIOPH
    MMPHPostVDHDevBeep()
#endif
        return FALSE;
       }
#ifdef MMIOPH
    MMPHPostVDHDevBeep()
#endif
    return TRUE;
}       /* VDHDevBeep */


#pragma END_MVDM_GLOBAL_CODE
