/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1992 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 source code is provided to you solely for       */
/*    the purpose of assisting you in your development of OS/2 device        */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Developer Connection Device Driver       */
/*    Source Kit for OS/2. This Copyright statement may not be removed.      */
/*                                                                           */
/*****************************************************************************/

#include        "par.h"

/*--------------------------------------------------------------------*/
/*                                                                    */
/*  parspp.c - Compatibility Mode protocols.                          */
/*                                                                    */
/*  This module contains routines for writing data to the attached    */
/*  device in compatibility mode and for reading data back from the   */
/*  device in IEEE-1284 byte and nibble modes.                        */
/*                                                                    */
/*--------------------------------------------------------------------*/

/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  do_data_xfer                                     */
/*                                                                    */
/* DESCRIPTIVE NAME:  Dispatches appropriate communication mode worker*/
/*                                                                    */
/* FUNCTION:  The function of this routine is to dispatch the read    */
/*            or write request to the appropriate worker routine      */
/*            based upon the data transfer direction and current      */
/*            communication mode.                                     */
/*                                                                    */
/* NOTES:  This routine also sets the correct initial state for the   */
/*         worker routine.                                            */
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  do_data_xfer                                         */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  pInst-> adapter instance                                   */
/*                                                                    */
/* EXIT-NORMAL: RC_SUCCESS                                            */
/*                                                                    */
/* EXIT-ERROR:  RC_INVALID_MODE                                       */
/*                                                                    */
/* EFFECTS: None                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  do_read_byte_mode                            */
/*                       SetInstanceFlags                             */
/*                       ResetInstanceFlags                           */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
USHORT do_data_xfer( parInstance_t *pInst )
{
  USHORT       rc = RC_SUCCESS;

  if ( !(pInst->Flags & F_WRITE_REQ) )
  {
    /*------------------------------------------------*/
    /* Route read requests based on current CommMode  */
    /*------------------------------------------------*/
    switch ( pInst->CurCommMode )
    {
      case PAR_NIBBLE:
        ResetInstanceFlags( pInst, F_BYTE_MODE );
        pInst->State = RNS_INITIAL;
        do_read_byte_mode( pInst );
        break;

      case PAR_BYTE:
        SetInstanceFlags( pInst, F_BYTE_MODE );
        pInst->State = RNS_INITIAL;
        do_read_byte_mode( pInst );
        break;

      default:
#ifdef DEBUG
  dprintf( "par1284.sys: do_data_xfer - invalid communication mode\r\n" );
#endif
        pInst->ReturnCode = rc = RC_INVALID_MODE;
        break;
    }
  }

  return( rc );
}

/*----------------------------------------------------------*/
/* This routine does a reverse channel read in either byte  */
/* or nibble mode.                                          */
/*                                                          */
/* It works strictly by polling and has no dependencies on  */
/* interrupts.                                              */
/*----------------------------------------------------------*/

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 timeout. */
          /*------------------------------------*/
          if ( pInst->IdleTimeoutMS )
          {
            setDelayTimer( pInst,
                           TIMER_ID_2,
                           byte_read_timeout,
                           pInst->IdleTimeoutMS );
          }

          /*----------------------------------------------*/
          /* Start the inter-character timeout            */
          /*                                              */
          /* Note: It would make more sense to start this */
          /*       after the first character was received */
          /*----------------------------------------------*/
          if ( pInst->TimeoutMS )
          {
            setDelayTimer( pInst,
                           TIMER_ID_1,
                           byte_read_timeout,
                           pInst->TimeoutMS );
          }

          /*----------------------------------------------*/
          /* 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) )
          {
            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->IdleTimeoutMS )
            {
              SetInstanceFlags( pInst, F_TIMEOUT );
            }
            else
            {
              setDelayTimer( pInst,
                             TIMER_ID_0,
                             do_read_byte_mode,
                             DELAY1284_TL         );

              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;

          /*------------------------------------------------*/
          /* P1284 D2.00 - State 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 )
          {
            /*-------------------------*/
            /* Turn HOSTCLK strobe off */
            /*-------------------------*/
            pInst->ControlReg |= SPP_CONTROL_HOSTCLK;
          }

          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;

          /*------------------------------------------------*/
          /* P1284 D2.00 - State 16                         */
          /*                                                */
          /* 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 )
          {
            /*-------------------------*/
            /* Turn HOSTCLK strobe on  */
            /*-------------------------*/
            pInst->ControlReg &= ~SPP_CONTROL_HOSTCLK;
          }

          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;
          }

          /*----------------------------------------------*/
          /* The device has completed the handshake for   */
          /* the current nibble/byte.                     */
          /*                                              */
          /* Check if the read count (in nibbles/bytes)   */
          /* has been satisfied.                          */
          /*----------------------------------------------*/
          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.                              */
          /*-------------------------------------------------*/
          pInst->State = RNS_WAIT_DATAAVAIL;

          if ( !(pInst->Flags & F_BYTE_MODE) && (pInst->DataCount & 1) )
          {
            pInst->State = RNS_SET_HOSTBUSY_LOW;
          }
          else
          {
            if ( pInst->TimeoutMS )
            {
              setDelayTimer( pInst,        /* restart intercharacter timeout */
                             TIMER_ID_1,
                             byte_read_timeout,
                             pInst->TimeoutMS );
            }
          }

          break;

        /*----------------------------------------------------*/
        /* If the read is complete then notify the requesting */
        /* routine.                                           */
        /*----------------------------------------------------*/
        case RNS_COMPLETE:

          cancelDelayTimer( pInst, TIMER_ID_0 );
          cancelDelayTimer( pInst, TIMER_ID_1 );
          cancelDelayTimer( pInst, TIMER_ID_2 );

          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 */
      /*-----------------------------*/
      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 );
  }

}


/*--------------------------------------------------------------------*/
/*                                                                    */
/* Convert nibble mode status lines to data.                          */
/*                                                                    */
/* This routine converts the status presented during a nibble mode    */
/* data transfer to 4 data bits. The device data is returned in       */
/* bits 4-7 of the UCHAR returned.                                    */
/*                                                                    */
/*--------------------------------------------------------------------*/
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 );
}

/*--------------------------------------------------------------------*/
/*                                                                    */
/* Nibble/Byte mode read timeouts.                                    */
/*                                                                    */
/* If a timeout occurs and we are actively polling indicated by       */
/* F_INTERRUPT_EXPECTED = 0, then we simply set F_TIMEOUT and         */
/* allow the timeout check at the bottom of do_read_byte_mode         */
/* to force the state loop to RNS_COMPLETE.                           */
/*                                                                    */
/*--------------------------------------------------------------------*/

void byte_read_timeout( parInstance_t *pInst )
{
  SetInstanceFlags( pInst, F_TIMEOUT);

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

    do_read_byte_mode( pInst );
  }
}

