/*static char *SCCSID = "@(#)sempmwkr.c 6.8 94/01/25";*/

#define SCCSID  "@(#)sempmwkr.c 6.8 94/01/25"

/*
 *      Semaphores - PM 32-bit Semaphore Routines
 *
 *      This is an OS/2 2.x specific file
 *      IBM Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 *      Created: Jan 1994 - Ron Cadima
 *
 *      2/16/94 - RAC Add in support for 16 Bit Semaphores. Look for PM16
 *      3/18/94 - RAC Update PMWAITEVENTSEM logic so that it will check
 *                to see if the event has already been posted. If so, we
 *                return a normal completion code.
 */

#define INCL_DOSP
#define OS2_NOPMAPI
#include <os2p.h>

#define INCL_TYPES
#include <basedef.h>
#define FNO_NUM FNO_SEMALLOC_C          /* for assertion macros */
#include <fnotask.h>                    /* MUST BE BEFORE osmac.h */
#include <osmac.h>
#include <osconst.h>
#include <ostypes.h>
#include <infoseg.h>
#include <mi.h>
#include <proc.h>
#include <error.h>
#include <sigdef.h>
#include <except.h>
#include <dossymc.h>
#include <seldesc.h>
#include <pgexport.h>
#include <vmexport.h>
#include <vmowner.h>
#include <tkexport.h>
#include <sem.h>
#include <screen.h>
#include <curdir.h>
#include <dirent.h>
#include <find.h>
#include <semxport.h>
#include <tkptda.h>
#include <bsetib.h>
#include <tklocal.h>
#include <semlocal.h>
#include <string.h>
#include <kmode.h>
#include <sched.h>

extern ULONG SEMRequestOwnedMutexWkr(HMTX,ULONG);



/***EP  pmSemIN - Check validity of PM Shared Event Sempahore and pass
 *      back the address of the pm semaphore table entry.
 *
 *      ENTRY   pmhem - PM Shared Event Semaphore handle
 *
 *      EXIT    Success - PSEM - pointer to pm sem table entry
 *              Error   - ERROR_INVALID_HANDLE
 *
 *      PSEUDO CODE
 *
 *      Validate that handle is for shared semaphore and good.
 *      get index value from shared semaphore table entry.
 *      insure index value is good.
 *      pass back address of pm semaphore table entry
 */

PSEM pmSemIN(HEV pmhev)   /* Shared event handle */
{

  ULONG ulIndex;            /* Index value */

    /*
     *  Check that the handle is for a shared semphore.
     */
    if (semIfShared(pmhev)) {

      ulIndex = semGetIndex(pmhev);        /* Get index into shared semaphore */
                                           /* pointer table.  */

      /*
       *  Insure that the index vaule is within the table limits and that
       *  the address of the semaphore control block is not 0.
       */
      if ((ulIndex < ulShTblSize) && ((PSHUN)pShSemTbl[ulIndex] != NULL) )

         return((PSEM)pShSemTbl[ulIndex]);     /* Return address of semaphore */
                                               /* control block to caller. */

    }

    /*
     *  Invalid Event Semaphore. Return Error
     */
    return((PSEM)ERROR_INVALID_HANDLE);

 }



/***EP  semPMPostEventSem - Post an PM Event Semaphore
 *
 *      SEMPMPostEventSem is the worker routine for the VR32PMPostEventSem
 *      API routine.  It call's pmSemIn to validate that the pm semaphore
 *      handle is valid and to obtain the address of the semaphore control
 *      block. It then checks the flags in the tcb, whose address was saved
 *      in the semaphore control block during the wait, to see if the thread
 *      is still sleeping.  If it is, we will set the TCB SLEEP Flags to
 *      indicate that PM has posted the thread to wake it up, and call the
 *      TKWakeThread function in TKSLEEP.C.
 *
 *      If the thread is not sleeping, or there is no TCB address, then we
 *      will return an Invalid handle return code.
 *
 *      ENTRY   hev   - handle of event to post
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *
 *      PSEUDO CODE
 *
 *      validate handle
 *      return ( TKWakeThread(ptcb))
 */

ULONG semPMPostEventSem(HEV pmhev)           /* PM event handle */

