/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1992 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 source code is provided to you solely for       */
/*    the purpose of assisting you in your development of OS/2 device        */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Developer Connection Device Driver       */
/*    Source Kit for OS/2. This Copyright statement may not be removed.      */
/*                                                                           */
/*****************************************************************************/
/* SCCSID = "src/snooper/parallel/parioctl.c, parsnp, c.basedd, currbld 96/04/26" */
/****************************************************************************/
/************************** START OF SPECIFICATIONS ***************************/
/*                                                                            */
/*   SOURCE FILE NAME:  PARIOCTL.C                                            */
/*                                                                            */
/*   DESCRIPTIVE NAME:  PARALLEL port device driver IOCTL                     */
/*                      routines.                                             */
/*                                                                            */
/*   FUNCTION: These routines handle the IOCTLs for the parallel port         */
/*             device driver.                                                 */
/*                                                                            */
/*   NOTES:                                                                   */
/*      DEPENDENCIES: None                                                    */
/*      RESTRICTIONS: None                                                    */
/*                                                                            */
/*   ENTRY POINTS:                                                            */
/*             adapterInit                                                    */
/*             parInitComp                                                    */
/*             parDevIDQuery                                                  */
/*              parDeviceIDQuery                                              */
/*             CompleteRP                                                     */
/*                                                                            */
/*   EXTERNAL REFERENCES:                                                     */
/*                                                                            */
/* Change Log                                                                 */
/*                                                                            */
/*  Mark    yy/mm/dd  Programmer      Comment                                 */
/*  ----    --------  ----------      -------                                 */
/*          96/03/01  Frank Schroeder Original developer.                     */
/*          96/06/24  Frank Schroeder Fixed timeout problem with Deskjets     */
/*                                                                            */
/**************************** END OF SPECIFICATIONS ***************************/

#include "par.h"

