/* SCCSID = "%W% %E%" */
/****************************************************************************/
/*                                                                          */
/*                           IBM Confidential                               */
/*                                                                          */
/*                 Copyright (c) IBM Corporation 1996                       */
/*                           All Rights Reserved                            */
/*                                                                          */
/****************************************************************************/
/************************** START OF SPECIFICATIONS ***************************/
/*                                                                            */
/*   SOURCE FILE NAME:  PAREPP.C                                              */
/*                                                                            */
/*   DESCRIPTIVE NAME:  PARALLEL port device driver EPP routines.             */
/*                                                                            */
/*   FUNCTION: These routines handle the EPP read and write functions.        */
/*                                                                            */
/*   NOTES:                                                                   */
/*                                                                            */
/*      DEPENDENCIES: None                                                    */
/*      RESTRICTIONS: None                                                    */
/*                                                                            */
/*   ENTRY POINTS:                                                            */
/*             do_read_epp_mode                                               */
/*             do_write_epp_mode                                              */
/*                                                                            */
/*   EXTERNAL REFERENCES:                                                     */
/*                                                                            */
/* Change Log                                                                 */
/*                                                                            */
/*  Mark    yy/mm/dd  Programmer      Comment                                 */
/*  ----    --------  ----------      -------                                 */
/*          96/03/01  Frank Schroeder                                         */
/*                                                                            */
/**************************** END OF SPECIFICATIONS ***************************/

#include        "par.h"