{
  PSEM     pEvt;                /* Semaphore Control block address */
  register PTCB ptcb;           /* Address of TCB associated with semaphore */
  ULONG    rc = NO_ERROR;       /* return code save area. */

    TKBeginNoBlock();           /* Section May NOT Block */

    /*
     *  Check validity of PM Shared Event Semaphore and get address of
     *  semaphores control block.
     */
    if ((ULONG)(pEvt = pmSemIN(semStripSigBits(pmhev))) == ERROR_INVALID_HANDLE ) {
       TKEndNoBlock();
       return(ERROR_INVALID_HANDLE);
    }

/********************** Update for PM Event Semaphore Wait ********************/
/*  Modify logic to check if the TCB associated with the semaphore is present */
/*  If it is, then we want to set the flag in the TCB stating that the PM     */
/*  event has been posted. Next check to see if the TCB is already blocked,   */
/*  when it is, go wake up the thread. If it isn't blocked, or no TCB address */
/*  is present, then we just return to the caller.                            */
/******************************************************************************/

    /*
     * Check to see if event is already posted. If so, then check if
     * too many posts have already been done.
     */
    if (((PSHUN)pEvt)->SEvt.usFlags & DE_POSTED) {

      if (((PSHUN)pEvt)->SEvt.usPostCt == SEM_MAXPOSTS) {
        TKEndNoBlock();
        return (ERROR_TOO_MANY_POSTS);
      }

      rc = ERROR_ALREADY_POSTED;  /* Set already posted return code. */

    } /* endif for DE_POSTED check */

    else { /* Set event posted flag in semaphore control block. */

         ((PSHUN)pEvt)->SEvt.usFlags |= DE_POSTED;
    }

    ((PSHUN)pEvt)->SEvt.usPostCt++;    /* Incrament semaphore post count */

    /*
     * Get ptcb address.
     */
    ptcb = ((PSHUN)pEvt)->SEvt.ptcb;

    /*
     * Check to see if the TCB address is zero. If it is not, then set the
     * TCBWakeFlags to indicate that the event has been posted by PM
     */
    if ( ptcb != 0 ) {

       /* Set TCB wake flags to PM Posted. */
       ptcb->TCBWakeFlags |= TK_WF_PMPOSTED;

       ((PSHUN)pEvt)->SEvt.ptcb = 0;     /* Set ptcb in sem table to 0 */

       /* If the thread is sleeping and blocked, and we have a PM */
       /* semaphore already waiting on the thread, then go wakeup */
       /* the thread.                                             */

       if  ( (ptcb->TCBWakeFlags & TK_WF_SLEEPING) &&
             (ptcb->TCBSpareFlags & SPFLAGS_PM_EVENT) &&
             (ptcb->TCBQState == STATE_BLOCKED) )   {

          /*
           *  Call THWakeThread to wake up the thread.
           */
          TKWakeThread(ptcb);

       } /* endif for thread sleeping check */

    } /* endif for ptcb != 0 */

    TKEndNoBlock();

    return(rc);

}



/***EP  VR32PMPostEventSem - Post a PM Event Semaphore
 *
 *      This is the API entry point which PMWIN calls to wake up threads
 *      that are waiting on a semaphore without actually posting the user's
 *      semaphore.
 *
 *      ENTRY   hev   - handle of event to post
 *
 *      EXIT    Success - NO_ERROR
 *                        ERROR_ALREADY_POSTED
 *              Failure - ERROR_INVALID_HANDLE
 *
 *      PSEUDO CODE
 *
 *      return ( SEMPMPostEventSem(pmhev) )
 */

ULONG cdecl VR32PMPostEventSem(THUNKSEF sef,
                         HEV pmhev)           /* PM event handle */

{
    return(semPMPostEventSem(pmhev | SEM_PMSEMCALL));    //98809
}


/***EP  VR32PMWaitEventSem - Wait on PM Event Semaphore
 *
 *      SEMPMWaitEventSem accepts a PM System Shared event semaphore and
 *      a user event semaphore.  We validate that the semaphore are valid.
 *      The call pmSemSet above to set up our flags fields and save the
 *      the avtive tcb address in the semaphore control block. If there is
 *      no error, then we call the normal semaphore worker routine.
 *      SEMWaitEventSem, and pass the return code back to PMWIN.
 *
 *      ENTRY   pmhev     - handle of PM event
 *              hev       - handle of user event to wait upon for posting
 *              ulTimeout - timeout in milliseconds
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INTERRUPT
 *                        ERROR_INVALID_HANDLE
 *                        PM_EVENT_POSTED
 *
 *      PSEUDO CODE
 *
 *      validate handle
 *      return ( SEMWaitEventSem(hev,ulTimeout) )
 */

