/* SCCSID = "%W% %E%" */
/****************************************************************************/
/*                                                                          */
/*                           IBM Confidential                               */
/*                                                                          */
/*                 Copyright (c) IBM Corporation 1996                       */
/*                           All Rights Reserved                            */
/*                                                                          */
/****************************************************************************/
/************************** START OF SPECIFICATIONS ***************************/
/*                                                                            */
/*   SOURCE FILE NAME:  PARNEG.C                                              */
/*                                                                            */
/*   DESCRIPTIVE NAME:  PARALLEL port device driver IEEE-1284 negotiation     */
/*                      routines.                                             */
/*                                                                            */
/*   FUNCTION: These routines handle the IEEE-1284 negotiations for the       */
/*             parallel port device driver.                                   */
/*                                                                            */
/*   NOTES:                                                                   */
/*      DEPENDENCIES: None                                                    */
/*      RESTRICTIONS: None                                                    */
/*                                                                            */
/*   ENTRY POINTS:                                                            */
/*             do_negotiate                                                   */
/*             negotiate_timeout                                              */
/*             ecp_setup_timeout                                              */
/*             CommModeToExtReq                                               */
/*             negotiate_mode                                                 */
/*                                                                            */
/*   EXTERNAL REFERENCES:                                                     */
/*                                                                            */
/* Change Log                                                                 */
/*                                                                            */
/*  Mark    yy/mm/dd  Programmer      Comment                                 */
/*  ----    --------  ----------      -------                                 */
/*          96/03/01  Frank Schroeder                                         */
/*                                                                            */
/**************************** END OF SPECIFICATIONS ***************************/

#include        "par.h"

