/* SCCSID = "%W% %E%" */
/****************************************************************************/
/*                                                                          */
/*                           IBM Confidential                               */
/*                                                                          */
/*                 Copyright (c) IBM Corporation 1996                       */
/*                           All Rights Reserved                            */
/*                                                                          */
/****************************************************************************/
/************************** START OF SPECIFICATIONS ***************************/
/*                                                                            */
/*   SOURCE FILE NAME:  PARSPP.C                                              */
/*                                                                            */
/*   DESCRIPTIVE NAME:  PARALLEL port device driver standard parallel         */
/*                      port (SPP) routines.                                  */
/*                                                                            */
/*   FUNCTION: These routines handle the read (IEEE-1284 nibble/byte mode)    */
/*             and write (compatibility) functions when the parallel port     */
/*             chip is configured to SPP Compatible or Extended mode.         */
/*                                                                            */
/*   NOTES:                                                                   */
/*                                                                            */
/*      DEPENDENCIES: None                                                    */
/*      RESTRICTIONS: None                                                    */
/*                                                                            */
/*   ENTRY POINTS:                                                            */
/*             do_read_byte_mode                                              */
/*             StatusToNibble                                                 */
/*             byte_read_timeout                                              */
/*             read_completion_timeout                                        */
/*             do_write_spp_mode                                              */
/*             service_spp_write                                              */
/*             wspp_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_byte_mode                                */
/*                                                                    */
/* DESCRIPTIVE NAME:  Read data in IEEE-1284 nibble or byte mode      */
/*                    from the device.                                */
/*                                                                    */
/* FUNCTION:  The function of this routine is to read data in         */
/*            IEEE-1284 nibble or byte mode from the device.          */
/*                                                                    */
/* NOTES: This routine works strickly by polling and has no           */
/*        dependencies on interrupts.                                 */
/*                                                                    */
/* CONTEXT: task or interrupt time                                    */
/*                                                                    */
/* ENTRY POINT:  do_read_byte_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                                */
/*                       StatusToNibble                               */
/*                       cancelAllTimers                              */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void do_read_byte_mode( parInstance_t *pInst )
{
  UCHAR                 far *pData;
  UCHAR                 data;
  USHORT                n;
  USHORT                fWaitState;

  /*-------------------------------------------------*/
  /* 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 RNS_INITIAL:
#ifdef DEBUG
  dprintf( "par1284.sys: do_read_byte_mode - RNS_INITIAL\r\n");
#endif
          pInst->DataCount = 0;

          ResetInstanceFlags( pInst, F_TIMEOUT | F_INTERRUPT_EXPECTED );

          /*------------------------------------------------------*/
          /* If we are reading in nibble mode we need to do twice */
          /* as many transfers than the requested byte count.     */
          /* Double the requested data count to compensate.       */
          /*------------------------------------------------------*/
          if ( !(pInst->Flags & F_BYTE_MODE) )
          {
            pInst->ReqDataCount <<= 1;
          }

          /*------------------------------------*/
          /* Start the request elapsed timer.   */
          /*------------------------------------*/
          if ( pInst->RIdleTimeoutMS )
          {
            setDelayTimer( pInst,
                           TIMER_ID_2,
                           read_completion_timeout,
                           pInst->RIdleTimeoutMS );
          }

          /*----------------------------------------------*/
          /* Start the inter-character timer.             */
          /*----------------------------------------------*/
          if ( pInst->RTimeoutMS )
          {
            setDelayTimer( pInst,
                           TIMER_ID_1,
                           read_completion_timeout,
                           pInst->RTimeoutMS );
          }

          /*----------------------------------------------*/
          /* P1284 D2.00 - State 14                       */
          /*                                              */
          /* In byte mode, place the data bus in high     */
          /* impedance mode in anticipation of peripheral */
          /* device to host transfer.                     */
          /*----------------------------------------------*/
          if ( pInst->Flags & F_BYTE_MODE )
          {
            pInst->ControlReg |= SPP_CONTROL_DIR;

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

          pInst->State = RNS_WAIT_DATAAVAIL;
          break;

        /*---------------------------------------------------------*/
        /* P1284 D2.00 - State 18                                  */
        /*                                                         */
        /* Wait for the device to set DATAAVAIL to indicate        */
        /* that the device has reverse channel data to             */
        /* transfer.                                               */
        /*                                                         */
        /* If we read and the device has no data the read          */
        /* will timeout in this state.                             */
        /*                                                         */
        /* Note: We may enter this routine from either IEEE-1284   */
        /*       State 14, with HOSTBUSY=1 (after a negotiation)   */
        /*       or from State 18 with HOSTBUSY=0 (during reverse  */
        /*       idle phase).                                      */
        /*                                                         */
        /*---------------------------------------------------------*/
        case RNS_WAIT_DATAAVAIL:
#ifdef DEBUG
  dprintf( "par1284.sys: do_read_byte_mode - RNS_WAIT_DATAAVAIL\r\n");
#endif
          /*---------------------------------*/
          /* Get IEEE-1284 Status Lines      */
          /*---------------------------------*/
          pInst->StatusReg  =  IORead8( pInst->pIO[0], SPP_STATUS_REG );
          pInst->StatusReg ^=  SPP1284_STATUS_INVERT;

          /*---------------------------------------------------*/
          /* If DATAAVAIL is not set then remain in this state */
          /* and recheck DATAAVAIL every 10mS                  */
          /*---------------------------------------------------*/
          if ( !(pInst->StatusReg & SPP_STATUS_DATAAVAIL) )
          {
            /*----------------------------------------------------*/
            /* Reverse idle phase.                                */
            /*----------------------------------------------------*/
            pInst->ControlReg &= ~SPP_CONTROL_HOSTBUSY;

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

            /*----------------------------------------------------*/
            /* When receive data is not available and RIdleTimeout*/
            /* is 0, a timeout occurs.  When receive data is not  */
            /* available and RIdleTimeout is not 0, set the       */
            /* redrive timer to check for data available (repeat  */
            /* this until data available or timeout occurs).      */
            /*----------------------------------------------------*/
            if ( !pInst->RIdleTimeoutMS )
            {
              SetInstanceFlags( pInst, F_TIMEOUT );
            }
            else
            {
              setDelayTimer( pInst,
                             TIMER_ID_0,
                             do_read_byte_mode,
                             DELAY_OS_MIN );

              fWaitState = 1;
            }
            break;
          }

          /*------------------------------------------------------*/
          /* If we started from State 18, HOSTBUSY=0, we continue */
          /* with State 20 to set HOSTBUSY=1.                     */
          /*                                                      */
          /* Otherwise, we proceed directly to State 7 which sets */
          /* HOSTBUSY=0 to begin clocking the first data item in  */
          /* we raise HOSTBUSY.                                   */
          /*------------------------------------------------------*/
          if ( pInst->ControlReg & SPP_CONTROL_HOSTBUSY )
          {
            pInst->State = RNS_SET_HOSTBUSY_LOW;
          }
          else
          {
            pInst->State = RNS_SET_HOSTBUSY_HIGH;
          }
          break;

        /*---------------------------------------------------*/
        /* P1284 D2.00 - State 20                            */
        /*                                                   */
        /* Set HOSTBUSY=1 and delay TL (35ms) to allow the   */
        /* device to respond. Then continue to State 7.      */
        /*---------------------------------------------------*/
        case RNS_SET_HOSTBUSY_HIGH:
#ifdef DEBUG
  dprintf( "par1284.sys: do_read_byte_mode - RNS_SET_HOSTBUSY_HIGH\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_read_byte_mode,
                         DELAY1284_TL     );

          fWaitState = 1;
          pInst->State = RNS_SET_HOSTBUSY_LOW;
          break;

        /*---------------------------------------------------*/
        /* P1284 D2.00 - State 7                             */
        /*                                                   */
        /* Set HOSTBUSY=0 to tell the device to begin the    */
        /* reverse channel transfer.                         */
        /*---------------------------------------------------*/

        case RNS_SET_HOSTBUSY_LOW:
#ifdef DEBUG
  dprintf( "par1284.sys: do_read_byte_mode - RNS_SET_HOSTBUSY_LOW\r\n");
#endif
          /*---------------------------------*/
          /* Set HOSTBUSY low                */
          /*---------------------------------*/
          pInst->ControlReg &= ~SPP_CONTROL_HOSTBUSY;

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

          /*-----------------------------------*/
          /* Wait for device to set PTRCLK low */
          /*-----------------------------------*/
          pInst->State = RNS_WAIT_PTRCLK_LOW;
          break;

        /*---------------------------------------------------*/
        /* P1284 D2.00 - State 9                             */
        /*                                                   */
        /* Wait for the device to set PTRCLK low to indicate */
        /* that the device status lines contain the reverse  */
        /* channel nibble.                                   */
        /*                                                   */
        /* Then set HOSTBUSY to tell the device we have read */
        /* the reverse channel nibble.                       */
        /*---------------------------------------------------*/

        case RNS_WAIT_PTRCLK_LOW:
#ifdef DEBUG
  dprintf( "par1284.sys: do_read_byte_mode - RNS_WAIT_PTRCLK_LOW\r\n");
#endif
          /*---------------------------------*/
          /* Get IEEE-1284 Status Lines      */
          /*---------------------------------*/
          pInst->StatusReg  =  IORead8( pInst->pIO[0], SPP_STATUS_REG );
          pInst->StatusReg ^=  SPP1284_STATUS_INVERT;

          /*---------------------------------------------------*/
          /* If PTRCLK is not low, then remain in this state   */
          /* and recheck PTRCLK.                               */
          /*---------------------------------------------------*/
          if ( pInst->StatusReg & SPP_STATUS_PTRCLK )
            break;

          /*-----------------------------------------------*/
          /* When PTRCLK is low we are ready to transfer   */
          /* data.                                         */
          /*                                               */
          /* The method of transfer depends whether we are */
          /* in byte or nibble mode.                       */
          /*-----------------------------------------------*/
          pData = pInst->pDataBuf;

          if ( pInst->Flags & F_BYTE_MODE )
          {
            /*-----------------------------------------------*/
            /* For byte mode, the reverse channel byte is    */
            /* contained on the data lines.                  */
            /*-----------------------------------------------*/
            pData[pInst->DataCount++] = IORead8( pInst->pIO[0],
                                                 SPP_DATA_REG );
          }
          else
          {
            /*-----------------------------------------------*/
            /* The status lines contain the reverse channel  */
            /* nibble.                                       */
            /*                                               */
            /* Convert the status line settings to device    */
            /* data nibble.                                  */
            /*                                               */
            /* Note: The nibble is returned in bits 7-4.     */
            /*       Also, the low nibble is returned first. */
            /*-----------------------------------------------*/
            data = StatusToNibble( pInst->StatusReg );

            /*---------------------------------------------*/
            /* Locate the current data byte.               */
            /* Note: DataCount is in terms on nibbles.     */
            /*---------------------------------------------*/
            n = pInst->DataCount++ >> 1;

            /*---------------------------------------------*/
            /* Shift the current data byte left 4-bits     */
            /* and or-in the new nibble to bits 7-4.       */
            /*---------------------------------------------*/
            pData[n] >>= 4;
            pData[n]  |= data;
          }

          /*----------------------------------------------*/
          /* P1284 D2.00 - State 10                       */
          /*                                              */
          /* Set HOSTBUSY to indicate the current device  */
          /* nibble/byte has been received.               */
          /*----------------------------------------------*/
          pInst->ControlReg |= SPP_CONTROL_HOSTBUSY;

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

          /*----------------------------------------------*/
          /* Wait for the device to set PTRCLK high to    */
          /* complete the handshake.                      */
          /*----------------------------------------------*/
          pInst->State = RNS_WAIT_PTRCLK_HIGH;
          break;

        /*----------------------------------------------------*/
        /* P1284 D2.00 - State 11                             */
        /*                                                    */
        /* Wait for the device to acknowledge our setting     */
        /* of HOSTBUSY by its setting PTRCLK high.            */
        /*                                                    */
        /* This completes the transfer of the reverse channel */
        /* nibble.                                            */
        /*----------------------------------------------------*/

        case RNS_WAIT_PTRCLK_HIGH:
#ifdef DEBUG
  dprintf( "par1284.sys: do_read_byte_mode - RNS_WAIT_PTRCLK_HIGH\r\n");
#endif
          /*---------------------------------*/
          /* Get IEEE-1284 Status Lines      */
          /*---------------------------------*/
          pInst->StatusReg  =  IORead8( pInst->pIO[0], SPP_STATUS_REG );
          pInst->StatusReg ^=  SPP1284_STATUS_INVERT;

          /*-----------------------------------------------*/
          /* If the device has not completed the handshake */
          /* by setting PTRCLK high, recheck the device    */
          /* status.                                       */
          /*-----------------------------------------------*/
          if ( !(pInst->StatusReg & SPP_STATUS_PTRCLK) )
            break;

          /*------------------------------------------------*/
          /* P1284 D2.00 - State 16, 17                     */
          /*                                                */
          /* Byte mode transfers require a strobe pulse on  */
          /* HOSTCLK after the device completes the byte    */
          /* transfer. There is no device response to this  */
          /* strobe, however, it is required for byte mode  */
          /* transfers to work.                             */
          /*------------------------------------------------*/
          if ( pInst->Flags & F_BYTE_MODE )
          {
            /*-------------------------*/
            /* Generate HOSTCLK strobe */
            /*-------------------------*/
            pInst->ControlReg &= ~SPP_CONTROL_HOSTCLK;

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

            TimeExecutionStall( 2 );

            pInst->ControlReg |= SPP_CONTROL_HOSTCLK;

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

          /*----------------------------------------------*/
          /* The device has completed the handshake for   */
          /* the current nibble/byte.                     */
          /*                                              */
          /* Check if the read count (in nibbles/bytes)   */
          /* has been satisfied.                          */
          /*                                              */
          /* For a read, the requestor almost never knows */
          /* how many bytes to transfer.  This "success"  */
          /* has the purpose of not overrunning the user  */
          /* buffer!                                      */
          /*----------------------------------------------*/

          if ( pInst->DataCount >= pInst->ReqDataCount )
          {
            pInst->ReturnCode  = RC_SUCCESS;
            pInst->State       = RNS_COMPLETE;
            break;
          }

          /*-------------------------------------------------*/
          /* If we are in nibble mode and the nibble count   */
          /* is odd, then set HOSTBUSY low again to complete */
          /* the byte transfer.                              */
          /*                                                 */
          /* Otherwise, we have transferred a complete byte, */
          /* If the nibble count is even, then check         */
          /* DATAAVAIL to see if the printer has additional  */
          /* bytes to transfer.                              */
          /*-------------------------------------------------*/
          if ( !(pInst->Flags & F_BYTE_MODE) && (pInst->DataCount & 1) )
          {
            pInst->State = RNS_SET_HOSTBUSY_LOW;
          }
          else
          {
            /*---------------------------------*/
            /* Restart intercharacter timer.   */
            /*---------------------------------*/
            if ( pInst->RTimeoutMS )
            {
              setDelayTimer( pInst,
                             TIMER_ID_1,
                             read_completion_timeout,
                             pInst->RTimeoutMS );
            }

            /*---------------------------------------------------*/
            /* If DATAAVAIL is set then read more data.          */
            /* DATAAVAIL is valid at PTRCLK transition.          */
            /*---------------------------------------------------*/
            if ( pInst->StatusReg & SPP_STATUS_DATAAVAIL )
              pInst->State = RNS_SET_HOSTBUSY_LOW;
            else
              pInst->State = RNS_COMPLETE;
          }

          break;

        /*----------------------------------------------------*/
        /* If the read is complete then notify the requesting */
        /* routine.                                           */
        /*----------------------------------------------------*/
        case RNS_COMPLETE:
          cancelAllTimers( pInst );

          ResetInstanceFlags( pInst, F_INTERRUPT_EXPECTED | F_TIMEOUT );
          pInst->UseCount = 1;
          fWaitState      = 1;
          break;
      }

      /*----------------------------------------------------*/
      /* If we have exceeded the maximum time for this read */
      /* then an external timer will set F_TIMEOUT in the   */
      /* status flags.                                      */
      /*----------------------------------------------------*/
      if ( pInst->Flags & F_TIMEOUT )
      {
        pInst->State       = RNS_COMPLETE;
        pInst->ReturnCode  = RC_TIMEOUT;

        fWaitState = 0;
        break;
      }

      /*----------------------------------------------------*/
      /* Check if read was cancelled by Control-C or equiv. */
      /*----------------------------------------------------*/
      if ( pInst->Flags & F_CANCELLED )
      {
        pInst->State       = RNS_COMPLETE;
        pInst->ReturnCode  = RC_CANCELLED;

        fWaitState = 0;
        break;
      }

    }
    while ( !(fWaitState) );

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

  if ( pInst->State == RNS_COMPLETE )
  {
    UnLockInstance( pInst );

    if ( !(pInst->Flags & F_BYTE_MODE) )
    {
      pInst->DataCount >>= 1;
    }

#ifdef DEBUG
  dprintf( "par1284.sys: do_read_byte_mode - RNS_COMPLETE\r\n");
#endif
    /*----------------------------------------------*/
    /* Async callback to originator of read.        */
    /*----------------------------------------------*/
    (*pInst->DoneRtn)( pInst );
  }
  else
  {
    UnLockInstance( pInst );
  }

}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  StatusToNibble                                   */
/*                                                                    */
/* DESCRIPTIVE NAME:  Convert nibble mode status to a nibble of data. */
/*                                                                    */
/* FUNCTION:  The function of this routine is to convert the status   */
/*            presented during a nibble mode data transfer to 4 data  */
/*            bits.                                                   */
/*                                                                    */
/* NOTES: The device data is returned in bits 4-7 of the UCHAR        */
/*        returned.                                                   */
/*                                                                    */
/* CONTEXT: task or interrupt time                                    */
/*                                                                    */
/* ENTRY POINT:  StatusToNibble                                       */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  UCHAR         StatusReg - nibble mode status register      */
/*                                                                    */
/* EXIT-NORMAL: nibble of data                                        */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS:  None                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
UCHAR StatusToNibble( UCHAR StatusReg )
{
  UCHAR                 c = 0;
  USHORT                i;

  UCHAR                 masks[4] = { SPP_STATUS_DATAAVAIL,
                                     SPP_STATUS_XFLAG,
                                     SPP_STATUS_ACKDATAREQ,
                                     SPP_STATUS_PTRBUSY     };

  /*------------------------------------*/
  /* SPP_STATUS_DATAAVAIL is active low */
  /*------------------------------------*/
  StatusReg ^= SPP_STATUS_DATAAVAIL;

  for ( i=0; i < 4; i++ )
  {
    c >>= 1;

    if ( StatusReg & masks[i] )
    {
      c |= 0x80;
    }
  }

  return( c );
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  byte_read_timeout                                */
/*                                                                    */
/* DESCRIPTIVE NAME:  Perform nibble/byte mode timeout process.       */
/*                                                                    */
/* FUNCTION:  The function of this routine is to set the timeout      */
/*            flag, clear the interrupt expected flag, and call       */
/*            do_read_byte_mode to process the timeout.               */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: interrupt time                                            */
/*                                                                    */
/* ENTRY POINT:  byte_read_timeout                                    */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  parInstance_t   pInst    - ptr to adapter instance         */
/*                                                                    */
/* EXIT-NORMAL: N/A                                                   */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS:  None                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void byte_read_timeout( parInstance_t *pInst )
{
#ifdef DEBUG
  dprintf( "par1284.sys: byte_read_timeout\r\n");
#endif
  SetInstanceFlags( pInst, F_TIMEOUT );

  if ( pInst->Flags & F_INTERRUPT_EXPECTED )
  {
    pInst->Flags &= ~F_INTERRUPT_EXPECTED;

    do_read_byte_mode( pInst );
  }
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  read_completion_timeout                          */
/*                                                                    */
/* DESCRIPTIVE NAME:  Perform nibble/byte mode timeout process.       */
/*                                                                    */
/* FUNCTION:  The function of this routine is to set the state to     */
/*            complete and call do_read_byte_mode to process the      */
/*            state change.                                           */
/*                                                                    */
/* NOTES:  A timeout is not an error on the read function.            */
/*                                                                    */
/* CONTEXT: interrupt time                                            */
/*                                                                    */
/* ENTRY POINT:  read_completion_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:  do_read_byte_mode                            */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void read_completion_timeout( parInstance_t *pInst )
{
#ifdef DEBUG
  dprintf( "par1284.sys: read_completion_timeout\r\n");
#endif
  if ( pInst->Flags & F_INTERRUPT_EXPECTED )
    pInst->Flags &= ~F_INTERRUPT_EXPECTED;

  pInst->State = RNS_COMPLETE;
  pInst->ReturnCode  = RC_SUCCESS;
  do_read_byte_mode( pInst );
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  do_write_spp_mode                                */
/*                                                                    */
/* DESCRIPTIVE NAME:  Write data in IEEE-1284 compatibility mode to   */
/*                    the device.                                     */
/*                                                                    */
/* FUNCTION:  The function of this routine is to write data in        */
/*            IEEE-1284 compatibility mode to the device.             */
/*                                                                    */
/* NOTES: This routine works in interrutp or polled mode depending    */
/*        on the setting of F_NO_INTERRUPT.                           */
/*                                                                    */
/* CONTEXT: task or interrupt time                                    */
/*                                                                    */
/* ENTRY POINT:  do_write_spp_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_spp_write                            */
/*                       cancelAllTimers                              */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void do_write_spp_mode( parInstance_t *pInst )
{
  USHORT                i;
  UCHAR                 far *pData;
  USHORT                fWaitState;
  USHORT                rc = 0;

#ifdef DEBUG
  dprintf( "par1284.sys: do_write_spp_mode\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 )
      {
        /*---------------------------------------------------*/
        /*                                                   */
        /*                                                   */
        /*                                                   */
        /*---------------------------------------------------*/
        case WSPP_INIT:
#ifdef DEBUG
  dprintf( "par1284.sys: do_write_spp_mode - WSPP_INIT\r\n");
#endif

          pInst->DataCount = 0;

          ResetInstanceFlags( pInst, F_TIMEOUT );

          /*------------------------------------------------*/
          /* Start the elapsed request timer.               */
          /*------------------------------------------------*/
          if ( pInst->WIdleTimeoutMS )
          {
            setDelayTimer( pInst,
                           TIMER_ID_2,
                           wspp_timeout,
                           pInst->WIdleTimeoutMS );
          }

          /*------------------------------------------------*/
          /* Start the inter-character timer.               */
          /*------------------------------------------------*/
          if ( pInst->WTimeoutMS )
          {
            setDelayTimer( pInst,
                           TIMER_ID_1,
                           wspp_timeout,
                           pInst->WTimeoutMS );
          }

          /*-----------------------------------------------*/
          /* If we are in polled mode, then turn off the   */
          /* parallel port ACK interrupt and setup for     */
          /* polled mode operation.                        */
          /*                                               */
          /* Otherwise enable the ACK interrupt and setup  */
          /* for interrupt driver operation                */
          /*-----------------------------------------------*/
          if ( pInst->Flags & F_NO_INTERRUPT )
          {
            pInst->State  = WSPP_POLLED;
          }
          else
          {
            pInst->State  = WSPP_INTERRUPT;
            pInst->IRQRtn = do_write_spp_mode;
          }

          break;

        /*---------------------------------------------------*/
        /* Polled mode                                       */
        /*---------------------------------------------------*/
        case WSPP_POLLED:
#ifdef DEBUG
  dprintf( "par1284.sys: do_write_spp_mode - WSPP_POLLED\r\n");
#endif
          /*---------------------------------------------------*/
          /* If we have timed out, don't execute state again   */
          /*---------------------------------------------------*/
          if ( pInst->Flags & F_TIMEOUT )
            break;

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

          for ( i=0;
                pInst->DataCount < pInst->ReqDataCount;
                pInst->DataCount++, i++                   )
          {
            /*---------------------------------------------*/
            /* If we cannot send the next character within */
            /* 500us then exit this send loop              */
            /*---------------------------------------------*/
            if ( rc = service_spp_write( pInst, pData[i] ) )
            {
              break;
            }
            /*-----------------------------------------------------*/
            /* If we successfully send at least one character      */
            /* during this poll cycle, cancel the inter-character  */
            /* timeout.                                            */
            /*-----------------------------------------------------*/
            if ( !i )
            {
              cancelDelayTimer( pInst, TIMER_ID_1 );
            }
          }

          /*-------------------------------------------------*/
          /* If we exited the send loop without a timeout,   */
          /* then we have sent all the characters required.  */
          /*-------------------------------------------------*/
          if ( !rc )
          {
            pInst->State = WSPP_COMPLETE;
            break;
          }

          /*-----------------------------------------------------*/
          /* Otherwise, If we exited the send loop due to a      */
          /* timeout.                                            */
          /*                                                     */
          /* We will poll the printer until either the elapsed   */
          /* request timeout or intercharacter timeout expire.   */
          /*-----------------------------------------------------*/
          setDelayTimer( pInst,
                         TIMER_ID_0,
                         do_write_spp_mode,
                         DELAY_OS_MIN );

          fWaitState = 1;

          /*-------------------------------------------------*/
          /* If we previously cancelled the inter-character  */
          /* timeout, then restart it.                       */
          /*-------------------------------------------------*/
          if ( i && pInst->WTimeoutMS )
          {
            setDelayTimer( pInst,
                           TIMER_ID_1,
                           wspp_timeout,
                           pInst->WTimeoutMS );

          }

          break;

        /*---------------------------------------------------*/
        /* Interrupt mode                                    */
        /*---------------------------------------------------*/
         case WSPP_INTERRUPT:
#ifdef DEBUG
  dprintf( "par1284.sys: do_write_spp_mode - WSPP_INTERRUPT\r\n");
#endif
          /*---------------------------------------------------*/
          /* If we have timed out, don't execute state again   */
          /*---------------------------------------------------*/
          if ( pInst->Flags & F_TIMEOUT )
            break;

          /*---------------------------------------------------*/
          /* If we have transferred all the required data then */
          /* complete processing of the current RP             */
          /*---------------------------------------------------*/
          if ( pInst->DataCount >= pInst->ReqDataCount )
          {
            pInst->State = WSPP_COMPLETE;
            break;
          }

          /*---------------------------------------------*/
          /* Indicate we are expecting another interrupt */
          /*---------------------------------------------*/
          SetInstanceFlags( pInst, F_INTERRUPT_EXPECTED );
          fWaitState = 1;

          /*---------------------------*/
          /* Strobe the next character */
          /*---------------------------*/
          if ( !service_spp_write( pInst,
                                   pInst->pDataBuf[pInst->DataCount] ) )
          {
            /*------------------------------------------------*/
            /* If we sent the character successfully, then    */
            /* bump the character count and reset the inter-  */
            /* character timeout (if any).                    */
            /*------------------------------------------------*/
            pInst->DataCount++;

            if ( pInst->WTimeoutMS )
            {
              setDelayTimer( pInst,
                             TIMER_ID_1,
                             wspp_timeout,
                             pInst->WTimeoutMS );
            }

          }
//        else
//        {
            /*---------------------------------------------*/
            /* If we failed to send the current character, */
            /* poll the printer. The inter-character and   */
            /* elapsed timers will continue to run.        */
            /*---------------------------------------------*/
//          ResetInstanceFlags( pInst, F_INTERRUPT_EXPECTED );

/*          setDelayTimer( pInst,
                           TIMER_ID_0,
                           do_write_spp_mode,
                           32L );
          }

*/        break;

       /*-----------------------------------------------------*/
       /* If the write is complete then notify the requesting */
       /* routine.                                            */
       /*-----------------------------------------------------*/
       case WSPP_COMPLETE:
         cancelAllTimers( pInst );

        /*-----------------------------------------*/
        /* Disable interrupt enable at controller. */
        /*                                         */
        /* Doing this after read requests causes   */
        /* data integrity problems.                */
        /*-----------------------------------------*/
        pInst->ControlReg &= ~SPP_CONTROL_ACKIRQ;

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

         ResetInstanceFlags( pInst, F_INTERRUPT_EXPECTED | F_TIMEOUT );
         pInst->UseCount = 1;
         fWaitState      = 1;
         break;
      } /* end case */

      /*----------------------------------------------------*/
      /* If we have exceeded the maximum time for this write*/
      /* then an external timer will set F_TIMEOUT in the   */
      /* status flags.                                      */
      /*----------------------------------------------------*/
      if ( pInst->Flags & F_TIMEOUT )
      {
        pInst->State       = WSPP_COMPLETE;
        pInst->ReturnCode  = RC_TIMEOUT;

        fWaitState = 0;
        break;
      }
      if ( pInst->Flags & F_CANCELLED )
      {
        pInst->State       = WSPP_COMPLETE;
        pInst->ReturnCode  = RC_CANCELLED;

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

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

  if ( pInst->State == WSPP_COMPLETE )
  {
    UnLockInstance( pInst );

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

    /*----------------------------------------------*/
    /* Async callback to originator of write.     . */
    /*----------------------------------------------*/
    (*pInst->DoneRtn)( pInst );
  }
  else
  {
    UnLockInstance( pInst );
  }

}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  service_spp_write                                */
/*                                                                    */
/* DESCRIPTIVE NAME:  SPP compatibility and extended configuration    */
/*                    mode (IEEE-1284 compatibility mode) write       */
/*                    service routine                                 */
/*                                                                    */
/* FUNCTION:  The function of this routine is to transfer a byte of   */
/*            data to the device by generating the required strobe    */
/*            pulse when the device is ready (not busy).              */
/*                                                                    */
/* NOTES:  This routine works in either interrupt or polled mode.     */
/*                                                                    */
/* CONTEXT:  Task or interrupt time                                   */
/*                                                                    */
/* ENTRY POINT:  service_spp_write                                    */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  parInstance_t   pInst    - ptr to adapter instance         */
/*         UCHAR           c        - character to transmit           */
/*                                                                    */
/* EXIT-NORMAL: N/A                                                   */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS: pInst->StatusReg, pInst->ControlReg                       */
/*                                                                    */
/* INTERNAL REFERENCES:  IORead8                                      */
/*                       IOWrite8                                     */
/*                       IODelay                                      */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
USHORT service_spp_write( parInstance_t *pInst, UCHAR c )
{
  ULONG                 cIntervals = 0;

  /*-----------------------------------------------*/
  /* Take a 'quick' peek at BUSY to see if the     */
  /* device can accept the next character.         */
  /*                                               */
  /* If the printer cannot immediately accept the  */
  /* character, poll BUSY for 500us. If we cannot  */
  /* send the character within this time, return   */
  /* failure to the calling routine                */
  /*-----------------------------------------------*/
  do
  {
     /* We do not have a high resolution clock. */
     if ( ++cIntervals > MAX_BUSY_WAIT_500NS_COUNT )
     {
       return( 1 );
     }

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

     pInst->StatusReg ^= SPP_STATUS_INVERT;

  }
  while ( (pInst->StatusReg & SPP_STATUS_BUSY) );

  /*------------------------------------------------------*/
  /* Write the character allowing 500ns for the parallel  */
  /* bus to settle. Then form a 1us strobe pulse.         */
  /*------------------------------------------------------*/

  IOWrite8( pInst->pIO[0],
            SPP_DATA_REG,
            c             );

  IODelay();

  pInst->ControlReg |= SPP_CONTROL_STROBE;

  if ( (pInst->Flags & F_NO_INTERRUPT) == 0 )
    pInst->ControlReg |= SPP_CONTROL_ACKIRQ;

  CLI();

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

  IODelay();  /* 1 microsecond (1us) delay. */
  IODelay();

  pInst->ControlReg &= ~SPP_CONTROL_STROBE;

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

  STI();

  return( 0 );
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  wspp_timeout                                     */
/*                                                                    */
/* DESCRIPTIVE NAME:  Perform compatibility mode write timeout        */
/*                    process.                                        */
/*                                                                    */
/* FUNCTION:  The function of this routine is to set the timeout      */
/*            flag, clear the interrupt expected flag, and call       */
/*            do_write_spp_mode to process the timeout.               */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: interrupt time                                            */
/*                                                                    */
/* ENTRY POINT:  wspp_timeout                                         */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  parInstance_t   pInst    - ptr to adapter instance         */
/*                                                                    */
/* EXIT-NORMAL: N/A                                                   */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS:  None                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  do_write_spp_mode                            */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void wspp_timeout( parInstance_t *pInst )
{

  SetInstanceFlags( pInst, F_TIMEOUT );

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

    do_write_spp_mode( pInst );
  }
}