ULONG cdecl VR32PMWaitEventSem(THUNKSEF sef,
                         HEV pmhev,          /* PM event handle */
                         HEV hev,            /* User event handle */
                         ULONG ulTimeout)    /* timeout in milliseconds */

{

  PSEM   pEvt;
  ULONG  rc;

    tkAssertTasktime();        /* call NOT allowed at interrupt time */

    /*
     *  Check validity of PM Shared Event Semaphore and get address of
     *  semaphores control block.
     */
    if ((ULONG)(pEvt = pmSemIN(semStripSigBits(pmhev))) == ERROR_INVALID_HANDLE ) {
        return(ERROR_INVALID_HANDLE);
    }

    /* If event is already posted, then return to caller */
    if (((PSHUN)pEvt)->SEvt.usFlags & DE_POSTED )
       return(PM_EVENT_POSTED);

    /*
     * If the user semaphore handle is not equal to the PM semaphore handle,
     * then go validate the user semaphore handle.
     */
    if ( hev != pmhev ) {

       /* Validate User semaphore handle. */   //98809 added SEM_PMSEMCALL
       if (semChkHdl(hev | SEM_PMSEMCALL,DCE_EVENT_SEM,pPTDACur) != NO_ERROR) {
           return (ERROR_INVALID_HANDLE);
       }
    }


    /* Set flags in the TCB to specify a PM Event Wait */
    pTCBCur->TCBSpareFlags |= SPFLAGS_PM_EVENT;

    /* Save current ptcb address in pm sem table entry. */
    ((PSHUN)pEvt)->SEvt.ptcb = pTCBCur;

    /*
     *  Call the normal semaphore worker layer to process the User semaphore.
     */
    rc = SEMWaitEventSem(hev | SEM_PMSEMCALL,ulTimeout);   //98809

    pEvt = (PSEM)pShSemTbl[semGetIndex(semStripSigBits(pmhev))];

    /* Insure that the PM_EVENT Waiting flag is reset. */
    pTCBCur->TCBSpareFlags &= ~SPFLAGS_PM_EVENT;

    _disable();
    ((PSHUN)pEvt)->SEvt.ptcb = 0;
    _enable();

    return(rc);

} /* VR32PMWaitEventSem */



/***EP  VR32PMRequestMutexSem - Request a Mutex Semaphore
 *
 *      VR32PMRequestMutexSem is the API that PMWIN calls to request a
 *      Mutex Semaphore.  We will only get here if the mutex semaphore
 *      is owned.  If it is not, the code in API32.ASM will have gotten
 *      the mutex semaphore and returned to PMWIN without ever calling this
 *      routine.  This routine will call the common function pmSemSet to
 *      validate the pm semaphore and set the propper flags and save the
 *      TCB address.  If no error is returned from this routine, it will
 *      pass control on to the SEMRequestOwnedMutex worker routine and pass
 *      the return code back to PMWIN.
 *
 *      ENTRY   pmhev     - handle of a PM Event Semaphore
 *              hmtx      - handle of a mutex
 *              ulTimeout - timeout in milliseconds
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INTERRUPT
 *                        ERROR_INVALID_HANDLE
 *                        ERROR_SEM_OWNER_DIED
 *                        ERROR_TIMEOUT
 *                        ERROR_TOO_MANY_SEM_REQUESTS
 *                        PM_EVENT_POSTED
 *
 *      PSEUDO CODE
 *
 *      validate handle
 *      return ( SEMRequestMutexSem(hmtx,ulTimeout) )
 */

ULONG cdecl VR32PMRequestMutexSem(THUNKSEF sef,
                            HEV pmhev,         /* pm event handle */
                            HMTX hmtx,         /* user mutex handle */
                            ULONG ulTimeout)   /* timeout in milliseconds */

