/* SCCSID = "%W% %E%" */
/****************************************************************************/
/*                                                                          */
/*                           IBM Confidential                               */
/*                                                                          */
/*                 Copyright (c) IBM Corporation 1996                       */
/*                           All Rights Reserved                            */
/*                                                                          */
/****************************************************************************/
/************************** START OF SPECIFICATIONS ***************************/
/*                                                                            */
/*   SOURCE FILE NAME:  PARIDC.C                                              */
/*                                                                            */
/*   DESCRIPTIVE NAME:  PARALLEL port device driver inter-device driver       */
/*                      communication routines.                               */
/*                                                                            */
/*   FUNCTION: These routines handle the PDD-PDD and VDD-PDD IDC for the      */
/*             parallel port device driver.                                   */
/*                                                                            */
/*   NOTES:                                                                   */
/*      DEPENDENCIES: None                                                    */
/*      RESTRICTIONS: None                                                    */
/*                                                                            */
/*   ENTRY POINTS:                                                            */
/*             parLPTCMD_Entry       VDD - PDD IDC Entry Point                */
/*              vdmRegister                                                   */
/*              vdmDeregister                                                 */
/*              parReqExclusive                                               */
/*               CheckDirectAccess                                            */
/*              parRelExclusive                                               */
/*               FreeDirectAccess                                             */
/*              vdmRetParBufSize                                              */
/*              parRequestDirect                                              */
/*              parReleaseDirect                                              */
/*              parQueryDeniedAccess                                          */
/*             parPDD_Entry          PDD - PDD IDC Entry Point                */
/*                                                                            */
/*   EXTERNAL REFERENCES:                                                     */
/*                                                                            */
/* Change Log                                                                 */
/*                                                                            */
/*  Mark    yy/mm/dd  Programmer      Comment                                 */
/*  ----    --------  ----------      -------                                 */
/*          96/03/01  Frank Schroeder Original developer.                     */
/*                                                                            */
/**************************** END OF SPECIFICATIONS ***************************/

#include "par.h"

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  parLPTCMD_Entry                                  */
/*                                                                    */
/* DESCRIPTIVE NAME:  Printer Physical Device Driver virtual IDC      */
/*                    entry point and request router routine.         */
/*                                                                    */
/* FUNCTION:  This routine is the Printer Physical Device Driver IDC  */
/*            entry point router/handler. IDC function requests are   */
/*            routed to the appropriate worker routine. The address   */
/*            of this routine is setup via the DevHelp RegisterPDD    */
/*            call during initialization.                             */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  parLPTCMD_Entry                                     */
/*    LINKAGE  :  CALL FAR                                            */
/*                                                                    */
/* INPUT:  On entry the stack is set-up as shown below:               */
/*                                                                    */
/*         TOS --> return address (4 words)                           */
/*                 variable 2     (2 words)                           */
/*                 variable 1     (2 words)                           */
/*                 function code  (2 words)                           */
/*                                                                    */
/* Where: Variable 1 and 2 are specified in the worker routines.      */
/*        BX:CX =  variable 1                                         */
/*        DX:DI -> variable 2                                         */
/*                                                                    */
/* EXIT-NORMAL:  EAX = 0                                              */
/*                                                                    */
/* EXIT-ERROR:  EAX = error return code                               */
/*                                                                    */
/* EFFECTS:  None                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  vidcRouteTable[]                             */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
void far pascal _loadds parLPTCMD_Entry( ULONG FuncCode,
                                            ULONG var1, ULONG var2 )
{

  _asm
  {
    add  bp,4
    push es
    push bx
    push cx
    push dx
  }
  /*
  VLPT is making 16:32 calls to this code.
  In particular it is using a 32 bit stack so CS is padded to 4 bytes and
  the EIP takes four bytes.  Since this compiler and model is only aware of
  16:16 code, we are wrong on the stack setup by 2+2=4 bytes.
  */


    if (FuncCode <= MAX_VIDC_CMD)
      (*vidcRouteTable[FuncCode])( var1, var2 );
    else
    {
      _asm
      {
        mov   ax, ERROR_I24_INVALID_PARAMETER
      }
    }
    _asm
    {
      pop   dx
      pop   cx
      pop   bx
      pop   es
      pop   ds             /* done by compiler */
      pop   si
      pop   di
      pop   bp
      _emit 66h            /* = retfd 12 */
      _emit 0cah
      _emit 0ch
      _emit 00h
    }

}


