/* SCCSID = "%W% %E%" */
/****************************************************************************/
/*                                                                          */
/*                           IBM Confidential                               */
/*                                                                          */
/*                 Copyright (c) IBM Corporation 1996                       */
/*                           All Rights Reserved                            */
/*                                                                          */
/****************************************************************************/
/************************** START OF SPECIFICATIONS ***************************/
/*                                                                            */
/*   SOURCE FILE NAME:  PARECP.C                                              */
/*                                                                            */
/*   DESCRIPTIVE NAME:  PARALLEL port device driver ECP routines.             */
/*                                                                            */
/*   FUNCTION: These routines handle the read and write functions when the    */
/*             ECP parallel port chip is set to ECP FIFO mode.                */
/*                                                                            */
/*   NOTES:  These routines do not provide software emulation of ECP hardware */
/*           protocols.                                                       */
/*                                                                            */
/*      DEPENDENCIES: None                                                    */
/*      RESTRICTIONS: None                                                    */
/*                                                                            */
/*   ENTRY POINTS:                                                            */
/*             do_read_ecp_mode                                               */
/*             service_ecp_read                                               */
/*             ecp_read_timeout                                               */
/*             do_write_ecp_mode                                              */
/*             service_ecp_write                                              */
/*             ecp_write_timeout                                              */
/*                                                                            */
/*   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_read_ecp_mode                                 */
/*                                                                    */
/* DESCRIPTIVE NAME:  Read data in ECP FIFO mode from the device.     */
/*                                                                    */
/* FUNCTION:  The function of this routine is to read data in ECP     */
/*            FIFO mode from the device.  A design assumption was     */
/*            that there would be more write requests than read       */
/*            requests so leave the interface in forward idle phase   */
/*            and occur the overhead of direction change (both ways)  */
/*            on the read request.                                    */
/*                                                                    */
/*            This routine assumes the following:                     */
/*                                                                    */
/*            (1) The device is already in ECP mode.                  */
/*            (2) The protocol is already in forward idle state.      */
/*            (3) The data bus is already in forward direction.       */
/*            (4) The ECP chip is already in ECP FIFO mode.           */
/*                                                                    */
/*            This routine performs the following steps:              */
/*                                                                    */
/*            (1) put ECP chip into PS2 mode                          */
/*            (2) put data bus in reverse direction                   */
/*            (3) put ECP chip into ECP FIFO mode                     */
/*            (4) perform Fwd2Rev phase (states 39, 40)               */
/*            (5) start intercharacter and request timers             */
/*            (6) setup for polled or interrupt driven transfer       */
/*            (7) call service_ecp_read to read from FIFO             */
/*            (8) at timeout, empty FIFO                              */
/*            (9) perform Rev2Fwd phase (states 47, 49)               */
/*            (10) put ECP chip into PS2 mode                         */
/*            (11) put data bus in forward direction                  */
/*            (12) put ECP chip into ECP FIFO mode                    */
/*                                                                    */
/* NOTES: The data bus direction can only be changed when the ECP     */
/*        chip is in PS2 mode.                                        */
/*        Any data in the ECP FIFO when the chip mode is changed      */
/*        will be lost.                                               */
/*        Unlike nibble/byte modes, direction can be reversed without */
/*        a re-negotiation with the device.                           */
/*        An exit from ECP mode cannot occur from reverse direction.  */
/*                                                                    */
/* CONTEXT: task or interrupt time                                    */
/*                                                                    */
/* ENTRY POINT:  do_read_ecp_mode                                     */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  parInstance_t   pInst    - ptr to adapter instance         */
/*                                                                    */
/* EXIT-NORMAL: N/A                                                   */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS: pInst->StatusReg, pInst->ControlReg, pInst->ExtControlReg */
/*          pInst->ReturnCode, pInst->State, pInst->IRQRtn            */
/*                                                                    */
/* INTERNAL REFERENCES:  LockInstance                                 */
/*                       UnLockInstance                               */
/*                       SetInstanceFlags                             */
/*                       ResetInstanceFlags                           */
/*                       IORead8                                      */
/*                       IOWrite8                                     */
/*                       TimeExecutionStall                           */
/*                       SetDelayTimer                                */
/*                       service_ecp_read                             */
/*                       cancelAllTimers                              */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void do_read_ecp_mode( parInstance_t *pInst )
{
  USHORT                fWaitState;
  USHORT                n;

  /*-------------------------------------------------*/
  /* 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 )
      {
        case RECP_INIT:
#ifdef DEBUGECP
  dprintf( "RECP_INIT\r\n");
#endif
          pInst->DataCount = 0;
          ResetInstanceFlags( pInst, F_TIMEOUT );

          pInst->ControlReg |= SPP_CONTROL_TWOHIGHBITS; /* HP ECP Spec. p.32 */

          /*---------------------------------------------------*/
          /* Copy any bytes that were in the FIFO at the end   */
          /* of the last read from our temporary buffer to the */
          /* user's buffer.                                    */
          /*---------------------------------------------------*/
          n = 0;
          while ( n < pInst->pLastRead->usCharCount )
          {
            pInst->pDataBuf[pInst->DataCount] =
              pInst->pLastRead->ucCharBuffer[n];
            pInst->DataCount++;
            n++;
          }
          pInst->pLastRead->usCharCount = 0;

          /*---------------------*/
          /* Get device status   */
          /*---------------------*/
          pInst->StatusReg  = IORead8( pInst->pIO[0], SPP_STATUS_REG );
          pInst->StatusReg ^= SPP1284_ECP_STATUS_INVERT;

          /*-----------------------------------------------*/
          /* if device has no data to send, then exit.     */
          /*-----------------------------------------------*/
          if ( (pInst->StatusReg & SPP_STATUS_PERIPHREQ) == 0 )
          {
            pInst->State = RECP_COMPLETE;
            break;
          }

          /*---------------------------------------------------*/
          /* Set the ECP chip to mode 001 (the ONLY mode for   */
          /* direction reversal).                              */
          /*---------------------------------------------------*/
          pInst->ExtControlReg &= ~(EXT_CONTROL_MODE);
          pInst->ExtControlReg |=  EXT_CONTROL_MODE1;

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

          TimeExecutionStall(1);

          /*---------------------------------------------------*/
          /* Set the data transfer direction towards the host. */
          /* This puts the data bus in high impedance mode so  */
          /* the peripheral can drive the bus.                 */
          /*                                                   */
          /* The chipset ECP logic uses this bit to determine  */
          /* the data transfer direction.                      */
          /*---------------------------------------------------*/
          pInst->ControlReg |= SPP_CONTROL_DIR;

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

          TimeExecutionStall(1);

          /*---------------------------------------------------*/
          /* Set the ECP chip to mode 003 (ECP mode).          */
          /*---------------------------------------------------*/
          pInst->ExtControlReg &= ~(EXT_CONTROL_MODE);
          pInst->ExtControlReg &= ~EXT_CONTROL_FIFOIRQ;
          pInst->ExtControlReg |= EXT_CONTROL_MODE3;

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

          TimeExecutionStall(2);    /* 1 usec required wait (Lexmark eng.) */

          /*-----------------------------------------------------*/
          /* P1284 D2.00 - State 39                              */
          /*                                                     */
          /* Set REVERSEREQ low (Our flag high, we invert,       */
          /* so register is 0, bus follows register).             */
          /*-----------------------------------------------------*/
          pInst->ControlReg |= SPP_CONTROL_REVERSEREQ;

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

          pInst->State = RECP_WAIT_REVERSE;

          break;

        /*---------------------------------------------------*/
        /* P1284 D2.00 - State 40                            */
        /*                                                   */
        /* Wait for the device to acknowledge the reverse    */
        /* channel request by setting ACKREVERSE low.        */
        /*---------------------------------------------------*/
        case RECP_WAIT_REVERSE:
#ifdef DEBUGECP
  dprintf( "RECP_WAIT_REVERSE\r\n");
#endif
          /*---------------------*/
          /* Get device status   */
          /*---------------------*/
          pInst->StatusReg  = IORead8( pInst->pIO[0], SPP_STATUS_REG );
          pInst->StatusReg ^= SPP1284_ECP_STATUS_INVERT;

          /*-----------------------------------------------*/
          /* Check if device is ready for reverse transfer */
          /*-----------------------------------------------*/
          if ( (pInst->StatusReg & SPP_STATUS_ACKREVERSE) )
          {
            SetInstanceFlags( pInst, F_DIR_REVERSE);

            pInst->State = RECP_REV_IDLE;
          }
          else
          {
            /*-----------------------------------------------------*/
            /* Device has not responded to the reverse request.    */
            /* Set REVERSEREQ high (stop requesting to reverse     */
            /* the bus).                                           */
            /*-----------------------------------------------------*/
            pInst->ControlReg &= ~SPP_CONTROL_REVERSEREQ;

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

            pInst->State = RECP_COMPLETE;
          }

          break;

        /*-------------------------------------------------------*/
        /* P1284 D2.00 - State 43-46                             */
        /*                                                       */
        /* At the point we begin servicing the chipset's         */
        /* data transfer requests via interrupt or polled        */
        /* mode.                                                 */
        /*                                                       */
        /* The chipset will perform ECP transfers asynchronously */
        /* and will signal the host when the FIFO fills to its   */
        /* interrupt threshold.                                  */
        /*-------------------------------------------------------*/
        case RECP_REV_IDLE:
#ifdef DEBUGECP
  dprintf( "RECP_REV_IDLE\r\n");
#endif
          if ( pInst->RIdleTimeoutMS )    /* set the request timeout. */
          {
            setDelayTimer( pInst,
                           TIMER_ID_2,
                           ecp_read_timeout,
                           pInst->RIdleTimeoutMS );
          }

          if ( pInst->RTimeoutMS )        /* set the intercharacter timeout. */
          {
            setDelayTimer( pInst,
                           TIMER_ID_1,
                           ecp_read_timeout,
                           pInst->RTimeoutMS * pInst->FIFOReadThresh );
          }

          /*-----------------------------------------------------*/
          /* If the length of the next transfer will not satisfy */
          /* the FIFO threshold or we are not using interrupts   */
          /* then setup for polling the FIFO status.             */
          /*-----------------------------------------------------*/
          if ( (pInst->ReqDataCount < pInst->FIFOReadThresh ) ||
               (pInst->Flags & F_NO_INTERRUPT) )
          {
            pInst->State = RECP_POLLED;

            /*-----------------------------------------------*/
            /* Turn off the FIFO threshold interrupt.        */
            /*-----------------------------------------------*/
            pInst->ExtControlReg &= ~EXT_CONTROL_FIFOIRQ;

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

          }
          /*-----------------------------------------------*/
          /* Setup for interrupt driven operation.         */
          /*-----------------------------------------------*/
          else
          {
            pInst->State    = RECP_INTERRUPT;
            pInst->IRQRtn   = do_read_ecp_mode;
          }
          break;

        /*-----------------------------------------------*/
        /* Handle interrupt mode transfers.              */
        /*-----------------------------------------------*/
        case RECP_INTERRUPT:
#ifdef DEBUGECP
  dprintf( "RECP_INTERRUPT\r\n");
#endif
          /*-----------------------------------------------*/
          /* Turn off the FIFO threshold interrupt.        */
          /*-----------------------------------------------*/
          pInst->ExtControlReg &= ~EXT_CONTROL_FIFOIRQ;

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

          service_ecp_read( pInst );

          if ( pInst->DataCount >= pInst->ReqDataCount )
          {
            pInst->State = RECP_XFER_COMPLETE;
            break;
          }

          /*-----------------------------------------------*/
          /* If the amount of data remaining to be read    */
          /* does not exceed the FIFO read threshold, then */
          /* revert to polling mode for the last transfer. */
          /*-----------------------------------------------*/
          n = pInst->ReqDataCount - pInst->DataCount;

          if ( n < pInst->FIFOReadThresh )
          {
            pInst->State = RECP_POLLED;
            break;
          }

          /*-----------------------------------------------*/
          /* Turn on the FIFO threshold interrupt.         */
          /*-----------------------------------------------*/
          SetInstanceFlags( pInst, F_INTERRUPT_EXPECTED );

          pInst->ExtControlReg |= EXT_CONTROL_FIFOIRQ;

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

          fWaitState = 1;

          break;

        /*-----------------------------------------------*/
        /* Handle polled mode transfers.                 */
        /*                                               */
        /* The FIFO status is checked every timer tick   */
        /* to determine whether data has arrived.        */
        /*-----------------------------------------------*/
        case RECP_POLLED:
#ifdef DEBUGECP
  dprintf( "do_read_ecp_mode - RECP_POLLED\r\n");
#endif
          service_ecp_read( pInst );

          if ( pInst->DataCount >= pInst->ReqDataCount )
          {
            pInst->State = RECP_XFER_COMPLETE;
            break;
          }

          setDelayTimer( pInst,
                         TIMER_ID_0,
                         do_read_ecp_mode,
                         (USHORT)DELAY_OS_MIN );

          fWaitState = 1;

          break;

        /*-----------------------------------------------*/
        /* ECP read data transfer is complete.           */
        /*                                               */
        /* Either the user's buffer is full or a timeout */
        /* occurred.                                     */
        /*-----------------------------------------------*/
        case RECP_XFER_COMPLETE:
#ifdef DEBUGECP
  dprintf( "RECP_XFER_COMPLETE\r\n");
#endif
          cancelAllTimers( pInst );
          ResetInstanceFlags( pInst, F_INTERRUPT_EXPECTED | F_TIMEOUT );

          /*-----------------------------------------------*/
          /* P1284 D2.00 - State 47                        */
          /*                                               */
          /* Set REVERSEREQ low to switch the channel to   */
          /* the forward direction.                        */
          /*-----------------------------------------------*/
          pInst->ControlReg &= ~(SPP_CONTROL_REVERSEREQ);

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

          pInst->State = RECP_WAIT_FORWARD;

          break;

        /*-----------------------------------------------*/
        /* P1284 D2.00 - State 49                        */
        /*                                               */
        /* Wait for the device to acknowledge the        */
        /* forward channel request by setting ACKREVERSE */
        /* high (clear).                                 */
        /*                                               */
        /* If the device is not ready to reverse the     */
        /* channel, remain in this state and poll.       */
        /*-----------------------------------------------*/
        case RECP_WAIT_FORWARD:
#ifdef DEBUGECP
  dprintf( "RECP_WAIT_FORWARD \r\n");
#endif
          /*---------------------*/
          /* Get device status   */
          /*---------------------*/
          pInst->StatusReg  = IORead8( pInst->pIO[0], SPP_STATUS_REG );
          pInst->StatusReg ^= SPP1284_ECP_STATUS_INVERT;

          /*-----------------------------------------------*/
          /* If not ready for forward transfer, wait.      */
          /*-----------------------------------------------*/
          if ( (pInst->StatusReg & SPP_STATUS_ACKREVERSE) ||
              !(pInst->StatusReg & SPP_STATUS_PERIPHCLK ) )
          {
            setDelayTimer( pInst,
                           TIMER_ID_0,
                           do_read_ecp_mode,
                           (USHORT)DELAY_OS_MIN  );

            fWaitState = 1;

            break;
          }
          else
          {
            ResetInstanceFlags( pInst, F_DIR_REVERSE );

            /*-----------------------------------------------*/
            /* The device is now in the forward idle phase.  */
            /* The device cannot send more data to the FIFO. */
            /*                                               */
            /* The data bus direction must now be set to     */
            /* forward direction.  To set the data bus to    */
            /* the forward direction requires the ECP chip   */
            /* to be reprogrammed to PS2 mode.  This will    */
            /* flush any remaining data bytes from the FIFO. */
            /*                                               */
            /* In order to maintain data integrity, read any */
            /* remaining bytes from the FIFO before setting  */
            /* the data bus to the forward direction.        */
            /*-----------------------------------------------*/
            pInst->ExtControlReg  = IORead8( pInst->pIO[1], EXT_CONTROL_REG );
            pInst->ExtControlReg ^= EXT_CONTROL_INVERT;

            if ( (pInst->ExtControlReg & EXT_CONTROL_FIFOEMPTY) == 0 )
            {
              /*-----------------------------------------------*/
              /* If space remaining in user's buffer, empty    */
              /* FIFO to user's buffer.                        */
              /*-----------------------------------------------*/
              if ( (pInst->ReqDataCount - pInst->DataCount) > 0 )
                service_ecp_read(pInst);

              cancelDelayTimer( pInst, TIMER_ID_1 );

              pInst->ExtControlReg  = IORead8( pInst->pIO[1], EXT_CONTROL_REG );
              pInst->ExtControlReg ^= EXT_CONTROL_INVERT;

              if ( (pInst->ExtControlReg & EXT_CONTROL_FIFOEMPTY) == 0 )
              {
                /*-----------------------------------------------*/
                /* Save remaining data to temporary buffer until */
                /* next read is requested.                       */
                /*-----------------------------------------------*/
                n = pInst->DataCount;        /* save user count read */

                pInst->pDataBuf = pInst->pLastRead->ucCharBuffer;
                pInst->ReqDataCount = MAX_FIFO_DEPTH;
                pInst->DataCount = 0;
                service_ecp_read(pInst);
                pInst->pLastRead->usCharCount = pInst->DataCount;

                pInst->DataCount = n;        /* restore user count read */

                cancelDelayTimer( pInst, TIMER_ID_1 );
              }
            }

            /*-----------------------------------------------*/
            /* The data bus direction must now be set to     */
            /* forward direction.  To set the data bus to    */
            /* the forward direction requires the ECP chip   */
            /* to be reprogrammed to PS2 mode.  This will    */
            /* flush any remaining data bytes from the FIFO. */
            /*                                               */
            /* Set the ECP chip to mode 001 (the ONLY mode   */
            /* for direction reversal).                      */
            /*-----------------------------------------------*/
            pInst->ExtControlReg &= ~(EXT_CONTROL_MODE);
            pInst->ExtControlReg |=  EXT_CONTROL_MODE1;

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

            TimeExecutionStall(1);

            /*-----------------------------------------------*/
            /* Set the data transfer direction to the device.*/
            /* This puts the data bus in high impedance mode */
            /* so the host can drive the bus.                */
            /*                                               */
            /* The chipset ECP logic uses this bit to        */
            /* determine the data transfer direction.        */
            /*-----------------------------------------------*/
            pInst->ControlReg &= ~SPP_CONTROL_DIR;

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

            TimeExecutionStall(1);

            /*-----------------------------------------------*/
            /* Set the ECP chip to mode 003 (ECP mode).      */
            /*-----------------------------------------------*/
            pInst->ExtControlReg &= ~(EXT_CONTROL_MODE);
            pInst->ExtControlReg |=  (EXT_CONTROL_MODE3);

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

            pInst->State = RECP_COMPLETE;

            break;
          }

        /*-----------------------------------------------*/
        /* ECP forward idle phase.                       */
        /*-----------------------------------------------*/
        case RECP_COMPLETE:
#ifdef DEBUGECP
  dprintf( "RECP_COMPLETE\r\n");
#endif
          pInst->UseCount = 1;
          fWaitState      = 1;
          break;

      }  /* End of Switch */

      /*-----------------------------------------------*/
      /* Check if the read was cancelled by the user   */
      /*-----------------------------------------------*/
      if ( pInst->Flags & F_CANCELLED )
      {
        pInst->ReturnCode  = RC_CANCELLED;
        pInst->State       = RECP_XFER_COMPLETE;
        fWaitState         = 0;
        break;
      }
    }
    while ( !(fWaitState) );

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

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


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  service_ecp_read                                 */
/*                                                                    */
/* DESCRIPTIVE NAME:  ECP chipset read service routine                */
/*                                                                    */
/* FUNCTION:  The function of this routine is to transfer data from   */
/*            the chipset FIFO until the FIFO status indicates the    */
/*            FIFO is empty.   This routine services the ECP FIFO in  */
/*            both interrupt and polled mode.                         */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT:  Task or interrupt time                                   */
/*                                                                    */
/* ENTRY POINT:  service_ecp_write                                    */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  parInstance_t   pInst    - ptr to adapter instance         */
/*                                                                    */
/* EXIT-NORMAL: N/A                                                   */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS: pInst->ExtControlReg, pInst->DataCount                    */
/*                                                                    */
/* INTERNAL REFERENCES:  cancelDelayTimer                             */
/*                       IORead8                                      */
/*                       SetDelayTimer                                */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void service_ecp_read( parInstance_t *pInst )
{
  USHORT                i=0;
  USHORT                nBytes;
  UCHAR                 far *pData;

#ifdef DEBUGECP
  dprintf( "service_ecp_read entry.\r\n");
#endif

  pData = &pInst->pDataBuf[pInst->DataCount];

  nBytes = pInst->ReqDataCount - pInst->DataCount;

  /*----------------------------------------------------*/
  /* Transfer bytes until the ReqDataCount is satisfied */
  /* or the FIFO is empty.                              */
  /*----------------------------------------------------*/
  while ( nBytes )
  {
    pInst->ExtControlReg  = IORead8( pInst->pIO[1], EXT_CONTROL_REG );
    pInst->ExtControlReg ^= EXT_CONTROL_INVERT;

    if ( pInst->ExtControlReg & EXT_CONTROL_FIFOEMPTY )
      break;

    /*---------------------------------------------*/
    /* Cancel the inter-character timer if we read */
    /* at least one character.                     */
    /*---------------------------------------------*/
    if ( i==1 )
      cancelDelayTimer( pInst, TIMER_ID_1 );

    /*--------------------------------------------------------*/
    /* Note: The chipset does not support distinguishing DATA */
    /*       from ADDRESS bytes on reads.                     */
    /*--------------------------------------------------------*/
    pData[i] = IORead8( pInst->pIO[1], ECP_DFIFO_REG );
    i++;
    nBytes--;
  }

  pInst->DataCount += i;

  /*-------------------------------------------------------------*/
  /* If we cancelled the inter-character timer, then restart it  */
  /* after we emptied the FIFO.                                  */
  /* If we got a character there may be more, so exit and        */
  /* give the FIFO time to fill again and then read it again.    */
  /*-------------------------------------------------------------*/
  if ( i && pInst->RTimeoutMS )
  {
    setDelayTimer( pInst,
                   TIMER_ID_1,
                   ecp_read_timeout,
                   pInst->RTimeoutMS );
  }
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  ecp_read_timeout                                 */
/*                                                                    */
/* DESCRIPTIVE NAME:  Timeout routine for read requests in ECP mode   */
/*                                                                    */
/* FUNCTION:  A timer has expired signifying that the time allotted   */
/*            to receive the next character or group of characters    */
/*            has expired. The purpose of ecp_read_timeout is         */
/*            to set the next state and call do_read_ecp_mode to      */
/*            read any remaining characters out of the FIFO.          */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT:  Interrupt time                                           */
/*                                                                    */
/* ENTRY POINT:  ecp_read_timeout                                     */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  parInstance_t   pInst    - ptr to adapter instance         */
/*                                                                    */
/* EXIT-NORMAL: N/A                                                   */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS: pInst->State                                              */
/*                                                                    */
/* INTERNAL REFERENCES:  do_read_ecp_mode                             */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void ecp_read_timeout( parInstance_t *pInst )
{
#ifdef DEBUGECP
  dprintf( "ecp_read_timeout\r\n");
#endif

  SetInstanceFlags( pInst, F_TIMEOUT );

  if ( pInst->Flags & F_INTERRUPT_EXPECTED )
  {
    ResetInstanceFlags( pInst, F_INTERRUPT_EXPECTED );

    /*-----------------------------------------------*/
    /* Turn off the FIFO threshold interrupt.        */
    /*-----------------------------------------------*/
    pInst->ExtControlReg &= ~EXT_CONTROL_FIFOIRQ;

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

  }

  pInst->State = RECP_XFER_COMPLETE;
  do_read_ecp_mode( pInst );

}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  do_write_ecp_mode                                */
/*                                                                    */
/* DESCRIPTIVE NAME:  Write data in ECP FIFO mode to the device.      */
/*                                                                    */
/* FUNCTION:  The function of this routine is to write data in ECP    */
/*            FIFO mode to the device.  A design assumption was       */
/*            that there would be more write requests than read       */
/*            requests so leave the interface in forward idle phase   */
/*            and occur the overhead of direction change (both ways)  */
/*            on the read request.                                    */
/*                                                                    */
/*            This routine assumes the following:                     */
/*                                                                    */
/*            (1) The device is already in ECP mode.                  */
/*            (2) The protocol is already in forward idle state.      */
/*            (3) The data bus is already in forward direction.       */
/*            (4) The ECP chip is already in ECP FIFO mode.           */
/*                                                                    */
/*            This routine performs the following steps:              */
/*                                                                    */
/*            (1) start intercharacter and request timers             */
/*            (2) setup for polled or interrupt driven transfer       */
/*            (3) call service_ecp_write to write to the FIFO         */
/*            (4) at buffer empty, wait until FIFO empties            */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: task or interrupt time                                    */
/*                                                                    */
/* ENTRY POINT:  do_write_ecp_mode                                    */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  parInstance_t   pInst    - ptr to adapter instance         */
/*                                                                    */
/* EXIT-NORMAL: N/A                                                   */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS: pInst->StatusReg, pInst->ControlReg, pInst->ExtControlReg */
/*          pInst->ReturnCode, pInst->State, pInst->IRQRtn            */
/*                                                                    */
/* INTERNAL REFERENCES:  LockInstance                                 */
/*                       UnLockInstance                               */
/*                       SetInstanceFlags                             */
/*                       ResetInstanceFlags                           */
/*                       IORead8                                      */
/*                       IOWrite8                                     */
/*                       TimeExecutionStall                           */
/*                       SetDelayTimer                                */
/*                       service_ecp_write                            */
/*                       cancelAllTimers                              */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void do_write_ecp_mode( parInstance_t *pInst )
{
  USHORT                fWaitState;
  USHORT                n;

  LockInstance( pInst );

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

  do
  {
    fWaitState = 0;

    UnLockInstance( pInst );

    do
    {
      switch ( pInst->State )
      {
        case WECP_INIT:
#ifdef DEBUGECP
  dprintf( "do_write_ecp_mode: WECP_INIT\r\n" );
#endif
          pInst->DataCount = 0;
          ResetInstanceFlags( pInst, F_TIMEOUT );

          pInst->RLEState  = WECP_RLE_SCAN;
          pInst->ControlReg |= SPP_CONTROL_TWOHIGHBITS; /* HP ECP Spec. p.32 */

          pInst->State = WECP_MODE3_XFER;

          break;

        /*-----------------------------------------------*/
        /* P1284 D2.00 - State 34                        */
        /*                                               */
        /* Enable the hardware ECP data transfer, and    */
        /* Enable FIFO Interrupts.                       */
        /*-----------------------------------------------*/
        case WECP_MODE3_XFER:

          if ( pInst->WIdleTimeoutMS )    /* Set request timeout. */
          {
            setDelayTimer( pInst,
                           TIMER_ID_2,
                           ecp_write_timeout,
                           pInst->WIdleTimeoutMS );
          }

          if ( pInst->WTimeoutMS )        /* Set the inter character timeout. */
          {
            setDelayTimer( pInst,
                           TIMER_ID_1,
                           ecp_write_timeout,
                           pInst->WTimeoutMS * pInst->FIFOWriteThresh );
          }

          /*-----------------------------------------------*/
          /* P1284 D2.00 - State 35-37                     */
          /*                                               */
          /* At the point we begin servicing the chipset's */
          /* data transfer requests via interrupt or polled*/
          /* mode.                                         */
          /*                                               */
          /* The chipset will perform ECP transfers        */
          /* asynchronously and will signal the host when  */
          /* the FIFO empties to its interrupt threshold.  */
          /*-----------------------------------------------*/
          if ( pInst->Flags & F_NO_INTERRUPT )
          {
            pInst->State = WECP_POLLED;

            /*-----------------------------------------------*/
            /* Turn off the FIFO threshold interrupt.        */
            /*-----------------------------------------------*/
            pInst->ExtControlReg &= ~EXT_CONTROL_FIFOIRQ;

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

          /*-----------------------------------------------*/
          /* Setup for interrupt driven operation          */
          /*-----------------------------------------------*/
          else
          {
            pInst->State    = WECP_INTERRUPT;
            pInst->IRQRtn   = do_write_ecp_mode;
          }
          break;

        /*-----------------------------------------------*/
        /* Handle interrupt mode transfers.              */
        /*-----------------------------------------------*/
        case WECP_INTERRUPT:
#ifdef DEBUGECP
  dprintf( "do_write_ecp_mode - WECP_INTERRUPT\r\n");
#endif

          /*-----------------------------------------------*/
          /* Turn off the FIFO threshold interrupt.        */
          /*-----------------------------------------------*/
          pInst->ExtControlReg &= ~EXT_CONTROL_FIFOIRQ;

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

          if ( pInst->DataCount >= pInst->ReqDataCount )
          {
            pInst->State = WECP_WAIT_FIFO_EMPTY;
            break;
          }

          n = pInst->ReqDataCount - pInst->DataCount;

          if ( n < pInst->FIFOWriteThresh )
          {
            pInst->State = WECP_POLLED;
            break;
          }

          service_ecp_write( pInst );

          /*-----------------------------------------------*/
          /* Turn on the FIFO threshold interrupt.         */
          /*-----------------------------------------------*/
          SetInstanceFlags( pInst, F_INTERRUPT_EXPECTED );

          pInst->ExtControlReg |= EXT_CONTROL_FIFOIRQ;

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

          fWaitState = 1;

          break;

        /*-----------------------------------------------*/
        /* Handle polled mode transfers.                 */
        /*-----------------------------------------------*/
        case WECP_POLLED:
#ifdef DEBUGECP
  dprintf( "do_write_ecp_mode - WECP_POLLED\r\n");
#endif
          if ( pInst->DataCount >= pInst->ReqDataCount )
          {
            pInst->State = WECP_WAIT_FIFO_EMPTY;
            break;
          }

          service_ecp_write( pInst );

          setDelayTimer( pInst,
                         TIMER_ID_0,
                         do_write_ecp_mode,
                         (USHORT)DELAY_OS_MIN );

          fWaitState = 1;

          break;

        /*-----------------------------------------------*/
        /* Poll for FIFO Empty.                          */
        /*-----------------------------------------------*/
        case WECP_WAIT_FIFO_EMPTY:
#ifdef DEBUGECP
  dprintf( "do_write_ecp_mode - WECP_WAIT_FIFO_EMPTY\r\n");
#endif
          pInst->ExtControlReg  = IORead8( pInst->pIO[1], EXT_CONTROL_REG );
          pInst->ExtControlReg ^= EXT_CONTROL_INVERT;

          if ( pInst->ExtControlReg & EXT_CONTROL_FIFOEMPTY )
          {
            pInst->State  = WECP_COMPLETE;
            break;
          }

          setDelayTimer( pInst,
                         TIMER_ID_0,
                         do_write_ecp_mode,
                         (USHORT)DELAY_OS_MIN );

          pInst->State  = WECP_COMPLETE;

          fWaitState = 1;

          break;

        /*-----------------------------------------------*/
        /* Write ECP Timeout                             */
        /*-----------------------------------------------*/
        case WECP_TIMEOUT:
          SetInstanceFlags( pInst, F_TIMEOUT);
          pInst->ReturnCode  = RC_TIMEOUT;
          pInst->State       = WECP_COMPLETE;
          break;

        /*-----------------------------------------------*/
        /* ECP Write Complete                            */
        /*-----------------------------------------------*/
        case WECP_COMPLETE:
#ifdef DEBUGECP
  dprintf( "do_write_ecp_mode - WECP_COMPLETE\r\n");
#endif
          cancelAllTimers( pInst );
          ResetInstanceFlags( pInst, F_INTERRUPT_EXPECTED | F_TIMEOUT );
          pInst->UseCount = 1;
          fWaitState      = 1;
          break;

      }  /* END of switch statement. */

      /*-----------------------------------------------*/
      /* Check if the write was cancelled              */
      /*-----------------------------------------------*/
      if ( pInst->Flags & F_CANCELLED )
      {
        pInst->ReturnCode  = RC_CANCELLED;
        pInst->State       = WECP_COMPLETE;
        fWaitState         = 0;
        break;
      }
    }
    while ( !(fWaitState) );

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

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

}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  service_ecp_write                                */
/*                                                                    */
/* DESCRIPTIVE NAME:  ECP chipset write service routine               */
/*                                                                    */
/* FUNCTION:  The function of this routine is to transfer data to the */
/*            chipset FIFO until the FIFO status indicates the FIFO   */
/*            is full.   This routine services the ECP FIFO in both   */
/*            interrupt and polled mode.                              */
/*                                                                    */
/*            This routine can optionally perform RLE compression on  */
/*            the user's write data.  In this case, the RLE count is  */
/*            output to the address FIFO which causes the chipset to  */
/*            tag the count as a command.                             */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT:  Task or interrupt time                                   */
/*                                                                    */
/* ENTRY POINT:  service_ecp_write                                    */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  parInstance_t   pInst    - ptr to adapter instance         */
/*                                                                    */
/* EXIT-NORMAL: N/A                                                   */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS: pInst->RLEState, pInst->RLEData, pInst->RLECount          */
/*          pInst->ExtControlReg, pInst->DataCount                    */
/*                                                                    */
/* INTERNAL REFERENCES:  cancelDelayTimer                             */
/*                       IORead8                                      */
/*                       IOWrite8                                     */
/*                       SetDelayTimer                                */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void service_ecp_write( parInstance_t *pInst )
{
  USHORT        i = 0;
  USHORT        j;
  USHORT        l;
  USHORT        nBytes;
  UCHAR         far *pData;

#ifdef DEBUGECP
  dprintf( "service_ecp_write\r\n");
#endif
  pData = &pInst->pDataBuf[pInst->DataCount];
  nBytes = pInst->ReqDataCount - pInst->DataCount;

  while ( nBytes )
  {
    pInst->ExtControlReg  = IORead8( pInst->pIO[1], EXT_CONTROL_REG );
    pInst->ExtControlReg ^= EXT_CONTROL_INVERT;

    if ( pInst->ExtControlReg & EXT_CONTROL_FIFOFULL )
      break;

    if ( i==1 )   /* Cancel single character write timeout.  */
      cancelDelayTimer( pInst, TIMER_ID_1 );

    switch ( pInst->RLEState )
    {
      /*--------------------------------------------------------*/
      /* Scan the user buffer at the current character position */
      /* for repeated characters.                               */
      /*--------------------------------------------------------*/
      case WECP_RLE_SCAN:
        if ( !(pInst->Flags & F_RLE_COMPRESS) )
        {
          pInst->RLEState = WECP_SEND_DATA;
          break;
        }

        /*-----------------------------------------------------------*/
        /* A maximum RLE string count of 127 (actually 128) repeated */
        /* chars is supported by IEEE1284.                           */
        /*-----------------------------------------------------------*/
        l = ( nBytes > 128 ) ? 128 : nBytes;

        for ( j=1; j < l; j++ )
        {
          if ( pData[i] != pData[i+j] )
          {
            break;
          }
        }

        j--;

        /*----------------------------------------------------------*/
        /* j contains the length of the RLE string - 1              */
        /*                                                          */
        /* Now check if the string is long enough to bother sending */
        /* as a RLE sequence                                        */
        /*----------------------------------------------------------*/
        if ( j > MIN_RLE_COUNT )
        {
          pInst->RLECount = j;
          pInst->RLEData  = pData[i];
          pInst->RLEState = WECP_SEND_RLE_COUNT;
        }
        else
        {
          pInst->RLEState = WECP_SEND_DATA;
        }
        break;

      /*-----------------------------------------------------------*/
      /* Send the RLE Count to peripheral as an ECP 'Command' byte */
      /*-----------------------------------------------------------*/
      case WECP_SEND_RLE_COUNT:

        IOWrite8( pInst->pIO[0], ECP_AFIFO_REG, (UCHAR)pInst->RLECount );

        pInst->RLEState = WECP_SEND_RLE_DATA;
        break;

      /*-----------------------------------------------*/
      /* Send the RLE Data as a normal ECP 'Data' byte */
      /*-----------------------------------------------*/
      case WECP_SEND_RLE_DATA:

        IOWrite8( pInst->pIO[1], ECP_DFIFO_REG, pInst->RLEData );

        /*------------------------------------------------------*/
        /* Bump the counters to account for the number of bytes */
        /* in the RLE string                                    */
        /*------------------------------------------------------*/
        i           += pInst->RLECount+1;
        nBytes      -= pInst->RLECount+1;

        pInst->RLEState   = WECP_RLE_SCAN;

        break;

      /*-------------------*/
      /* Send non-RLE data */
      /*-------------------*/
      case WECP_SEND_DATA:
        IOWrite8( pInst->pIO[1], ECP_DFIFO_REG, pData[i] );
        i++;
        nBytes--;

        /*------------------------------------------------------------*/
        /* If compression was enabled, continue scanning for repeated */
        /* character strings.                                         */
        /*------------------------------------------------------------*/
        if ( pInst->Flags & F_RLE_COMPRESS )
        {
          pInst->RLEState = WECP_RLE_SCAN;
        }
        break;

    }
  }

  pInst->DataCount += i;

  /*-------------------------------------------------------------------*/
  /* Restart the inter-character timeout if we previously cancelled it */
  /*-------------------------------------------------------------------*/
  if ( i && pInst->WTimeoutMS )
  {
    setDelayTimer( pInst,
                   TIMER_ID_1,
                   ecp_write_timeout,
                   pInst->WTimeoutMS );
  }
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  ecp_write_timeout                                */
/*                                                                    */
/* DESCRIPTIVE NAME:  Timeout routine for write requests in ECP mode  */
/*                                                                    */
/* FUNCTION:  A timer has expired signifying that the parallel port   */
/*            chip has failed to transmit all the characters and      */
/*            generate an interrupt.  The function of this routine is */
/*            to set the timeout state and call do_write_ecp_mode to  */
/*            process the timeout.                                    */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT:  Interrupt time                                           */
/*                                                                    */
/* ENTRY POINT:  ecp_write_timeout                                    */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  parInstance_t   pInst    - ptr to adapter instance         */
/*                                                                    */
/* EXIT-NORMAL: N/A                                                   */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS: pInst->State                                              */
/*                                                                    */
/* INTERNAL REFERENCES:  do_write_ecp_mode                            */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void ecp_write_timeout( parInstance_t *pInst )
{
#ifdef DEBUGECP
  dprintf( "ecp_write_timeout\r\n");
#endif

  pInst->State = WECP_TIMEOUT;
  do_write_ecp_mode( pInst );

}