{

  PSEM   pEvt;
  ULONG  rc;

    tkAssertTasktime();        /* call NOT allowed at interrupt time */

    /*
     *  Check validity of PM Shared Event Semaphore and get address of
     *  semaphores control block.
     */
    if ((ULONG)(pEvt = pmSemIN(semStripSigBits(pmhev))) == ERROR_INVALID_HANDLE ) {
        return(ERROR_INVALID_HANDLE);
    }

    /* If event is already posted, then return to caller */
    if (((PSHUN)pEvt)->SEvt.usFlags & DE_POSTED )
       return(PM_EVENT_POSTED);

    /* Set flags in the TCB to specify a PM Event Wait */
    pTCBCur->TCBSpareFlags |= SPFLAGS_PM_EVENT;

    /* Save current ptcb address in pm sem table entry. */
    ((PSHUN)pEvt)->SEvt.ptcb = pTCBCur;

    /*
     *  Call the worker layer.
     */
    rc = SEMRequestOwnedMutexWkr(hmtx, ulTimeout);

    /* Insure that the PM_EVENT Waiting flag is reset. */
    pTCBCur->TCBSpareFlags &= ~SPFLAGS_PM_EVENT;

    pEvt = (PSEM)pShSemTbl[semGetIndex(semStripSigBits(pmhev))];

    /* Set ptcb address in pm sem table entry to zeroe. */
    ((PSHUN)pEvt)->SEvt.ptcb = 0;

    return(rc);


} /* VR32PMRequestMutexSem */



/***EP  VR32PMWaitMuxWaitSem - Wait on a MuxWait Semaphore
 *
 *      VR32PMWaitMuxWaitSem is the routine issued by PM to wait on
 *      WINWaitMuxWaitSem.  We first call the pmSemSet routine to
 *      validate that the PM Semaphore handle is valid and to set the
 *      the proper flags and TCB address in the pm semaphore control block.
 *      Upon a successful return, we will call the MuxWaitSem
 *      worker function that executes the wait and pass ot's return code
 *      back to PMWIN.
 *
 *      ENTRY   pmhev     - handle of a PM Event Semaphore
 *              hmux      - handle of a muxwait
 *              ulTimeout - timeout in milliseconds
 *              pulUser   - For DCMW_WAIT_ANY muxwait, it is filled in with
 *                            the ULONG of the semaphore that occured.  If
 *                            it was a mutex, then it's owned by the caller.
 *                        - For DCMW_WAIT_ALL muxwait, it is filled in with
 *                            the ULONG of the LAST semaphore that occurred.
 *                            All mutex semaphores are owned by the caller.
 *                            Note that, for events, this means that all of
 *                            them were cleared at some point since the wait
 *                            call.  This does not mean that they are all
 *                            clear now.
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_EMPTY_MUXWAIT
 *                        ERROR_INTERRUPT
 *                        ERROR_INVALID_HANDLE
 *                        ERROR_MUTEX_OWNED
 *                        ERROR_SEM_OWNER_DIED
 *                        ERROR_TIMEOUT
 *                        ERROR_WRONG_TYPE
 *                        PM_EVENT_POSTED
 *
 *      PSEUDO CODE
 *
 *      call pmSemSet to validate pmhev and set flags
 *      call SEMApiWaitMuxWaitSem routine
 *      return (rc)
 */

ULONG cdecl VR32PMWaitMuxWaitSem(THUNKSEF sef,
                           HEV pmhev,          /* muxwait handle */
                           HMUX hmux,          /* muxwait handle */
                           ULONG ulTimeout,    /* timeout in milliseconds */
                           PULONG pulUser)     /* user ulong */

{

  PSEM   pEvt;
  ULONG  rc;


    /*
     *  Check validity of PM Shared Event Semaphore and get address of
     *  semaphores control block.
     */
    if (((ULONG)(pEvt = pmSemIN(semStripSigBits(pmhev)))) == ERROR_INVALID_HANDLE ) {
        return(ERROR_INVALID_HANDLE);
    }

    /* If event is already posted, then return to caller */
    if (((PSHUN)pEvt)->SEvt.usFlags & DE_POSTED )
       return(PM_EVENT_POSTED);

    /* Set flags in the TCB to specify a PM Event Wait */
    pTCBCur->TCBSpareFlags |= SPFLAGS_PM_EVENT;

    /* Save current ptcb address in pm sem table entry. */
    ((PSHUN)pEvt)->SEvt.ptcb = pTCBCur;

    /*
     *  Call the worker layer.
     */
    rc = SEMApiWaitMuxWaitSem(hmux,ulTimeout,pulUser);

    /* Insure that the PM_EVENT Waiting flag is reset. */
    pTCBCur->TCBSpareFlags &= ~SPFLAGS_PM_EVENT;

    pEvt = (PSEM)pShSemTbl[semGetIndex(semStripSigBits(pmhev))];

    /* Set ptcb address in pm sem table entry to zeroe. */
    ((PSHUN)pEvt)->SEvt.ptcb = 0;

    return(rc);


} /* VR32PMWaitMuxWaitSem */