/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  VDMRegister                                      */
/*                                                                    */
/* DESCRIPTIVE NAME:  Register VLPT IDC entry point address.          */
/*                                                                    */
/* FUNCTION:  This routine is the printer physical device driver      */
/*            register VLPT worker routine.  It saves the 16:32       */
/*            address of the VLPT IDC router in the printer device    */
/*            driver's data segment.                                  */
/*                                                                    */
/* NOTES:  Currently, this interface is unidirectional, VLPT calls    */
/*         the PLPT so this function is a nop.                        */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  VDMRegister                                         */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  var1 = 16 bit Segment of the VLPT Entry Point              */
/*         var2 = 32 bit Offset of the VLPT Entry Point               */
/*                                                                    */
/* EXIT-NORMAL:  VLPT entry point address saved.                      */
/*               (TRUE - required for success by VDH interfaces)      */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS:  VLPT address is saved.                                   */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
USHORT vdmRegister( ULONG var1, ULONG var2 )
{
  return ( TRUE );
}


/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  VDMDeRegister                                    */
/*                                                                    */
/* DESCRIPTIVE NAME:  Deregister VLPT IDC entry point address.        */
/*                                                                    */
/* FUNCTION:  This routine is the printer physical device driver      */
/*            deregister VLPT worker routine.  It clears the 16:32    */
/*            address of the VLPT IDC router in the printer device    */
/*            driver's data segment.                                  */
/*                                                                    */
/* NOTES:  Currently, this interface is unidirectional, VLPT calls    */
/*         the PLPT so this function in a nop.                        */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  VDMDeRegister                                       */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  None                                                       */
/*                                                                    */
/* EXIT-NORMAL:  VLPT entry point address cleared.                    */
/*               (TRUE - required for success by VDH interfaces)      */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS:  VLPT address is cleared.                                 */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
USHORT vdmDeregister( ULONG var1, ULONG var2 )
{
  return ( TRUE );
}


/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  parReqExclusive                                  */
/*                                                                    */
/* DESCRIPTIVE NAME:  Request Exclusive Parallel Port Usage.          */
/*                                                                    */
/* FUNCTION:  This routine is the physical parallel port device driver*/
/*            request exclusive parallel port usage worker routine.   */
/*            It determines if a (physical or virtual) device driver  */
/*            already has exclusive access of the port.  If no one has*/
/*            exclusive access to the parallel port, then exclusive   */
/*            access is granted.  Exclusive access to the parallel    */
/*            port remains in effect until an PrtRelExclusive call is */
/*            made.                                                   */
/*                                                                    */
/* NOTES: User can set "Share Access" checkbox in print object to     */
/*        specify that user will perform serialization of access to   */
/*        parallel port hardware.                                     */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  parReqExclusive                                     */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* INPUT:  ULONG    var1 - port id (0 = lpt1, 1 = lpt2, 2 = lpt3)     */
/*         ULONG    var2 - flags (Bit 0 = 1 block til exclusive avail)*/
/*                  es   - caller's ds                                */
/*                                                                    */
/* EXIT-NORMAL:  0, Caller granted exclusive access.                  */
/*                                                                    */
/* EXIT-ERROR:  ERROR_I24_INVALID_PARAMETER (bad port id)             */
/*                                                                    */
/* EFFECTS:  None                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  CheckDirectAccess                            */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
USHORT parReqExclusive( ULONG var1, ULONG var2 )
{
  parInstance_t   *pInst;
  USHORT          UniqueID;         /* use user's es as unique id */

  _asm
  {
     mov UniqueID, es
  }

  if ( var1 < MAX_LPT )
  {
    pInst = (parInstance_t *)&InstanceCBs[var1];
    return ( CheckDirectAccess( pInst, UniqueID, (BOOL)var2 ) );
  }
  else
    return ( ERROR_I24_INVALID_PARAMETER );
}