/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  do_negotiate                                     */
/*                                                                    */
/* DESCRIPTIVE NAME:  Negotiate an IEEE-1284 mode                     */
/*                                                                    */
/* FUNCTION:  The function of this routine is to negotiate into or    */
/*            out of an IEEE-1284 mode.                               */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task or Interrupt Time                                    */
/*                                                                    */
/* ENTRY POINT:  do_negotiate                                         */
/*    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                               */
/*                       IOWrite8                                     */
/*                       IORead8                                      */
/*                       IODelay                                      */
/*                       ResetInstanceFlags                           */
/*                       setDelayTimer                                */
/*                       cancelDelayTimer                             */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void do_negotiate( parInstance_t *pInst )
{
  UCHAR                 status;
  BOOL                  XFlag;
  USHORT                fWaitState;


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

  /*-------------------------------------------------*/
  /* A reentrant call results in pInst->UseCount     */
  /* being incremented with no further action until  */
  /* the original instance completes.                */
  /*-------------------------------------------------*/
  LockInstance( pInst );

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

  do
  {
    fWaitState = 0;

    UnLockInstance( pInst );

    do
    {
      switch ( pInst->State )
      {
        /*-----------------------------------------------------*/
        /* Return the chipset to compatibility mode. The       */
        /* IEEE-1284 spec requires all negotiations to start   */
        /* from compatibility mode.                            */
        /*                                                     */
        /*-----------------------------------------------------*/
        case NEGS_INIT :
#ifdef DEBUG
  dprintf( "par1284.sys: do_negotiate - NEGS_INIT\r\n");
#endif

          /*-----------------------------------------------*/
          /* Translate the CONTROL register image          */
          /* to its 1284 definition.                       */
          /* ACKIRQ signal always reads as a 1. Turn IRQ   */
          /* off until needed in control register shadow.  */
          /*-----------------------------------------------*/
          pInst->ControlReg =  IORead8( pInst->pIO[0], SPP_CONTROL_REG );
          pInst->ControlReg ^= SPP1284_CONTROL_INVERT;
          pInst->ControlReg &= SPP_CONTROL_ACKIRQ;

          /*------------------------------------------------*/
          /* If the chipset supports the Extended Control   */
          /* Reg set (Base+0x400) then return the parallel  */
          /* port to PS/2 mode.                             */
          /*------------------------------------------------*/
          if ( pInst->Flags & F_FIFO_SUPPORT )
          {
            pInst->ExtControlReg &= ~EXT_CONTROL_MODE;
            pInst->ExtControlReg |= EXT_CONTROL_MODE1;

            IOWrite8( pInst->pIO[1],
                      EXT_CONTROL_REG,
                      ((UCHAR)(pInst->ExtControlReg ^ EXT_CONTROL_INVERT)));

#ifdef DEBUG
  dprintf( "par1284.sys: do_negotiate - NEGS_INIT - ExtControlReg = %b\r\n",
                pInst->ExtControlReg ^ EXT_CONTROL_INVERT );
#endif
          }

          ResetInstanceFlags( pInst, F_DIR_REVERSE );

          /*-----------------------------------------------------*/
          /* Set negotiate timeout timer.                        */
          /*-----------------------------------------------------*/
          setDelayTimer( pInst,
                         TIMER_ID_1,
                         negotiate_timeout,
                         (USHORT)1000 );     /* 1 second */

          /*-----------------------------------------------------*/
          /* If device is already in compatibility mode then     */
          /* no need to perform termination phase.               */
          /*-----------------------------------------------------*/
          if ( (pInst->CurCommMode == PAR_COMPATIBILITY) ||
               (pInst->CurCommMode == PAR_INVALID_COMM_MODE) )
          {
            pInst->State = NEGS_START;
            break;
          }

          /*---------------------------------------------------*/
          /* P1284 D2.00 - State 22 (also see Sec 6.8)         */
          /*                                                   */
          /* Assert HOSTBUSY. This tells the attached device   */
          /* not to request a reverse channel transfer while   */
          /* we are exiting 1284 mode.                         */
          /*---------------------------------------------------*/
          pInst->ControlReg |= (SPP_CONTROL_HOSTCLK | SPP_CONTROL_HOSTBUSY);

          IOWrite8( pInst->pIO[0],
                    SPP_CONTROL_REG,
                    ((UCHAR)(pInst->ControlReg ^ SPP1284_CONTROL_INVERT)));

          /*----------------------------------------------------*/
          /* Allow a TL (35ms) delay here.                      */
          /*                                                    */
          /* Although this delay is not explicitly required. It */
          /* avoids a potential race caused by changing HOSTBUSY*/
          /* and 1284ACTIVE at the same time. A shorter in-line */
          /* delay may also work.                               */
          /*----------------------------------------------------*/
          setDelayTimer( pInst,
                         TIMER_ID_0,
                         do_negotiate,
                         DELAY1284_TL );

          fWaitState = 1;

          pInst->State  = NEGS_RESET1284_0;

          break;

        /*---------------------------------------------------*/
        /* P1284 D2.00 - State 22 - Continued                */
        /*                                                   */
        /* Drop 1284ACTIVE (ACTIVE) to complete State 22.    */
        /*---------------------------------------------------*/
        case NEGS_RESET1284_0:
#ifdef DEBUG
  dprintf( "par1284.sys: do_negotiate - NEGS_RESET1284_0\r\n");
#endif

          pInst->ControlReg &= ~(SPP_CONTROL_1284ACTIVE);

          IOWrite8( pInst->pIO[0],
                    SPP_CONTROL_REG,
                    ((UCHAR)(pInst->ControlReg ^ SPP1284_CONTROL_INVERT)));

          /*-----------------------------------------------------*/
          /* Allow TL (35ms) for the attached device to respond. */
          /*                                                     */
          /* After this delay we will verify the attach device   */
          /* responded per P1284 D2.00 - State 24                */
          /*-----------------------------------------------------*/
          setDelayTimer( pInst,
                         TIMER_ID_0,
                         do_negotiate,
                         DELAY1284_TL );

          fWaitState = 1;

          pInst->State  = NEGS_RESET1284_1;

          break;

        /*---------------------------------------------------*/
        /* P1284 D2.00 - States 23, 24                       */
        /*                                                   */
        /* Verify the device has responded properly to our   */
        /* attempt to exit 1284 mode. If the device does not */
        /* respond as expected, we assume that the device was*/
        /* originally in compatibility mode and start the    */
        /* negotiation immediately.                          */
        /*---------------------------------------------------*/
        case NEGS_RESET1284_1:
#ifdef DEBUG
  dprintf( "par1284.sys: do_negotiate - NEGS_RESET1284_1\r\n");
#endif

          /*-----------------------------------------------------*/
          /* Its been at least 35ms since we dropped 1284Active. */
          /*                                                     */
          /* A device should immediately stop driving the        */
          /* parallel bus when 1284 Active is dropped, so it     */
          /* should be 'safe' at this point to reenable host's   */
          /* parallel port line drivers.                         */
          /*-----------------------------------------------------*/
          pInst->ControlReg &= ~SPP_CONTROL_DIR;

          IOWrite8( pInst->pIO[0],
                    SPP_CONTROL_REG,
                    ((UCHAR)(pInst->ControlReg ^ SPP1284_CONTROL_INVERT)));

          /*-------------------------------------------------*/
          /* Verify the device is following 1284 termination */
          /* protocol.                                       */
          /*                                                 */
          /* We expect: PTRCLK    = 0                        */
          /*            PTRBUSY   = 1                        */
          /*            DATAAVAIL = 0                        */
          /*                                                 */
          /* If the device responded as expected we continue */
          /* 1284 termination.                               */
          /*-------------------------------------------------*/
          pInst->StatusReg = IORead8( pInst->pIO[0], SPP_STATUS_REG );
          pInst->StatusReg ^= SPP1284_STATUS_INVERT;

//        status = pInst->StatusReg & (UCHAR)(SPP_STATUS_PTRCLK  | \
//                                     SPP_STATUS_PTRBUSY | \
//                                     SPP_STATUS_DATAAVAIL );
//
//        /*-------------------------------------------------*/
//        /* P1284 D2.00 - State 24                          */
//        /*                                                 */
//        /* Wait for PTRCLK to be set low.                  */
//        /* PTRCLK tells us other signals valid.            */
//        /*-------------------------------------------------*/
//        if (pInst->StatusReg & SPP_STATUS_PTRCLK)            /* ptrclk = 1 */
//          break;
//
//        /*-------------------------------------------------*/
//        /* P1284 D2.00 - State 23                          */
//        /*                                                 */
//        /* Wait for PTRBUSY to be set high.                */
//        /* Wait for DATAAVAIL to be set high.              */
//        /*-------------------------------------------------*/
//        if ( pInst->StatusReg & (SPP_STATUS_PTRBUSY | SPP_STATUS_DATAAVAIL) )
//          break;

          pInst->State = NEGS_RESET1284_2;

          break;

        /*----------------------------------------------------*/
        /* P1284 D2.00 - State 25                             */
        /*                                                    */
        /* We enter State 25 by dropping HOSTBUSY and waiting */
        /* TL (35ms) for the device to respond.               */
        /*----------------------------------------------------*/
        case NEGS_RESET1284_2:
#ifdef DEBUG
  dprintf( "par1284.sys: do_negotiate - NEGS_RESET1284_2\r\n");
#endif

          pInst->ControlReg &= ~SPP_CONTROL_HOSTBUSY;

          IOWrite8( pInst->pIO[0],
                    SPP_CONTROL_REG,
                    ((UCHAR)(pInst->ControlReg ^ SPP1284_CONTROL_INVERT)));

          setDelayTimer( pInst,
                         TIMER_ID_0,
                         do_negotiate,
                         DELAY1284_TL );

          pInst->State = NEGS_RESET1284_3;

          fWaitState = 1;

          break;

        /*----------------------------------------------------*/
        /* P1284 D2.00 - State 28                             */
        /*                                                    */
        /* We have allowed 35ms for the device to respond.    */
        /*                                                    */
        /* The expected response of the device is:            */
        /*   PTRCLK  = 1                                      */
        /*   PTRBUSY = 1                                      */
        /*                                                    */
        /* We do not check whether the device responded       */
        /* properly at this point since there is no recourse  */
        /* but to complete the termination protocol by        */
        /* raising HOSTBUSY.                                  */
        /*----------------------------------------------------*/
        case NEGS_RESET1284_3:
#ifdef DEBUG
  dprintf( "par1284.sys: do_negotiate - NEGS_RESET1284_3\r\n");
#endif

          pInst->StatusReg = IORead8( pInst->pIO[0], SPP_STATUS_REG );
          pInst->StatusReg ^=  SPP1284_STATUS_INVERT;

          /*----------------------------------------------------*/
          /* P1284 D2.00 - State 28                             */
          /*                                                    */
          /* We enter State 28 by raising HOSTBUSY.             */
          /*----------------------------------------------------*/

          pInst->ControlReg |= SPP_CONTROL_HOSTBUSY;

          IOWrite8( pInst->pIO[0],
                    SPP_CONTROL_REG,
                    ((UCHAR)(pInst->ControlReg ^ SPP1284_CONTROL_INVERT)));

          /*--------------------------------------------------------*/
          /* P1284 D2.00 - State 29                                 */
          /*                                                        */
          /* We allow TL (35ms) prior to starting the negotiation.  */
          /*                                                        */
          /* The 1284 Spec specifically requires .5us, however,     */
          /* it seems a longer delay may allow things to settle.    */
          /* This delay is optional and a shorter delay may be      */
          /* substituted.                                           */
          /*                                                        */
          /* The device and host should be in P1284 D2.00 - State 0 */
          /* after this delay.                                      */
          /*--------------------------------------------------------*/
          setDelayTimer( pInst,
                         TIMER_ID_0,
                         do_negotiate,
                         DELAY1284_TL );

          pInst->State = NEGS_START;

          fWaitState = 1;

          break;

        /*---------------------------------------------------*/
        /* P1284 D2.00 - State 1                             */
        /*                                                   */
        /* Start a IEEE-1284 negotiation cycle setting the   */
        /* 1284 control lines as follows:                    */
        /*                                                   */
        /*   Data 0-7   = Extensibility Request Byte         */
        /*   HOSTBUSY   = 0                                  */
        /*   HOSTCLK    = 1                                  */
        /*   1284ACTIVE = 1                                  */
        /*                                                   */
        /* Allow a maximum delay of 35ms to for the attached */
        /* printer to respond.                               */
        /*                                                   */
        /*---------------------------------------------------*/
        case NEGS_START:
#ifdef DEBUG
  dprintf( "par1284.sys: do_negotiate - NEGS_START\r\n");
#endif
          pInst->StatusReg = IORead8( pInst->pIO[0], SPP_STATUS_REG );
          pInst->StatusReg  ^= SPP1284_STATUS_INVERT;

          /* make sure printer is not busy before starting negotiation */

          if (pInst->StatusReg & SPP_STATUS_PTRBUSY)
            break;

          /*----------------------------------------------*/
          /* If we just needed to return to compatibility */
          /* mode, then quit while we're ahead            */
          /*----------------------------------------------*/
          if ( pInst->ExtReqByte == (UCHAR)-1)
          {
            /*-----------------------------------------------*/
            /* However, we need to translate the CONTROL and */
            /* STATUS register images settings back to their */
            /* SPP (non-1284) definitions                    */
            /*-----------------------------------------------*/
            pInst->ControlReg ^= (SPP1284_CONTROL_INVERT ^ SPP_CONTROL_INVERT);
            pInst->StatusReg  ^= (SPP1284_STATUS_INVERT  ^ SPP_STATUS_INVERT);

            pInst->State =  NEGS_COMPLETE;
            break;
          }

          /*--------------------------------------*/
          /* State 0 - Extensibility Request Byte */
          /*--------------------------------------*/
          IOWrite8( pInst->pIO[0], SPP_DATA_REG, pInst->ExtReqByte );

          IODelay();

          /*----------------------------------------*/
          /* State 1 - Signal IEEE-1284 Negotiation */
          /*----------------------------------------*/
          pInst->ControlReg |=  (SPP_CONTROL_1284ACTIVE | SPP_CONTROL_HOSTCLK);
          pInst->ControlReg &=  ~SPP_CONTROL_HOSTBUSY;

          IOWrite8( pInst->pIO[0],
                    SPP_CONTROL_REG,
                    ((UCHAR)(pInst->ControlReg ^ SPP1284_CONTROL_INVERT)));

          pInst->State =  NEGS_ACK1284;

          break;

        /*---------------------------------------------------*/
        /* P1284 D2.00 - State 2-3                           */
        /*                                                   */
        /* Check if the device responded as a IEEE-1284      */
        /* compliant device. If not end the negotiation      */
        /* cycle here.                                       */
        /*                                                   */
        /* A 1284 compliant device should set the its 1284   */
        /* status lines as follows:                          */
        /*                                                   */
        /*    PTRCLK     = 0                                 */
        /*    DATAAVAIL  = 0                                 */
        /*    XFLAG      = 1                                 */
        /*    ACKDATAREQ = 1                                 */
        /*                                                   */
        /* If the device responds as expected we generate    */
        /* a strobe pulse (1us) on HOSTCLK to clock the      */
        /* Extensibility Request into the device and then    */
        /* raise HOSTBUSY.                                   */
        /*                                                   */
        /* We then allow a maximum delay of 35ms to for the  */
        /* device to respond to the Extensibility Request    */
        /* byte.                                             */
        /*                                                   */
        /*---------------------------------------------------*/
        case NEGS_ACK1284:
#ifdef DEBUG
  dprintf( "par1284.sys: do_negotiate - NEGS_ACK1284\r\n");
#endif

          /*-----------------------------------*/
          /* State 2 - Verify IEEE-1284 Device */
          /*-----------------------------------*/
          pInst->StatusReg = IORead8( pInst->pIO[0], SPP_STATUS_REG );
          pInst->StatusReg ^=  SPP1284_STATUS_INVERT;

          if (pInst->StatusReg & SPP_STATUS_PTRCLK)
            break;

          status = pInst->StatusReg & (UCHAR)( SPP_STATUS_PTRCLK  |
                                        SPP_STATUS_DATAAVAIL  |
                                        SPP_STATUS_XFLAG      |
                                        SPP_STATUS_ACKDATAREQ  );

          if ( status != (SPP_STATUS_XFLAG | SPP_STATUS_ACKDATAREQ) )
          {
            pInst->ReturnCode     = RC_NOT_1284_DEVICE;
            pInst->State          = NEGS_STATE_0;
            break;
          }

          /*---------------------------------------*/
          /* State 3 - Set HOSTCLK strobe low      */
          /* "Strobing Extensibility Request Byte" */
          /*---------------------------------------*/
          pInst->ControlReg &= ~(SPP_CONTROL_HOSTCLK | SPP_CONTROL_DIR);

          IOWrite8( pInst->pIO[0],
                    SPP_CONTROL_REG,
                    ((UCHAR)(pInst->ControlReg ^ SPP1284_CONTROL_INVERT)));

          IODelay();
          IODelay();

          /*-----------------------------------*/
          /* State 4 - Set HOSTCLK strobe high */
          /*-----------------------------------*/
          pInst->ControlReg |= (SPP_CONTROL_HOSTBUSY | SPP_CONTROL_HOSTCLK );

          IOWrite8( pInst->pIO[0],
                    SPP_CONTROL_REG,
                    ((UCHAR)(pInst->ControlReg ^ SPP1284_CONTROL_INVERT)));

          /*--------------------------*/
          /* Wait for device response */
          /*--------------------------*/
          setDelayTimer( pInst,
                         TIMER_ID_0,
                         do_negotiate,
                         DELAY1284_TL       );

          pInst->State =  NEGS_RESPONSE;

          fWaitState = 1;

          break;

        /*---------------------------------------------------*/
        /* P1284 D2.00 - State 5, 6                          */
        /*                                                   */
        /* If the attached device completed the negotiation  */
        /* handshake properly, it will set:                  */
        /*                                                   */
        /*  ACKDATAREQ = 0                                   */
        /*  PTRCLK     = 1                                   */
        /*                                                   */
        /* If the device accepted our Extensibility Request  */
        /* byte it will also set XFLAG.                      */
        /*                                                   */
        /* Note:                                             */
        /*   In the case of Nibble Mode (ExtReqByte = 0)     */
        /*   XFLAG = 0 indicates a successful negotiation.   */
        /*   For all other ExtReqBytes, XFLAG must be set    */
        /*   1.                                              */
        /*                                                   */
        /* At this point the printer may have data for us.   */
        /* This is handled by the called requesting a data   */
        /* transfer in the negotiated mode.                  */
        /*                                                   */
        /*---------------------------------------------------*/
        case NEGS_RESPONSE:
#ifdef DEBUG
  dprintf( "par1284.sys: do_negotiate - NEGS_RESPONSE\r\n");
#endif

          pInst->State = NEGS_COMPLETE;

          /*---------------------------------*/
          /* Get IEEE-1284 Status Lines      */
          /*---------------------------------*/
          pInst->StatusReg = IORead8( pInst->pIO[0], SPP_STATUS_REG );

#ifdef DEBUG
  dprintf( "par1284.sys: Negotiate status = %b\r\n", pInst->StatusReg );
#endif

          pInst->StatusReg ^=  SPP1284_STATUS_INVERT;

          status = pInst->StatusReg & (UCHAR)SPP_STATUS_PTRCLK;

          /*---------------------------------*/
          /* State 6 - Check for PTRCLK high */
          /*---------------------------------*/
          if ( status != SPP_STATUS_PTRCLK )
            break;

          /*-------------------------------------------*/
          /* State 5 - Check for correct XFLAG setting */
          /*                                           */
          /* We generate C boolean(s) based on the     */
          /* value of XFLAG and ExtReqByte. If these   */
          /* booleans agree, i.e. both TRUE or both    */
          /* FALSE then the negotiation was successful.*/
          /*                                           */
          /* This checks the expected XFLAG values vs  */
          /* ExtReqByte in P1284 D2.00 Table 4.        */
          /*-------------------------------------------*/
          XFlag = ((pInst->StatusReg & SPP_STATUS_XFLAG) != 0);

          if ( (BOOL)(pInst->ExtReqByte != 0) != XFlag )
          {
            pInst->ReturnCode = RC_INVALID_MODE;
            pInst->State      = NEGS_STATE_0;
            break;
          }

          /*-------------------------------------------*/
          /* In ECP mode, must perform setup phase.    */
          /*-------------------------------------------*/
          if ( pInst->ExtReqByte & NEGOTIATE_ECP_MODE )
            pInst->State = NEGS_ECP_SETUP;

          break;

        /*--------------------------------------------------*/
        /* P1284 D2.00 - State 30                           */
        /*                                                  */
        /* Negotiation into ECP mode has successfully       */
        /* completed (through state 6).  Begin ECP setup    */
        /* phase.                                           */
        /*                                                  */
        /* State 6 completed with:                          */
        /* nAckReverse (PError) was set low by peripheral   */
        /* PeriphClk (nAck) was set high by peripheral      */
        /*                                                  */
        /* State 30:                                        */
        /* HostAck (nAutoFd) is set low by host            */
        /*--------------------------------------------------*/
        case NEGS_ECP_SETUP:
#ifdef DEBUG
  dprintf( "par1284.sys: do_negotiate - NEGS_ECP_SETUP\r\n");
#endif
          pInst->ControlReg &= ~SPP_CONTROL_HOSTACK;

          IOWrite8( pInst->pIO[0],
                  SPP_CONTROL_REG,
                  (UCHAR)(pInst->ControlReg ^ SPP1284_CONTROL_INVERT));

          /* Set fixed interval for setup handshake timeout. */
          setDelayTimer( pInst,
                         TIMER_ID_0,
                         ecp_setup_timeout,
                         (USHORT)DELAY1284_SETUP );

          pInst->State = NEGS_WAIT_ACKREVERSE_HIGH;

          break;

        /*---------------------------------------------------*/
        /* P1284 D2.00 - State 31                            */
        /*                                                   */
        /* Wait for nAckReverse to be set high by peripheral */
        /*---------------------------------------------------*/
        case NEGS_WAIT_ACKREVERSE_HIGH:
#ifdef DEBUG
  dprintf( "par1284.sys: do_negotiate - NEGS_WAIT_ACKREVERSE_HIGH\r\n");
#endif
          pInst->StatusReg  = IORead8( pInst->pIO[0], SPP_STATUS_REG );
          pInst->StatusReg ^= SPP1284_ECP_STATUS_INVERT;

          /*-----------------------------------------------*/
          /* Determine whether device has transitioned to  */
          /* the forward idle phase.                       */
          /*-----------------------------------------------*/
          if ( (pInst->StatusReg & SPP_STATUS_ACKREVERSE) ||
              !(pInst->StatusReg & SPP_STATUS_PERIPHCLK ) )

          {
            break;      /* signal still low, poll peripheral for status */
          }
          else
          {
            /*---------------------------------------------------*/
            /* P1284 D2.00 - State 33                            */
            /*                                                   */
            /* Complete the handshake by setting HOSTACK high    */
            /* (HOSTCLK and 1284ACTIVE should still be high)     */
            /*                                                   */
            /* Set the data transfer direction to the device.    */
            /* This puts the data bus in high impedance mode     */
            /* so the host can drive the bus.                    */
            /*---------------------------------------------------*/
            cancelDelayTimer( pInst, TIMER_ID_0 );

            pInst->ControlReg |= SPP_CONTROL_HOSTACK;
            pInst->ControlReg &= ~SPP_CONTROL_DIR;

            IOWrite8( pInst->pIO[0],
                      SPP_CONTROL_REG,
                      (UCHAR)(pInst->ControlReg ^ SPP1284_CONTROL_INVERT));

            TimeExecutionStall(1);

            /*---------------------------------------------------*/
            /* Set the ECP chip from PS2 mode to ECP FIFO mode.  */
            /* Disable FIFO IRQ and ERR interrupts.              */
            /*                                                   */
            /* Protocol is now in forward idle mode.             */
            /*---------------------------------------------------*/
            pInst->ExtControlReg &= ~(EXT_CONTROL_MODE | EXT_CONTROL_FIFOIRQ);
            pInst->ExtControlReg |=  (EXT_CONTROL_MODE3 | EXT_CONTROL_NOERRIRQ);

            IOWrite8( pInst->pIO[1],
                      EXT_CONTROL_REG,
                      (UCHAR)(pInst->ExtControlReg ^ EXT_CONTROL_INVERT));

            pInst->State = NEGS_COMPLETE;

          }
          break;

        /*---------------------------------------------------*/
        /* P1284 D2.00 - State  0                            */
        /*                                                   */
        /* If the negotiation failed in some previous state  */
        /* the parallel port interface must be set back to   */
        /* compatibility mode so that the next negotiatation */
        /* can succeed.                                      */
        /*---------------------------------------------------*/
        case NEGS_STATE_0:
          pInst->ControlReg |= (SPP_CONTROL_HOSTCLK | SPP_CONTROL_HOSTBUSY);
          pInst->ControlReg &= ~(SPP_CONTROL_1284ACTIVE);

          IOWrite8( pInst->pIO[0],
                    SPP_CONTROL_REG,
                    ((UCHAR)(pInst->ControlReg ^ SPP1284_CONTROL_INVERT)));

          pInst->State = NEGS_COMPLETE;

          break;

        /*---------------------------------------------------*/
        /* Negotiation Complete                              */
        /*                                                   */
        /* If the negotiation failed in some previous state  */
        /* we set an invalid current CommMode to force       */
        /* a new negotiation. Otherwise, we assume the       */
        /* caller set the new CommMode.                      */
        /*---------------------------------------------------*/
        case NEGS_COMPLETE:
           cancelDelayTimer( pInst, TIMER_ID_0 );
           cancelDelayTimer( pInst, TIMER_ID_1 );

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

          fWaitState = 1;
          break;
      }
    }
    while ( !fWaitState );

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

  /* Note: We must remain locked here until we done */
  /*       with pInst->State.                       */

  if ( pInst->State == NEGS_COMPLETE )
  {
    UnLockInstance( pInst );
    /*----------------------------------------------*/
    /* Async callback to originator of negotiation. */
    /*----------------------------------------------*/
    (*pInst->DoneRtn)( pInst );
  }
  else
  {
    UnLockInstance( pInst );
  }
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  negotiate_timeout                                */
/*                                                                    */
/* DESCRIPTIVE NAME:  Timeout routine for the negotiation phase       */
/*                                                                    */
/* FUNCTION:  The function of this routine is to set the timeout      */
/*            return code and set the state to state 0 when the       */
/*            device fails to complete the current state.             */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Interrupt Time                                            */
/*                                                                    */
/* ENTRY POINT:  negotiate_timeout                                    */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  parInstance_t   pInst    - ptr to adapter instance         */
/*                                                                    */
/* EXIT-NORMAL: N/A                                                   */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS: pInst->State, pInst->ReturnCode                           */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void negotiate_timeout( parInstance_t *pInst )
{
#ifdef DEBUG
  dprintf( "par1284.sys: negotiate_timeout\r\n");
#endif

  pInst->State = NEGS_STATE_0;
  pInst->ReturnCode = RC_TIMEOUT;

}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  ecp_setup_timeout                                */
/*                                                                    */
/* DESCRIPTIVE NAME:  Timeout routine for ECP setup phase             */
/*                                                                    */
/* FUNCTION:  The function of this routine is to set the timeout      */
/*            state when the device fails to finish the setup         */
/*            handshaking for ECP mode.                               */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Interrupt Time                                            */
/*                                                                    */
/* ENTRY POINT:  ecp_setup_timeout                                    */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  parInstance_t   pInst    - ptr to adapter instance         */
/*                                                                    */
/* EXIT-NORMAL: N/A                                                   */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS: pInst->State, pInst->ReturnCode                           */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void ecp_setup_timeout( parInstance_t *pInst )
{
#ifdef DEBUG
  dprintf( "par1284.sys: ecp_setup_timeout\r\n");
#endif

  pInst->State = NEGS_COMPLETE;
  pInst->ReturnCode = RC_TIMEOUT;

}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  CommModeToExtReq                                 */
/*                                                                    */
/* DESCRIPTIVE NAME:  Convert the communication mode to an            */
/*                    extensibility request byte.                     */
/*                                                                    */
/* FUNCTION:  The function of this routine is to convert the          */
/*            communication mode to an extensibility request byte.    */
/*            This routine enforces checks to determine whether the   */
/*            hardware adequately supports the requested mode.        */
/*                                                                    */
/* NOTES:  The extensibility request byte is the byte sent to the     */
/*         device to request the device communicate bidirectionally   */
/*         using a particular protocol (nibble, byte, ecp, etc.).     */
/*                                                                    */
/* CONTEXT: Task or Interrupt Time                                    */
/*                                                                    */
/* ENTRY POINT:  CommModeToExtReq                                     */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  parInstance_t   pInst    - ptr to adapter instance         */
/*         UCHAR           ReqCommMode - requested communication mode */
/*         UCHAR           *pExtReqByte - ptr to ext req byte returned*/
/*                                                                    */
/* EXIT-NORMAL: FALSE                                                 */
/*                                                                    */
/* EXIT-ERROR:  TRUE - unsupported communication mode                 */
/*                                                                    */
/* EFFECTS: pExtReqByte                                               */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
USHORT CommModeToExtReq( parInstance_t *pInst,
                         UCHAR         ReqCommMode,
                         UCHAR         *pExtReqByte )
{
#ifdef DEBUG
  dprintf( "par1284.sys: CommModeToExtReq\r\n" );
#endif

  switch ( ReqCommMode )
  {
    case PAR_COMPATIBILITY:
#ifdef DEBUG
  dprintf( "par1284.sys: Negotiate Compatibility Mode\r\n" );
#endif
      *pExtReqByte = -1;
      break;

    case PAR_NIBBLE:
#ifdef DEBUG
  dprintf( "par1284.sys: Negotiate Nibble Mode\r\n" );
#endif
      *pExtReqByte =  NEGOTIATE_NIBBLE_MODE;
      break;

    case PAR_BYTE:
#ifdef DEBUG
  dprintf( "par1284.sys: Negotiate Byte Mode\r\n" );
#endif
      *pExtReqByte =  NEGOTIATE_BYTE_MODE;
      break;

    case PAR_EPP:
#ifdef DEBUG
  dprintf( "par1284.sys: Negotiate EPP Mode\r\n" );
#endif
      if ( !(pInst->Flags & F_EPP_SUPPORT) )
      {
#ifdef DEBUG
  dprintf( "par1284.sys: EPP Mode Not Supported\r\n" );
#endif
        return( 1 );
      }
      *pExtReqByte =  NEGOTIATE_EPP_MODE;
      break;

  case PAR_ECP_WITHOUT_RLE:
#ifdef DEBUG
dprintf( "par1284.sys: Negotiate ECP Without RLE Mode\r\n" );
#endif
    if ( !(pInst->Flags & F_FIFO_SUPPORT) )
    {
#ifdef DEBUG
dprintf( "par1284.sys: ECP Mode Not Supported without FIFO.\r\n" );
#endif
      return( 1 );
    }
    *pExtReqByte =  NEGOTIATE_ECP_MODE;
    break;

    case PAR_ECP_WITH_RLE:
#ifdef DEBUG
  dprintf( "par1284.sys: Negotiate ECP With RLE Mode\r\n" );
#endif
      if ( !(pInst->Flags & F_FIFO_SUPPORT) )
      {
#ifdef DEBUG
  dprintf( "par1284.sys: ECP Mode Not Supported without FIFO.\r\n" );
#endif
        return( 1 );
      }
      *pExtReqByte =  NEGOTIATE_ECP_RLE_MODE;
      break;

    default:
#ifdef DEBUG
  dprintf( "par1284.sys: CommModeToExtReq - Unknown Comm Mode\r\n" );
#endif
      return( 1 );
  }

  return( 0 );
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  negotiate_mode                                   */
/*                                                                    */
/* DESCRIPTIVE NAME:  Set the communication mode.                     */
/*                                                                    */
/* FUNCTION:  The function of this routine is to set the parallel     */
/*            communication mode. The routine validates that the      */
/*            chipset hardware supports the requested CommMode and    */
/*            then negotiates (IEEE-1284) with the device to insure   */
/*            the device supports the requested CommMode.             */
/*                                                                    */
/* NOTES:  ReqCommMode is assumed valid when negotiate_mode is called.*/
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  negotiate_mode                                       */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  parInstance_t   pInst    - ptr to adapter instance         */
/*         UCHAR           ReqCommMode - requested communication mode */
/*                                                                    */
/* EXIT-NORMAL: N/A                                                   */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS: pInst->ExtReqByte, pInst->CurCommMode, pInst->ReturnCode  */
/*                                                                    */
/* INTERNAL REFERENCES:  CommModeToExtReq                             */
/*                       do_negotiate                                 */
/*                       parBlockThread                               */
/*                       SetRPErrorCode                               */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void negotiate_mode( parInstance_t *pInst, UCHAR ReqCommMode )
{
  UCHAR         CommMode = ReqCommMode;
  BOOL          fDeviceID = FALSE;

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

  switch ( pInst->pRP->Cmd )
  {
    /*----------------------------------------------*/
    /* Read strategy command.                       */
    /*----------------------------------------------*/
    case CMDINPUT:

      if ( ReqCommMode == PAR_COMPATIBILITY )
      {
        pInst->ReturnCode = RC_INVALID_MODE;
        SetRPErrorCode( pInst );
        return;
      }
      else
      {
        if ( ReqCommMode == pInst->CurCommMode )
        {
          pInst->pRP->Status = STATUS_DONE;
          return;
        }
      }

      break;

    /*----------------------------------------------*/
    /* Write strategy command.                      */
    /*----------------------------------------------*/
    case CMDOUTPUT:

      /*------------------------------------------------*/
      /* For NIBBLE and BYTE CommModes, the actual mode */
      /* in the forward direction is PAR_COMPATIBILITY. */
      /*------------------------------------------------*/
      if ( ReqCommMode == PAR_NIBBLE ||
           ReqCommMode == PAR_BYTE )
      {
        CommMode = PAR_COMPATIBILITY;
        break;
      }
      else
      {
        if ( ReqCommMode == pInst->CurCommMode )
        {
          pInst->pRP->Status = STATUS_DONE;
          return;
        }
      }

      break;

    /*----------------------------------------------*/
    /* Generic IOCtl strategy command.              */
    /*----------------------------------------------*/
    case CMDGenIOCTL:

      /*----------------------------------------------*/
      /* set communication mode IOCtl.                */
      /*----------------------------------------------*/
      if ( ((PRP_GENIOCTL)pInst->pRP)->Function == IOLW_CM )
      {
        break;
      }
      /*----------------------------------------------*/
      /* query device id IOCtl.                       */
      /*----------------------------------------------*/
      else if ( ((PRP_GENIOCTL)pInst->pRP)->Function == IOLR_ID )
      {
        /*----------------------------------------------*/
        /* IEEE-1284 spec supports querying device id   */
        /* in nibble, byte, and ECP modes only.         */
        /*----------------------------------------------*/
        if ( ( ReqCommMode <= PAR_EPP ) &&
             ( ReqCommMode != PAR_COMPATIBILITY ) )
        {
          fDeviceID = TRUE;
          break;
        }
        /*----------------------------------------------*/
        /* IEEE-1284 spec requires the interface to be  */
        /* returned to compatibility mode after the     */
        /* device id is queried.                        */
        /*----------------------------------------------*/
        else if ( ( ReqCommMode == PAR_COMPATIBILITY ) &&
                  ( pInst->ExtReqByte & NEGOTIATE_DEVICE_ID ) &&
                  ( pInst->ExtReqByte != 0xFF ) )
             {
               break;
             }
             else
             {
               pInst->ReturnCode = RC_INVALID_MODE;
               SetRPErrorCode( pInst );
               return;
             }
      }

      break;

    default :
      pInst->ReturnCode = RC_INVALID_MODE;
      SetRPErrorCode( pInst );
      return;

  }

  /*-------------------------------------------------*/
  /* Convert the requested communication mode to its */
  /* extensibility request byte.                     */
  /*-------------------------------------------------*/
  if ( !CommModeToExtReq( pInst,
                          CommMode,
                          &pInst->ExtReqByte ) )
  {
    /*-------------------------------------------------*/
    /* Negotiate with the device id bit set.           */
    /*-------------------------------------------------*/
    if (fDeviceID == TRUE)
      pInst->ExtReqByte |= NEGOTIATE_DEVICE_ID;

    /*----------------------------------------------*/
    /* Reset status fields.                         */
    /*----------------------------------------------*/
    pInst->pRP->Status = 0;
    pInst->ReturnCode = 0;

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

    /*----------------------------------------------*/
    /* Block thread until request completes.        */
    /*----------------------------------------------*/
    parBlockThread( pInst );

    /*-------------------------------------------------*/
    /* If negotiation completed successfully, update   */
    /* current communication mode in adapter instance. */
    /*-------------------------------------------------*/
    if ( pInst->pRP->Status == STATUS_DONE )
      pInst->CurCommMode = CommMode;

    return;
  }
  else
    pInst->ReturnCode = RC_INVALID_MODE;

  /*----------------------------------------------*/
  /* Map internal return code to request packet   */
  /* return code (sets the done bit).             */
  /*----------------------------------------------*/
  SetRPErrorCode( pInst );
  return;
}