/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  adapterInit                                      */
/*                                                                    */
/* DESCRIPTIVE NAME:  Initializes the PARALLEL port adapter instance  */
/*                    data area.                                      */
/*                                                                    */
/* FUNCTION:  The function of this routine is to initialize the       */
/*            parallel port adapter data area.                        */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Initialization time                                       */
/*                                                                    */
/* ENTRY POINT:  adapterInit                                          */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  PSZ    pRP      - init kernel request packet               */
/*                                                                    */
/* EXIT-NORMAL: None                                                  */
/*                                                                    */
/* EXIT-ERROR:  None                                                  */
/*                                                                    */
/* EFFECTS: None                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  AllocateInstance                             */
/*                                                                    */
/* EXTERNAL REFERENCES:  DevHelp_AllocGDTSelector                     */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void far adapterInit( PSZ pRP )
{
  parInstance_t   *pInst;        /* ptr to adapter instance data area */
  FPBIOSDATAAREA pBIOSDataArea;  /* ptr to BIOS data area */
  PRPINITIN       pRPI;          /* input request packet far pointer */
  PMACHINE_CONFIG_INFO  pMInfo;  /* ptr to machine config information pkt  */

  /*---------------------------------------------------*/
  /* Get bus info from initialization request packet   */
  /* Initialize BusType field                          */
  /*---------------------------------------------------*/
  pRPI = (PRPINITIN) pRP;
  pMInfo = (PMACHINE_CONFIG_INFO)pRPI->InitArgs;
  OFFSETOF(pMInfo) = ((PDDD_PARM_LIST)pMInfo)->machine_config_table;

  /*-----------------------------------------*/
  /* Allocate a parallel instance structure  */
  /*-----------------------------------------*/
  if ( !(pInst = AllocateInstance()) )
    goto InitAdap_Failed;              /* then fail initialization */

  /*---------------------------------------------------*/
  /* Allocate GDT selector for data transfers          */
  /*---------------------------------------------------*/
  if (DevHelp_AllocGDTSelector( (PSEL)(&(pInst->GDTSel)), 1 ) != 0 )
    goto InitAdap_Failed;              /* then fail initialization */

  /*---------------------------------------------------*/
  /* Initialize pio object structure                   */
  /*---------------------------------------------------*/
  pBIOSDataArea = MAKEP(0x40, 8);
  pInst->pIO[0] = pBIOSDataArea->parBaseAddr[cInstances-1];

  /*---------------------------------------------------*/
  /* Initialize default timeout values                 */
  /*---------------------------------------------------*/
  pInst->IdleTimeoutMS = 0x4000;
  pInst->TimeoutMS = 0x0100;

  /*---------------------------------------------------*/
  /* Initialize default communication modes            */
  /*---------------------------------------------------*/
  pInst->ReqCommMode = PAR_NIBBLE;
  pInst->CurCommMode = PAR_COMPATIBILITY;

InitAdap_Failed:  /* no special failure processing */
  return;
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  parInitComp                                      */
/*                                                                    */
/* DESCRIPTIVE NAME:  Initialization complete routine                 */
/*                                                                    */
/* FUNCTION:  The function of this routine is to attach to the        */
/*            parallel port device driver.                            */
/*                                                                    */
/* NOTES: Due to driver init ordering, the attachdd must be done at   */
/*        init complete.                                              */
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  parInitComp                                          */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  pRP-> kernel request packet                                */
/*                                                                    */
/* EXIT-NORMAL: STATUS_DONE                                           */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS: None                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  attachIDC                                    */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
USHORT parInitComp( PSZ pRP )
{
#ifdef DEBUG
  dprintf( "par1284.sys: parInitComp\r\n" );
#endif

  attachIDC();          /* attach to PRINT0x.SYS */
  return (STATUS_DONE);
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  parDevIDQuery                                    */
/*                                                                    */
/* DESCRIPTIVE NAME:  Query the device id.                            */
/*                                                                    */
/* FUNCTION:  The function of this routine is to call the routine     */
/*            to query the device id.  On return, block the thread    */
/*            until the request completes.  Then unlock the segment   */
/*            containing the return data.                             */
/*                                                                    */
/* NOTES: parDeviceIDQuery completes at interrupt time preventing     */
/*        the call to DevHelp_UnLock from being issued there.         */
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  parDevIDQuery                                        */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  pRP-> kernel request packet                                */
/*                                                                    */
/* EXIT-NORMAL: N/A                                                   */
/*                                                                    */
/* EXIT-ERROR:  STDON | STERR | ERROR_I24_CHAR_CALL_INTERRUPTED       */
/*              STDON + STERR + ERROR_I24_BAD_UNIT                    */
/*                                                                    */
/* EFFECTS:  None                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  parDeviceIDQuery                             */
/*                                                                    */
/* EXTERNAL REFERENCES:  DevHelp_ProcBlock                            */
/*                       DevHelp_UnLock                               */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
USHORT parDevIDQuery( PSZ pRP1 )
{
  PRP_GENIOCTL pRP;                 /* generic ioctl request packet */
  USHORT       PortID;
  parInstance_t *pInst;

#ifdef DEBUG
  dprintf( "par1284.sys: parDevIDQuery\r\n" );
#endif

  pRP = ((PRP_GENIOCTL)pRP1);
  PortID = (USHORT)*pRP->ParmPacket;

  /*--------------------------------------------------------*/
  /* Validate PortID parameter.  Setup addressability to    */
  /* adapter instance.                                      */
  /*--------------------------------------------------------*/
  if ( PortID < MAX_LPT )
  {
    pInst = (parInstance_t *)&InstanceCBs[PortID];
  }
  else
    return (STDON + STERR + ERROR_I24_INVALID_PARAMETER);

  /*--------------------------------------------------------*/
  /* Make sure parallel port exists.                        */
  /*--------------------------------------------------------*/
  if (pInst->pIO[0] == 0)           /* if no port */
  {
    return(STDON + STERR + ERROR_I24_BAD_UNIT);
  }

  /*--------------------------------------------------------*/
  /* Request exclusive access to hardware.                  */
  /*--------------------------------------------------------*/
  reqexcl();

  pInst->pRP = (PRPH)pRP;
  pInst->ReqState = REQS_INITIAL;
  pInst->ReturnCode = RC_SUCCESS;
  parDeviceIDQuery( pInst );

  /*--------------------------------------------------------*/
  /* Block in-progress IOCtl request.                       */
  /* CompleteRP calls DevHelp_Devdone to wake this request. */
  /* If Ctrl Break is generated, abort this request.        */
  /*--------------------------------------------------------*/
  if (!(pInst->pRP->Status & STDON))
  {
    if ((DevHelp_ProcBlock( (ULONG)pInst->pRP,
                                           -1,
                        WAIT_IS_INTERRUPTABLE ) == WAIT_INTERRUPTED))
    {
      return(STDON | STERR | ERROR_I24_CHAR_CALL_INTERRUPTED);
    }
  } /* endif */

  /*--------------------------------------------------------*/
  /* Release exclusive access to hardware.                  */
  /*--------------------------------------------------------*/
  relexcl();

  /*--------------------------------------------------------*/
  /* Unlock users buffer.                                   */
  /*--------------------------------------------------------*/
  if ( pInst->LockHandle )          /* must issue at task time */
  {
    DevHelp_UnLock( pInst->LockHandle );
    pInst->LockHandle = 0;
  }
  return(pInst->pRP->Status);
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  parDeviceIDQuery                                 */
/*                                                                    */
/* DESCRIPTIVE NAME:  Query the device id.                            */
/*                                                                    */
/* FUNCTION:  The function of this routine is to query the parallel   */
/*            port attached device for its device id.  DeviceID       */
/*            information is obtained by doing a negotiation with     */
/*            a bit set in the extensibility request byte indicating  */
/*            that DeviceID information is to be provided. After the  */
/*            negotiation, a read is done in the negotiated mode to   */
/*            obtain the actual data.                                 */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  parDeviceIDQuery                                     */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  parInstance_t   pInst    - ptr to adapter instance         */
/*                                                                    */
/* EXIT-NORMAL: N/A                                                   */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS: pInst->ReturnCode                                         */
/*                                                                    */
/* INTERNAL REFERENCES:  LockInstance                                 */
/*                       UnLockInstance                               */
/*                       CommModeToExtReq                             */
/*                       do_negotiate                                 */
/*                       ResetInstanceFlags                           */
/*                       do_data_xfer                                 */
/*                       CompleteRP                                   */
/*                                                                    */
/* EXTERNAL REFERENCES:  DevHelp_Lock                                 */
/*                       DevHelp_VirtToPhys                           */
/*                       DevHelp_PhysToGDTSelector                    */
/*                       DevHelp_UnLock                               */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void parDeviceIDQuery( parInstance_t *pInst )
{
  USHORT                fWaitState;
  UCHAR                 ReqCommMode;
  USHORT                maxBufferSize;
  ULONG                 PhysBufAddr;
  PUCHAR                pData;
  USHORT                rc;

#ifdef DEBUG
  dprintf( "par1284.sys: parDeviceIDQuery\r\n" );
#endif

  pData = ((PRP_GENIOCTL)pInst->pRP)->DataPacket;

  LockInstance( pInst );

  if (++pInst->ReqUseCount > 1 )
  {
    UnLockInstance( pInst );
    return;
  }

  do
  {
    UnLockInstance( pInst );

    fWaitState = 0;

    do
    {
      switch ( pInst->ReqState )
      {
        case REQS_INITIAL:
#ifdef DEBUG
  dprintf( "par1284.sys: parDeviceIDQuery - REQS_INITIAL\r\n");
#endif

          /*-----------------------------------------*/
          /* Callback when 'inner' state machine has */
          /* completed.                              */
          /*-----------------------------------------*/
          pInst->DoneRtn       = parDeviceIDQuery;
          pInst->LockHandle    = 0;

          /*-------------------------------------------------*/
          /* Setup pointers to user buffer.                  */
          /*                                                 */
          /* The buffer must be at least three bytes long to */
          /* receive the length prefix and one data byte     */
          /*-------------------------------------------------*/
          pInst->ReqDataCount = ((PRP_GENIOCTL)pInst->pRP)->DataLen;

          if ( pInst->ReqDataCount < 3 )
          {
            pInst->ReturnCode = RC_INVALID_PARM;
            pInst->ReqState   = REQS_COMPLETE;
            break;
          }

          pData = ((PRP_GENIOCTL)pInst->pRP)->DataPacket;

          /*-----------------------------*/
          /* Verify access to parameters */
          /*-----------------------------*/
          if (DevHelp_VerifyAccess(SELECTOROF(pData),
                                   ((PRP_GENIOCTL)pInst->pRP)->DataLen,
                                   OFFSETOF(pData),
                                   VERIFY_READWRITE) != 0)
          {
            pInst->ReturnCode = RC_INVALID_PARM;
            pInst->ReqState   = REQS_COMPLETE;
            break;
          }

          rc = DevHelp_Lock( SELECTOROF(pData),
                             LOCKTYPE_LONG_ANYMEM,
                             WAIT_IS_NOT_INTERRUPTABLE,
                             &pInst->LockHandle );

          rc = DevHelp_VirtToPhys( pData,
                                   &PhysBufAddr );

          rc = DevHelp_PhysToGDTSelector( PhysBufAddr,
                                          pInst->ReqDataCount,
                                          pInst->GDTSel );

          pInst->pDataBuf = MAKEP(pInst->GDTSel, 0);

          ReqCommMode = pInst->ReqCommMode;

          /*------------------------------------------------------*/
          /* Compatibility and EPP Modes do not support obtaining */
          /* DeviceID data                                        */
          /*------------------------------------------------------*/
          if ( pInst->ReqCommMode == PAR_COMPATIBILITY ||
                                         pInst->ReqCommMode == PAR_EPP )

          {
            pInst->ReturnCode = RC_INVALID_PARM;
            pInst->ReqState   = REQS_COMPLETE;
            break;
          }

          /*----------------------------------------------*/
          /* Set the next state for after the negotiation */
          /* completes                                    */
          /*----------------------------------------------*/
          pInst->ReqState = REQS_NEGOTIATE_DONE;


          /*----------------------------------------------*/
          /* Calculate the Extensibility Request byte and */
          /* 'or-in' the DeviceID bit                     */
          /*----------------------------------------------*/
          CommModeToExtReq( pInst,
                            pInst->ReqCommMode,
                            &pInst->ExtReqByte        );

          pInst->ExtReqByte |= NEGOTIATE_DEVICE_ID;

          pInst->CurCommMode = pInst->ReqCommMode;

          /*-------------------------------------------------*/
          /* Begin the negotiation with the DeviceID bit set */
          /*-------------------------------------------------*/
          pInst->State   = NEGS_INIT;
          do_negotiate( pInst );

          /*--------------------------------------*/
          /* Wait for the negotiation to complete */
          /*--------------------------------------*/
          fWaitState = 1;
          break;

        case REQS_NEGOTIATE_DONE:
#ifdef DEBUG
  dprintf( "par1284.sys: parDeviceIDQuery - REQS_NEGOTIATE_DONE\r\n");
#endif

          /*-----------------------------------------------*/
          /* If we had a problem we will attempt to return */
          /* to compatibility mode                         */
          /*-----------------------------------------------*/
          if ( pInst->ReturnCode != RC_SUCCESS )
          {
            pInst->ReqState   = REQS_SET_CPP_MODE;
#ifdef DEBUG
  dprintf( "par1284.sys: parDeviceIDQuery - BAD RETURN CODE\r\n");
#endif
            break;
          }

          /*-----------------------------------------------*/
          /* Setup to issue a two-byte read to obtain the  */
          /* length of the DeviceID data                   */
          /*-----------------------------------------------*/
          ResetInstanceFlags( pInst, F_WRITE_REQ );

          /*-------------------------------------------------*/
          /* Set the next state for after the read completes */
          /*-------------------------------------------------*/
          pInst->ReqState       = REQS_EXECUTE_REQ;
          pInst->ReqDataCount   = 2;

          if( do_data_xfer( pInst ) != RC_SUCCESS )
          {
            pInst->State = REQS_SET_CPP_MODE;
#ifdef DEBUG
  dprintf( "par1284.sys: parDeviceIDQuery - DO_DATA_XFER ERROR\r\n");
#endif
            break;
          }

          /*-------------------------------*/
          /* Wait for the read to complete */
          /*-------------------------------*/
          fWaitState = 1;

          break;

        case REQS_EXECUTE_REQ:
#ifdef DEBUG
  dprintf( "par1284.sys: parDeviceIDQuery - REQS_EXECUTE_REQ\r\n");
#endif

          /*-----------------------------------------------*/
          /* If we had a problem we will attempt to return */
          /* to compatibility mode                         */
          /*-----------------------------------------------*/
          pInst->ReqState = REQS_SET_CPP_MODE;

          if ( pInst->ReturnCode != RC_SUCCESS )
          {
            break;
          }

          /*--------------------------------------*/
          /* The two byte count is sent MSB first */
          /*--------------------------------------*/
          pInst->ReqDataCount = ((pInst->pDataBuf[0] << 8) |
                                        pInst->pDataBuf[1]) - 2;

          if ( !pInst->ReqDataCount )
          {
            break;
          }

          pInst->pDataBuf += 2;

          /*------------------------------------------------*/
          /* Check whether there is sufficient buffer space */
          /* to receive the remainder of the DeviceID data  */
          /*------------------------------------------------*/
          maxBufferSize = ((PRP_GENIOCTL)pInst->pRP)->DataLen - 2;

          if ( pInst->ReqDataCount > maxBufferSize )
          {
            pInst->ReqDataCount = maxBufferSize;
          }

          /*-----------------------------------------*/
          /* Read the remainder of the DeviceID data */
          /*-----------------------------------------*/
          if ( do_data_xfer( pInst ) != RC_SUCCESS )
          {
            break;
          }

          /*-------------------------------*/
          /* Wait for the read to complete */
          /*-------------------------------*/
          fWaitState = 1;
          break;

        case REQS_SET_CPP_MODE:
#ifdef DEBUG
  dprintf( "par1284.sys: parDeviceIDQuery - REQS_SET_CPP_MODE\r\n");
#endif
          /*------------------------------------------*/
          /* Set next state for when negotiation back */
          /* to Compatibility mode completes          */
          /*------------------------------------------*/
          pInst->ReqState = REQS_SET_CPP_DONE;

          /*----------------------------------------------*/
          /* The IEEE-1284 spec requires a return to      */
          /* Compatibility Mode after obtaining DeviceID  */
          /* data                                         */
          /*----------------------------------------------*/
          pInst->CurCommMode = PAR_COMPATIBILITY;

          CommModeToExtReq( pInst,
                            pInst->CurCommMode,
                            &pInst->ExtReqByte        );

          pInst->State   = NEGS_INIT;
          do_negotiate( pInst );

          /*----------------------------------*/
          /* Wait for negotiation to complete */
          /*----------------------------------*/
          fWaitState = 1;

          break;

        case REQS_SET_CPP_DONE:
          /*---------------------------------------------*/
          /* After the negotiation completes we are done */
          /*---------------------------------------------*/
          pInst->ReqState = REQS_COMPLETE;
          break;

        case REQS_COMPLETE:
          fWaitState = 1;
          break;
      }
    }
    while ( !fWaitState );

    LockInstance( pInst );
  }
  while( --pInst->ReqUseCount );

  if ( pInst->ReqState == REQS_COMPLETE )
  {
    UnLockInstance( pInst );
    CompleteRP( pInst );
  }
  else
  {
    UnLockInstance( pInst );
  }
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  CompleteRP                                       */
/*                                                                    */
/* DESCRIPTIVE NAME:  Signals completion of the current request and   */
/*                    dispatches the next waiting request.            */
/*                                                                    */
/* FUNCTION:  The function of this routine is to set the done bit in  */
/*            the in-progress request and return the request.  Then   */
/*            remove the next request from the FIFO queue and         */
/*            dispatch the request to the worker routine.             */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  parCompleteRP                                        */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  pInst-> adapter instance                                   */
/*                                                                    */
/* EXIT-NORMAL: N/A                                                   */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS: None                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  SetRPErrorCode                               */
/*                       CompleteRP                                   */
/*                                                                    */
/* EXTERNAL REFERENCES:  DevHelp_DevDone                              */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void CompleteRP( parInstance_t *pInst )
{

  /*---------------------------------------------------*/
  /* Sanity check...we should always have a current RP */
  /* to complete.                                      */
  /*---------------------------------------------------*/
  if ( !pInst->pRP )
  {
    return;
  }

  if ( pInst->ReturnCode != RC_SUCCESS )
  {
    SetRPErrorCode( pInst );
  }

  DevHelp_DevDone( (PBYTE)pInst->pRP );

}