/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  CheckDirectAccess                                */
/*                                                                    */
/* DESCRIPTIVE NAME:  Grant direct access privilege when no one else  */
/*                    has it.                                         */
/*                                                                    */
/* FUNCTION:  This routine will test to see if a physical or virtual  */
/*            device driver has requested direct access to the paralle*/
/*            port hardware and if so the caller can block until the  */
/*            device driver with direct access releases it.           */
/*                                                                    */
/* NOTE:  User can set "Share Access" checkbox in print object to     */
/*        specify that user will perform serialization of access to   */
/*        parallel port hardware.                                     */
/*                                                                    */
/* ENTRY POINT :  CheckDirectAccess                                   */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  parInstance_t    pInst - adapter instance                  */
/*         USHORT           ExclusiveID - ID to identify caller       */
/*         BOOL             Block (TRUE = block til exclusive avail)  */
/*                                                                    */
/* EXIT-NORMAL: 0                                                     */
/*                                                                    */
/* EXIT-ERROR:  ERROR_I24_CHAR_CALL_INTERRUPTED                       */
/*              ERROR_I24_DEVICE_IN_USE                               */
/*                                                                    */
/* EFFECTS:  F_DENIED_ACCESS, F_DIRECT_MODE, pInst->BlockedWaitCount  */
/*                                                                    */
/* INTERNAL REFERENCES:  ResetInstanceFlags                           */
/*    ROUTINES:          SetInstanceFlags                             */
/*                       LockInstance                                 */
/*                       UnLockInstance                               */
/*                                                                    */
/* EXTERNAL REFERENCES:  DEVICE_HELP  ProcBlock                       */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
USHORT CheckDirectAccess( parInstance_t *pInst, USHORT ExclusiveID,
                                                  BOOL bBlock )
{
  USHORT rc = 0;
  USHORT rcDevHelp;

  LockInstance( pInst );

  while ( TRUE )
  {
    /*---------------------------------------------------------*/
    /* if share access option set by user, always grant access */
    /* (user performing serialization of hardware accesses) or */
    /* if no one has direct access, grant direct access.       */
    /*---------------------------------------------------------*/
    if ((pInst->Flags & F_SHARE_MODE) ||
        ((pInst->Flags & F_DIRECT_MODE) == 0))
    {
      pInst->ExclusiveID = ExclusiveID;
      UnLockInstance( pInst );
      ResetInstanceFlags( pInst, F_DENIED_ACCESS );
      SetInstanceFlags( pInst, F_DIRECT_MODE );      /* set direct access */
      return( rc );
    }
    /*---------------------------------------------------------*/
    /* else a VDD or PDD already has direct access to the port */
    /*---------------------------------------------------------*/
    else
    {
      /*---------------------------------------------------------*/
      /* Is requester willing to block waiting for the port?     */
      /*---------------------------------------------------------*/
      if (bBlock)
      {
        pInst->BlockedWaitCount++;
        rcDevHelp = DevHelp_ProcBlock( (ULONG)(parInstance_t far *)pInst,
                                                                      -1,
                                                   WAIT_IS_INTERRUPTABLE );
        pInst->BlockedWaitCount--;

        if (rcDevHelp == WAIT_INTERRUPTED)  /* User hit Control-Break */
        {
          UnLockInstance( pInst );
          rc = ERROR_I24_CHAR_CALL_INTERRUPTED;
          return( rc );
        }
      }
      /*---------------------------------------------------------*/
      /* Requester wants direct access and not willing to block. */
      /* Another thread has direct access so must deny access.   */
      /*---------------------------------------------------------*/
      else
      {
        UnLockInstance( pInst );
        SetInstanceFlags( pInst, F_DENIED_ACCESS );
        rc = ERROR_I24_DEVICE_IN_USE;
        return( rc );
      }
    }
  } /* endwhile */
}


/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  parRelExclusive                                  */
/*                                                                    */
/* DESCRIPTIVE NAME:  Release Exclusive Parallel Port Usage.          */
/*                                                                    */
/* FUNCTION:  This routine is the physical parallel port device driver*/
/*            release exclusive parallel port usage worker routine.   */
/*            It determines if a physical or virtual device driver has*/
/*            exclusive access to the port and releases it.  If a     */
/*            device driver is blocked waiting for access, it will be */
/*            run so that it can obtain direct access priviledge to   */
/*            the port.                                               */
/*                                                                    */
/* NOTES: User can set "Share Access" checkbox in print object to     */
/*        specify that user will perform serialization of access to   */
/*        parallel port hardware.                                     */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  parRelExclusive                                     */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  ULONG    var1 - port id (0 = lpt1, 1 = lpt2, 2 = lpt3)     */
/*         ULONG    var2 -                                            */
/*                                                                    */
/* EXIT-NORMAL:  0, Caller granted release of exclusive access.       */
/*                                                                    */
/* EXIT-ERROR:  ERROR_I24_INVALID_PARAMETER (bad port id)             */
/*                                                                    */
/* EFFECTS:  None                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  FreeDirectAccess                             */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
USHORT parRelExclusive( ULONG var1, ULONG var2 )
{
  parInstance_t   *pInst;
  USHORT          UniqueID;         /* use user's es as unique id */

  _asm
  {
     mov UniqueID, es
  }

  if ( var1 < MAX_LPT )
  {
    pInst = (parInstance_t *)&InstanceCBs[var1];
    return ( FreeDirectAccess( pInst, UniqueID ) );
  }
  else
    return ( ERROR_I24_INVALID_PARAMETER );
}