/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  do_read_epp_mode                                 */
/*                                                                    */
/* DESCRIPTIVE NAME:  Read data in EPP mode from the device.          */
/*                                                                    */
/* FUNCTION:  The function of this routine is to read data in EPP     */
/*            mode from the device.                                   */
/*                                                                    */
/* NOTES: The chipset does most of the work.                          */
/*                                                                    */
/* CONTEXT: task or interrupt time                                    */
/*                                                                    */
/* ENTRY POINT:  do_read_epp_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                           */
/*                                                                    */
/* INTERNAL REFERENCES:  LockInstance                                 */
/*                       UnLockInstance                               */
/*                       SetInstanceFlags                             */
/*                       ResetInstanceFlags                           */
/*                       IORead8                                      */
/*                       IORead32                                     */
/*                       IOWrite8                                     */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void do_read_epp_mode( parInstance_t *pInst )
{
  USHORT                i;
  USHORT                n;
  USHORT                fWaitState;
  UCHAR                 far *pData;

  LockInstance( pInst );

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

  do
  {
    fWaitState = 0;

    UnLockInstance( pInst );

    do
    {
      switch ( pInst->State )
      {
        /*---------------------------------------------------*/
        /* P1284 D2.00 - State 64,67 (addr/data read)        */
        /*                                                   */
        /* Initialize EPP Read                               */
        /*                                                   */
        /* If we are reversing the data transfer direction,  */
        /* first verify that the device has its WAIT line    */
        /* asserted (!SPP_STATUS_BUSY).                      */
        /*---------------------------------------------------*/
        case REPP_INIT:
          pInst->DataCount = 0;
          ResetInstanceFlags( pInst, F_TIMEOUT );

          /*----------------------------------------------------*/
          /* Note: Some chipsets (SMC FDC37C665) require MODE4  */
          /*       for EPP operation.                           */
          /*----------------------------------------------------*/
          if ( pInst->Flags & F_FIFO_SUPPORT )
          {
            pInst->ExtControlReg &= ~EXT_CONTROL_MODE;
            pInst->ExtControlReg |= EXT_CONTROL_MODE1;

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

          /*---------------------------------*/
          /* Set current channel direction   */
          /*---------------------------------*/
          SetInstanceFlags( pInst, F_DIR_REVERSE );

          pInst->State = REPP_DO_READ;

          break;


        /*-------------------------------------------------------*/
        /* P1284 D2.00 - State 58-66 Addr Read                   */
        /*                     58-67 Data Read                   */
        /*                                                       */
        /* At the point we begin reading device data from        */
        /* the chipset.                                          */
        /*                                                       */
        /* In the case of address data, byte reads will always   */
        /* be issued. In the case of data reads byte/dword       */
        /* reads will be issued based on the amount of data      */
        /* requested.                                            */
        /*                                                       */
        /* Once the I/O cycle is issued, the chipset performs    */
        /* the handshake with the device and the cycle           */
        /* completes synchronously, i.e. program execution is    */
        /* suspended until the cycle completes.                  */
        /*                                                       */
        /*-------------------------------------------------------*/
        case REPP_DO_READ:
          pData = &pInst->pDataBuf[pInst->DataCount];
          n     = pInst->ReqDataCount;
          i     = 0;

          /*----------------------------------------------------*/
          /* If an address cycle is required, do a byte read to */
          /* the EPP Address reg to execute the read cycle      */
          /*----------------------------------------------------*/
          if ( pInst->Flags & F_ADDRESS_CYCLE )
          {
            while ( n )
            {
              pData[i] = IORead8( pInst->pIO[0], EPP_ADDR_REG );
              i++;
              n--;

              /*-----------------------------------------------------------*/
              /* Check the status reg to see if there was a device timeout */
              /*                                                           */
              /* Note: EPP timings are much stricter than ECP/BYTE/NIBBLE  */
              /*       mode timings. If the device does not respond within */
              /*       10uS then the chipset will abort the transfer and   */
              /*       will set SPP_STATUS bit 0.                          */
              /*-----------------------------------------------------------*/
              pInst->StatusReg  =  IORead8( pInst->pIO[0], SPP_STATUS_REG );
              pInst->StatusReg ^=  SPP1284_STATUS_INVERT;

              if ( pInst->StatusReg & SPP_STATUS_EPPTIMEOUT )
              {
                pInst->ReturnCode = RC_TIMEOUT;
                break;
              }
            }
          }
          /*----------------------------------------------------*/
          /* If a data cycle read is required do 4-byte reads   */
          /* until less than 4 bytes remain.                    */
          /*----------------------------------------------------*/
          else
          {
            while ( n )
            {
              if ( n < 4 )
              {
                pData[i] = IORead8( pInst->pIO[0], EPP_DATA_REG );
                i++;
                n--;
              }
              else
              {
                (ULONG)pData[i] = IORead32( pInst->pIO[0], EPP_DATA_REG );
                i += 4;
                n -= 4;
              }

              /*-----------------------------------------------------------*/
              /* Check the status reg to see if there was a device timeout */
              /*                                                           */
              /* Note: EPP timings are much stricter than ECP/BYTE/NIBBLE  */
              /*       mode timings. If the device does not respond within */
              /*       10uS then the chipset will abort the transfer and   */
              /*       will set SPP_STATUS bit 0.                          */
              /*-----------------------------------------------------------*/
              pInst->StatusReg  =  IORead8( pInst->pIO[0], SPP_STATUS_REG );
              pInst->StatusReg ^=  SPP1284_STATUS_INVERT;

              if ( pInst->StatusReg & SPP_STATUS_EPPTIMEOUT )
              {
                pInst->ReturnCode = RC_TIMEOUT;
                break;
              }
            }
          }

          pInst->DataCount = i;

          pInst->State  = REPP_COMPLETE;
          break;

        /*---------------------------------------------------*/
        /*                                                   */
        /*                                                   */
        /*                                                   */
        /*---------------------------------------------------*/
        case REPP_COMPLETE:
          fWaitState = 1;
          break;
      }
    }
    while ( !(fWaitState) );

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

  /*----------------------------------------------------*/
  /* If the read is complete then notify the requesting */
  /* routine.                                           */
  /*----------------------------------------------------*/
  if ( pInst->State == REPP_COMPLETE )
  {
    UnLockInstance( pInst );
    (*pInst->DoneRtn)( pInst );
  }
  else
  {
    UnLockInstance( pInst );
  }

}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  do_write_epp_mode                                */
/*                                                                    */
/* DESCRIPTIVE NAME:  Write data in EPP mode to the device.           */
/*                                                                    */
/* FUNCTION:  The function of this routine is to write data in EPP    */
/*            mode to the device.                                     */
/*                                                                    */
/* NOTES: The chipset does most of the work.                          */
/*                                                                    */
/* CONTEXT: task or interrupt time                                    */
/*                                                                    */
/* ENTRY POINT:  do_write_epp_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                           */
/*                                                                    */
/* INTERNAL REFERENCES:  LockInstance                                 */
/*                       UnLockInstance                               */
/*                       SetInstanceFlags                             */
/*                       ResetInstanceFlags                           */
/*                       IORead8                                      */
/*                       IORead32                                     */
/*                       IOWrite8                                     */
/*                       IOWrite32                                    */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
void do_write_epp_mode( parInstance_t *pInst )
{

  USHORT                i;
  USHORT                n;
  USHORT                fWaitState;
  UCHAR                 far *pData;

  LockInstance( pInst );

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

  do
  {
    fWaitState = 0;

    UnLockInstance( pInst );

    do
    {
      switch ( pInst->State )
      {
        /*---------------------------------------------------*/
        /* Initialize EPP Write                              */
        /*                                                   */
        /* If we are reversing the data transfer direction,  */
        /* first verify that the device has its WAIT line    */
        /* asserted (!SPP_STATUS_BUSY).                      */
        /*---------------------------------------------------*/
        case WEPP_INIT:
          pInst->DataCount = 0;
          ResetInstanceFlags( pInst, F_TIMEOUT );

          /*----------------------------------------------------*/
          /* Note: Some chipsets (SMC FDC37C665) require MODE4  */
          /*       for EPP operation.                           */
          /*----------------------------------------------------*/
          if ( pInst->Flags & F_FIFO_SUPPORT )
          {
            pInst->ExtControlReg &= ~EXT_CONTROL_MODE;
            pInst->ExtControlReg |= EXT_CONTROL_MODE1;

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

          /*---------------------------------*/
          /* Set current channel direction   */
          /*---------------------------------*/
          ResetInstanceFlags( pInst, F_DIR_REVERSE );

          pInst->State = WEPP_DO_WRITE;
          break;


        /*-------------------------------------------------------*/
        /* P1284 D2.00 - State 56-61 Addr Write                  */
        /*                     58-63 Data Write                  */
        /*                                                       */
        /* At the point we begin writing device data to          */
        /* the chipset.                                          */
        /*                                                       */
        /* In the case of address data, byte writes will always  */
        /* be issued. In the case of data writes, byte/dword     */
        /* writes will be issued based on the amount of data     */
        /* requested.                                            */
        /*                                                       */
        /* Once the I/O cycle is issued, the chipset performs    */
        /* the handshake with the device and the cycle           */
        /* completes synchronously, i.e. program execution is    */
        /* suspended until the cycle completes.                  */
        /*                                                       */
        /*-------------------------------------------------------*/
        case WEPP_DO_WRITE:
          pData = &pInst->pDataBuf[pInst->DataCount];
          n     = pInst->ReqDataCount;
          i     = 0;

          /*-----------------------------------------------------*/
          /* If an address cycle is required, do a byte write to */
          /* the EPP Address reg to execute the write cycle      */
          /*-----------------------------------------------------*/
          if ( pInst->Flags & F_ADDRESS_CYCLE )
          {
            while ( n )
            {
              IOWrite8( pInst->pIO[0], EPP_ADDR_REG, pData[i] );
              i++;
              n--;

              /*-----------------------------------------------------------*/
              /* Check the status reg to see if there was a device timeout */
              /*                                                           */
              /* Note: EPP timings are much stricter than ECP/BYTE/NIBBLE  */
              /*       mode timings. If the device does not respond within */
              /*       10uS then the chipset will abort the transfer and   */
              /*       will set SPP_STATUS bit 0.                          */
              /*-----------------------------------------------------------*/
              pInst->StatusReg  =  IORead8( pInst->pIO[0], SPP_STATUS_REG );
              pInst->StatusReg ^=  SPP1284_STATUS_INVERT;

              if ( pInst->StatusReg & SPP_STATUS_EPPTIMEOUT )
              {
                pInst->ReturnCode = RC_TIMEOUT;
                break;
              }
            }
          }
          /*----------------------------------------------------*/
          /* If a data cycle write is required do 4-byte writes */
          /* until less than 4 bytes remain.                    */
          /*----------------------------------------------------*/
          else
          {
            while ( n )
            {
              if ( n < sizeof(ULONG) )
              {
                IOWrite8( pInst->pIO[0], EPP_DATA_REG, pData[i] );
                i++;
                n--;
              }
              else
              {
                IOWrite32( pInst->pIO[0], EPP_DATA_REG, ((ULONG far *)pData)[i] );
                i += sizeof(ULONG);
                n -= sizeof(ULONG);
              }

              /*-----------------------------------------------------------*/
              /* Check the status reg to see if there was a device timeout */
              /*                                                           */
              /* Note: EPP timings are much stricter than ECP/BYTE/NIBBLE  */
              /*       mode timings. If the device does not respond within */
              /*       10uS then the chipset will abort the transfer and   */
              /*       will set SPP_STATUS bit 0.                          */
              /*-----------------------------------------------------------*/
              pInst->StatusReg  =  IORead8( pInst->pIO[0], SPP_STATUS_REG );
              pInst->StatusReg ^=  SPP1284_STATUS_INVERT;

              if ( pInst->StatusReg & SPP_STATUS_EPPTIMEOUT )
              {
                pInst->ReturnCode = RC_TIMEOUT;
                break;
              }
            }
          }

          pInst->DataCount = i;

          pInst->State  = WEPP_COMPLETE;
          break;

        /*---------------------------------------------------*/
        /*                                                   */
        /*---------------------------------------------------*/
        case WEPP_COMPLETE:
          fWaitState = 0;
      }
    }
    while ( !(fWaitState) );

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

  /*----------------------------------------------------*/
  /* If the write is complete, notify the requesting    */
  /* routine.                                           */
  /*----------------------------------------------------*/
  if ( pInst->State == WEPP_COMPLETE )
  {

    UnLockInstance( pInst );
    (*pInst->DoneRtn)( pInst );
  }
  else
  {
    UnLockInstance( pInst );
  }

}