/*
 *   Device Helper C worker routines.
 */

/***EP  dhw_PMPostEventSem - Dev Help Post PM Event Semaphore
 *
 *      dhw_PMPostEventSem is the worker for the dh_PMPostEventSem routine.
 *      This routine is callable from C and is exported for use by the
 *      kernel.  This routine calls directly into the PM post event semaphore
 *      layer to post the event semaphore.  All allocations and validations
 *      are done in semPMPostEventSem.
 *
 *      ENTRY   hev - handle to PM shared event semaphore
 *
 *      EXIT    Success - NO_ERROR
 *                        ERROR_ALREADY_POSTED
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_TOO_MANY_POSTS
 *
 *      PSEUDO CODE
 *
 *      return ( call SEMApiPostEventSem )
 */

ULONG dhw_PMPostEventSem(HEV pmhev)         /* shared event handle */

{                                    //98809
    return(semPMPostEventSem(pmhev | SEM_PMSEMCALL)); /* Go do Post event processing. */
}


/***EP  VR32PM16SEMCHK - Check PM Semaphore for 16 Bit Support
 *
 *      This routine is called from the 16 Bit semaphore support in DOSCALL1.
 *      It validates the PM Semaphore as being good and set a flag in the
 *      PM Semaphore tables entry stating we are waiting on a 16 bit semaphore.
 *      and returns control back to the DOSCALL1 16 semaphore routine. This
 *      routine should only be called from DOSPMSEMWAIT and DSOPMMUXSEMWAIT.
 *
 *      ENTRY   hev - handle to PM shared event semaphore
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *
 *      PSEUDO CODE
 *
 *      return
 */

ULONG cdecl VR32PM16SEMCHK(THUNKSEF sef,
                           HEV pmhev)          /* PM event handle */

{

  PSEM   pEvt;

    tkAssertTasktime();        /* call NOT allowed at interrupt time */

    /* Go validate PM Semphore and get address of pm sempahore table entry */
    if ( (ULONG)(pEvt = pmSemIN(semStripSigBits(pmhev))) == ERROR_INVALID_HANDLE )
       return(ERROR_INVALID_HANDLE);

    /* If Semaphore has already been posted, return to caller */
    if ( ((PSHUN)pEvt)->SEvt.usFlags & DE_POSTED ) {
       return(PM_EVENT_POSTED);
    } /* endif */

    /* Set flags in the TCB to specify a PM Event Wait */
    pTCBCur->TCBSpareFlags |= SPFLAGS_PM_EVENT;

    /* Save current ptcb address in pm sem table entry. */
    ((PSHUN)pEvt)->SEvt.ptcb = pTCBCur;

    return(NO_ERROR);

} /* VR32PM16SEMCHK */


/***EP  VR32PM16SEMRST - PM Semaphore for 16 Bit Reset Support
 *
 *      This routine is called from the 16 Bit semaphore support in DOSCALL1.
 *      It validates the PM Semaphore as being good and set a flag in the
 *      PM Semaphore tables entry stating we are waiting on a 16 bit semaphore.
 *      and returns control back to the DOSCALL1 16 semaphore routine. This
 *      routine should only be called from DOSPMSEMWAIT and DSOPMMUXSEMWAIT.
 *
 *      ENTRY   hev - handle to PM shared event semaphore
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *
 *      PSEUDO CODE
 *
 *      return
 */

ULONG cdecl VR32PM16SEMRST(THUNKSEF sef,
                           HEV pmhev)          /* PM event handle */

{

  PSEM   pEvt;

    tkAssertTasktime();        /* call NOT allowed at interrupt time */

    /* Go validate PM Semphore and get address of pm sempahore table entry */
    if ( (ULONG)(pEvt = pmSemIN(semStripSigBits(pmhev))) == ERROR_INVALID_HANDLE )
       return(ERROR_INVALID_HANDLE);

    /* Reset ptcb address in pm sem table entry to 0. */
    ((PSHUN)pEvt)->SEvt.ptcb = 0;

    return(NO_ERROR);

} /* VR32PM16SEMCHK */