/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  FreeDirectAccess                                 */
/*                                                                    */
/* DESCRIPTIVE NAME:  Free direct access privilege so others can      */
/*                    access the hardware.                            */
/*                                                                    */
/* FUNCTION:  This routine will run any threads blocked waiting for   */
/*            direct access to the parallel port hardware.            */
/*                                                                    */
/* NOTE:  User can set "Share Access" checkbox in print object to     */
/*        specify that user will perform serialization of access to   */
/*        parallel port hardware.                                     */
/*                                                                    */
/* ENTRY POINT :  FreeDirectAccess                                    */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  parInstance_t   *pInst - adapter instance                  */
/*         USHORT           ExclusiveID - ID to identify caller       */
/*                                                                    */
/* EXIT-NORMAL: 0                                                     */
/*                                                                    */
/* EXIT-ERROR:  ERROR_I24_DEVICE_IN_USE                               */
/*                                                                    */
/* EFFECTS:  DIRECTACCESS flag is clear.                              */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  DEVICE_HELP  ProcRun                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
USHORT FreeDirectAccess( parInstance_t *pInst, USHORT ExclusiveID )
{
  USHORT    AwakeCount;
  USHORT    rc = 0;

  if ( (pInst->Flags & F_SHARE_MODE) ||
       (pInst->ExclusiveID == ExclusiveID) )
  {
     ResetInstanceFlags( pInst, F_DIRECT_MODE );
     DevHelp_ProcRun( (ULONG)(parInstance_t far *)pInst, &AwakeCount );
     return( rc );
  }
  else
  {
     rc = ERROR_I24_DEVICE_IN_USE;
     return( rc );
  }
}


/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  vdmRetParBufSize                                 */
/*                                                                    */
/* DESCRIPTIVE NAME:  Return parallel buffer size routine.            */
/*                                                                    */
/* FUNCTION:  This routine is the printer physical device driver      */
/*            return parallel port buffer size worker routine.        */
/*            This allows the virtual device driver to maximize its   */
/*            buffer to the size of the physical parallel port device */
/*            driver buffer size.                                     */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  vdmRetParBufSize                                    */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  var1 = port id (0 = lpt1, 1 = lpt2, 2 = lpt3)              */
/*         var2 = SELECTOR:OFFSET return area                         */
/*                                                                    */
/* EXIT-NORMAL:  0, var2-> parallel port buffer size.                 */
/*                                                                    */
/* EXIT-ERROR:  ERROR_I24_INVALID_PARAMETER (bad port id)             */
/*                                                                    */
/* EFFECTS:  None                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
USHORT vdmRetParBufSize( ULONG var1, ULONG var2 )
{
  PUSHORT pTemp=(PUSHORT)var2;
  parInstance_t   *pInst;

  if ( var1 < MAX_LPT )
    pInst = (parInstance_t *)&InstanceCBs[var1];
  else
    return ( ERROR_I24_INVALID_PARAMETER );

  /*-----------------------------------------------*/
  /* Currently return absolute 128 rather than     */
  /* retrieve value from PRTMONBUFSIZE= config.sys */
  /* statement stored in adapter instance (pInst). */
  /*-----------------------------------------------*/
  *pTemp = 128;
  return ( 0 );
}


/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  parRequestDirect                                 */
/*                                                                    */
/* DESCRIPTIVE NAME:  Request direct access to the parallel port      */
/*                                                                    */
/* FUNCTION:  This routine is called by the par1283.sys device        */
/*            driver to see if another physical or virtual device     */
/*            driver has exclusive access to the parallel port.       */
/*            This routine requests that the caller's thread be       */
/*            blocked until direct access to the parallel port can    */
/*            granted.                                                */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  parRequestDirect                                    */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  parInstance_t    pInst - adapter instance                  */
/*                                                                    */
/* EXIT-NORMAL:  0                                                    */
/*                                                                    */
/* EXIT-ERROR:  device driver internal error code                     */
/*                                                                    */
/* EFFECTS:  None                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  CheckDirectAccess                            */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
USHORT parRequestDirect( parInstance_t *pInst )
{
  USHORT          UniqueID;         /* use our ds as unique id */
  USHORT          rc = 0;

  _asm
  {
     mov UniqueID, ds
  }

  rc = CheckDirectAccess( pInst, UniqueID, TRUE ); /* block when required */
  if ( rc == ERROR_I24_DEVICE_IN_USE ) /* convert error codes to internal */
    rc = RC_DEVICE_IN_USE;
  if ( rc == ERROR_I24_CHAR_CALL_INTERRUPTED )
    rc = RC_CANCELLED;
  return ( rc );

}


/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  parReleaseDirect                                 */
/*                                                                    */
/* DESCRIPTIVE NAME:  Release direct access to the parallel port      */
/*                                                                    */
/* FUNCTION:  This routine is called by the par1283.sys device        */
/*            driver to release exclusive access to the parallel      */
/*            port.                                                   */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  parReleaseDirect                                    */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  parInstance_t    pInst - adapter instance                  */
/*                                                                    */
/* EXIT-NORMAL:  0                                                    */
/*                                                                    */
/* EXIT-ERROR:  device driver internal error code                     */
/*                                                                    */
/* EFFECTS:  None                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  FreeDirectAccess                             */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
USHORT parReleaseDirect( parInstance_t *pInst )
{
  USHORT          UniqueID;         /* use our ds as unique id */
  USHORT          rc = 0;

  _asm
  {
     mov UniqueID, ds
  }

  rc = FreeDirectAccess( pInst, UniqueID );
  if ( rc == ERROR_I24_DEVICE_IN_USE )    /* convert error codes to internal */
    rc = RC_DEVICE_IN_USE;
  return ( rc );
}


/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  parQueryDeniedAccess                             */
/*                                                                    */
/* DESCRIPTIVE NAME:  Query Denied Access Routine.                    */
/*                                                                    */
/* FUNCTION:  This routine can be used by the device driver that      */
/*            owns exclusive access to the parallel port to determine */
/*            whether another device driver wants exclusive access    */
/*            to the parallel port hardware.  If another device driver*/
/*            was denied access or is currently waiting for access,   */
/*            this routine returns 0xffff.  Other device drivers      */
/*            with queued requests can use this routine to decide     */
/*            when to release exclusive access to the parallel port.  */
/*                                                                    */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  parQueryDeniedAccess                                */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  USHORT       port id (0 = lpt1, 1 = lpt2, 2 = lpt3)        */
/*                                                                    */
/* EXIT-NORMAL:  0xffff - another driver denied access or is waiting  */
/*               0x0000 - no driver denied access or waiting          */
/*                                                                    */
/* EXIT-ERROR:  ERROR_I24_INVALID_PARAMETER                           */
/*                                                                    */
/* EFFECTS:  None                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
USHORT parQueryDeniedAccess( USHORT PortID )
{
  parInstance_t   *pInst;
  USHORT          rc = 0;

  if ( PortID < MAX_LPT )
    pInst = (parInstance_t *)&InstanceCBs[PortID];
  else
    return ( ERROR_I24_INVALID_PARAMETER );

  if (( pInst->BlockedWaitCount != 0 ) ||
      ( pInst->Flags & F_DENIED_ACCESS ))
    rc = 0xffff;

  return ( rc );
}


/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  parPDD_Entry                                     */
/*                                                                    */
/* DESCRIPTIVE NAME:  PDD-PDD IDC entry point and request router      */
/*                                                                    */
/* FUNCTION:  This routine is the PDD-PDD IDC entry point and         */
/*            request router..  IDC function requests are routed      */
/*            to the appropriate worker routine.  The address of      */
/*            this routine is returned to other device drivers via    */
/*            the DevHelp AttachDD call.                              */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  parPDD_Entry                                        */
/*    LINKAGE  :  CALL FAR                                            */
/*                                                                    */
/* INPUT:  USHORT  function code                                      */
/*         USHORT  flags (Bit 0 = 1 block til exclusive avail)        */
/*         USHORT  port id (0 = lpt1, 1 = lpt2, 2 = lpt3)             */
/*                 ES  (caller's DS)                                  */
/*                                                                    */
/* EXIT-NORMAL:  0                                                    */
/*                                                                    */
/* EXIT-ERROR:  device driver error code                              */
/*                                                                    */
/* EFFECTS:  None                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  parReqExclusive                              */
/*    ROUTINES:          parRelExclusive                              */
/*                       parQueryDeniedAccess                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
USHORT parPDD_Entry( USHORT FuncCode, USHORT Flags, USHORT PortID )
{
  USHORT          rc = 0;

  switch (FuncCode)
  {
      case 0:

        rc = parReqExclusive( (ULONG) PortID, (ULONG) Flags );
        break;

      case 1:

        rc = parRelExclusive( (ULONG) PortID, 0 );
        break;

      case 2:

        rc = parQueryDeniedAccess( PortID );
        break;

      default:
         rc = ERROR_I24_INVALID_PARAMETER;

  } /* endswitch */

  return ( rc );
}


