Source file: /~heha/basteln/PC/fx2/fx2ata-200917.zip/scsi.c

//-----------------------------------------------------------------------------
//   File:      scsi.c
//   Contents:   Functions to handle a scsi device.
//
// indent 3.  NO TABS!
//
// Description:
//    SCSI devices differ from IDE device as follows:
//    - SCSI devices may accept or return data according to the value of
//       its byte count registers which may differ from the host request
//       indicated in the CBW variable 'dataTransferlen'. 
//
//       If the host requests to write a greater amount than the byte count 
//       registers indicate, the extra data is processed by throwing it on 
//       the floor and reporting it as a residue.
//
//       If the host requests a read greater than what is indicated in the 
//       byte count registers, and the last packet is a full packet, a short 
//       packet is sent to terminate the transfer.
//
//    - generalSCSIInCommand() and generalSCSIOutCommand() are called from
//       periph.c.  Their conditional compile flags are kept inside the function
//       to maintain original code for periph.c.  All other functions in this
//       module are specific for SCSI and are static to this file.  They are
//       under one conditional compile flag.
//
//-----------------------------------------------------------------------------
// $Archive: /USB/atapifx2/CY4611B/scsi.c $
// $Date: 5/27/05 4:11p $
// $Revision: 4 $
//
//-----------------------------------------------------------------------------
//  Copyright (c) 1999 Cypress Semiconductor, Inc. All rights reserved
//-----------------------------------------------------------------------------
#include "fx2.h"
#include "fx2regs.h"
#include "scsi.h"
#include "gpif.h"
#include "globals.h"

// this bit determines if the current transfer is to be carried out using UDMA (1) or
// PIO (0)

#define SENSE_LEN 18

#if DEVICE_TYPE_IS_SCSI
//-----------------------------------------------------------------------------
// Function:  sendSCSICommand()
//
// Input:   
//    char xdata *cmdbuf   - scsi command packet (EP2FIFOBUF + CBW_DATA_START)
//
// Output:  bit flag
//          0 = success, 1 = failure
//
// Local data:
//    WORD cmd_data - modified command data packet
//
// Description:
//    The command packet is sent to the drive using 16-bit register writes to
//    the drive's data register.  Data is modified to handle endian-ness before
//    before it is sent to the drive.
//
//    ALERT!!!:  Sending the command packet to the drive using register writes was
//       due to an assumption that we could not GPIF the middle of an endpoint's
//       content.  From SSW, we can walk down an endpoint's content using GPIF
//       as long as the transaction count is not exhausted.  For optimization,
//       we may want to GPIF the start of the CBW (maybe send it to the null
//       register where it won't cause grief), GPIF the scsi command packet
//       to the drive then discard the rest of the CBW.  This would remove the
//       need to process the command packet for endian-ness.
//-----------------------------------------------------------------------------
static bit sendSCSICommand()
{
   BYTE cmdLen = EP2FIFOBUF[CBW_CBW_LEN] & CBW_CBW_LEN_MASK;

   // Get ready for fast xfer (just in case)
   mymemmoveix(prevCmd,&EP2FIFOBUF[0x0F],12);
   prevDataTransferLen = dataTransferLen;
   // clear the fast flag.  Will be set within the UDMA routines.
   attemptFastScsi = 0;

   if (!bScsiRegsPreloaded)
      preloadSCSIRegs();

   // if the drive is configured for udma then use udma
   if (ActiveLunConfigData.udmaMode)
      bUseUdma = 1;     
   else
      bUseUdma = 0;

   if (EP2FIFOBUF[0xf] == INQUIRY)     // Manually process INQUIRY so we can modify the device string.
      bUseUdma = 0;

   if (!dataTransferLenLSW && !dataTransferLenMSW)
      bUseUdma = 0;

   if (directionIn && (dataTransferLenLSB & 1))
      bUseUdma = 0;

   if (!bUseUdma)
      writePIO8(ATAPI_FEATURE_REG, 0x00);    // Feature is ALWAYS set to UDMA mode in prepSCSICommand

   if (deviceCount != 1)
      {
      // Select ATAPI device
      writeATA_DRIVESEL_REG();
   
      // Make sure the device is ready for the packet
      while(readATAPI_STATUS_REG() & ATAPI_STATUS_BUSY_BIT)
         ;
      }

   // Send the "ATAPI packet" command
   fastWritePIO8(ATAPI_COMMAND_REG, ATAPI_COMMAND_ATAPI_PACKET);

//   if(driveStatus & ATAPI_STATUS_ERROR_BIT)
//      {
//      EP2BCL = 0x80;         // Release the endpoint buffer
//      return(USBS_FAILED);
//      }
   
   // Zero out non-command bytes
   {
   BYTE i;

   AUTOPTR2H = MSB(EP2FIFOBUF+CBW_DATA_START);
   AUTOPTR2L = LSB(EP2FIFOBUF+CBW_DATA_START+cmdLen);
   for (i = cmdLen; i < 12; i++)
      XAUTODAT2 = 0;
   }

   // Wait for the register block to be non-busy and to request data
   while( (readATAPI_STATUS_REG() & (ATAPI_STATUS_BUSY_BIT | ATAPI_STATUS_DRQ_BIT)) != ATAPI_STATUS_DRQ_BIT)
      ;

   ////////////////////////////////////////////////////////////////////////////////////////////////////////////
   // It may seem cumbersome to write out the SCSI command one word at a time when we could just move the block 
   // and use our block copy routine.  It turns out that moving the block takes LONGER than writing the data
   // in the manner used below (SYK 5/03)
   ////////////////////////////////////////////////////////////////////////////////////////////////////////////
   // Write 12 bytes of command
   AUTOPTR2L = LSB(EP2FIFOBUF+CBW_DATA_START);
   {
      BYTE i;
      BYTE cmd_lsb;
   
      // for(i=0; i<12; i+=2)  structuring the loop backwards reminds the compiler to use DJNZ instruction

      // GPIF cannot be busy.  We just did a read.
      // Write the address/chip selects
      OUTATAPI = (BYTE)(ATAPI_DATA_REG | ~ATAPI_ADDR_MASK & ATAPI_IDLE_VALUE);
      
      for(i = 6; i != 0; i--)
         {
         cmd_lsb = XAUTODAT2;

         // make sure GPIF is not busy
         while (!gpifIdle())
            {
            }
        
         // trigger the GPIF
         XGPIFSGLDATH = XAUTODAT2;                       // Single bus transaction on the GPIF
         XGPIFSGLDATLX = cmd_lsb;                   // Single bus transaction on the GPIF
         }  
    } 
   AUTOPTR2H = MSB(EP6FIFOBUF);     // Restore Autoptr to the value that's used elsewhere.
   
   if (!bUseUdma)
   {
      WAIT_FOR_INTRQ();  
      
      if (waitForBusyBit() == USBS_FAILED)
         {
         EP2BCL = 0x80;         // Release the endpoint buffer
         return(USBS_FAILED);
         }

      #if ATAPI_WAIT_FOR_SERV_BIT
      // Wait for the register block to be non-busy and report SERV = 1.  
      for (driveStatus=ATAPI_STATUS_BUSY_BIT; (driveStatus & (ATAPI_STATUS_BUSY_BIT | 0x10)) != 0x10; )
         {
         // Read the alt status register so we don't trigger any events
         driveStatus = readPIO8(ATAPI_ALT_STATUS_REG);
         }
      #endif
   }

   EP2BCL = 0x80;         // Release the endpoint buffer
   return(USBS_PASSED);
}   

//-----------------------------------------------------------------------------
// Function:  scsiReadPio(BYTE cmd)
//
// Input:   BYTE -- Current command.  Needed so we can modify INQUIRY data received from the device.
// Output:  bit flag
//          0 = success, 1 = failure
//
// Global data:
//
// Local data:
//
// Description:
// Read from the drive until the drive is empty or the buffer is full.
// This breaks up the 16 bit length read into wPacketSize. driveDataLen is
// the amount of data available from the drive.  If the last read results in 
// a short packet, but the drive has more data to give, don't release the packet.
// until either the drive has finished or the packet is full.
//
// Phase error note:  In the Hi<>Do case, we will send garbage to the host before
// reporting the phase error.  In the Hi<Di case we return Di and report phase error.
//-----------------------------------------------------------------------------
static BYTE scsiReadPio()
{

   BYTE driveStatus = 0;
   WORD wDriveDataLen = 0;
   WORD wReadLen = 0;       // Amount of data in the current transaction w/ the drive
   WORD wInSize = 0;        // Tracks the amount of data in the current IN packet
   bit bOddTransfer = 0;    // Compliance testing (not the real world) requires odd packet support.  
   bit bFirstPacket = 1;
   bit bDone = 0;
   bit cReturnStatus = USBS_PASSED;
   BYTE cmd = EP2FIFOBUF[0x0f];

   if (bOddTransfer = dataTransferLen & 1)      // = is intentional here!
      dataTransferLen++;

   while( (!(driveStatus & ATAPI_STATUS_ERROR_BIT)) && (!bDone) && dataTransferLen)
      {
      // Wait for an available buffer.
      waitForInBuffer();

      while( (wInSize < wPacketSize) && (!bDone) && dataTransferLen)
         {
         if(!wDriveDataLen)
            {
            // Wait for the register block to be non-busy and for the DRQ bit to be set.
            driveStatus = waitForDRQBit(1);
            readATAPI_STATUS_REG();             // Clear interrupt for next round.

            // Check if we're done or the drive still has data to transfer.
            if(driveStatus & ATAPI_STATUS_ERROR_BIT)
               {
               bDone = 1;
               cReturnStatus = USBS_FAILED;
               break;
               }
            else if(!(driveStatus & ATAPI_STATUS_DRQ_BIT) )
               {
               bDone=1;      // No more data
               cReturnStatus = USBS_PASSED;
               break;
               }
            else if (!(readPIO8(ATAPI_INT_CAUSE_REG) & 2))  // Check to see if the drive is trying to do IN our OUT (Hi <> Do)
               {
               bDone=1;      // No more data
               cReturnStatus = USBS_PHASE_ERROR;
               break;
               }
            else 
               wDriveDataLen = getDriveDataLen();
            }

         // Get data from drive to endpoint.
         wReadLen = min(wPacketSize-wInSize, wDriveDataLen);
         if (!dataTransferLenMSW)
            wReadLen = min(dataTransferLenLSW, wReadLen);      // added to cover Hi<Di case
         readPIO16(wReadLen);          // add 1 in case readLen is odd
   
         // Adjust counts.
         wDriveDataLen -= wReadLen;
         dataTransferLen -= wReadLen;
         wInSize += wReadLen;
         }

      // Make sure we actually have the data before we send it!  (readPIO16 doesn't wait for done)
      while (!gpifIdle())
         ;

      // Replace device string with our own if we have a replacement string.
      if (cmd == INQUIRY && bFirstPacket)
         {
         if (currentLunNum == 0 && mx2_config_data.Lun0String)
            mymemmovexx(EP6FIFOBUF+SCSI_INQUIRY_MANUFACTURER, (BYTE xdata *)DeviceDscr + (mx2_config_data.Lun0String) - 16, SCSI_IDENTIFY_LEN); 
         else if (currentLunNum == 1 && mx2_config_data.Lun1String)
            mymemmovexx(EP6FIFOBUF+SCSI_INQUIRY_MANUFACTURER, (BYTE xdata *)DeviceDscr + (mx2_config_data.Lun1String) - 16, SCSI_IDENTIFY_LEN); 
         }

      // We either have a full packet or we're at the last packet.
      // Release the buffer to the SIE and re-init packet byte count.
      if (wInSize || (SHORT_PACKET_BEFORE_STALL))
         {
         if (bOddTransfer && wInSize && wInSize < wPacketSize)
            wInSize--;
         EP6BCH = MSB(wInSize);
         EP6BCL = LSB(wInSize);
         }
      if (wInSize < wPacketSize)
         bShortPacketSent = 1;
      wInSize = 0;
      bFirstPacket = 0;
      }

   // If we were doing an odd transfer, must adjust the residue.
   if (bOddTransfer && dataTransferLen)
      dataTransferLen--;
      
   // If the device hasn't yet told us that the command is done, make sure that the device says "done"
   if (!bDone)
      WAIT_FOR_INTRQ();
   // If the device still says it has data ready for us, we're either in case
   // 7 (Hi<Di) or 8 (Hi <> Do).  Both are phase error cases.
   if (waitForBusyBit() == USBS_FAILED)
      return (USBS_FAILED);
   else if ((readATAPI_ALT_STATUS_REG() & ATAPI_STATUS_DRQ_BIT) || wDriveDataLen)
      {
      return (USBS_PHASE_ERROR);
      }
 
   return(cReturnStatus);
}

//-----------------------------------------------------------------------------
// Function:  scsiWrite()
//
// Input:   none
// Output:  bit flag
//          0 = success, 1 = failure
//
// Global data:
//    dataTransferLen   - Amount of data requested by the host.  Counts down.
//    EP2CS             - Endpoint buffer status byte.
//    EP2BCL            - Endpoint LSB byte count register.
//
// Local data:
//    wDriveDataLen     - Amount of data drive will accept.
//    wAmountToWrite    - Amount of data to write to the drive.  This is typically
//                         a portion wDriveDataLen, and is qualified by the packet lenght.
//    wAmountSent       - Amount of data sent to the drive.  Counts up to ensure we don't
//                         exceed the packet size.
//    driveStatus       - Drive status from last action.  
//    bDone             - loop control flag     
//
// Description:
//    This function handles the special case of scsi MODE PAGE write (MODE SELECT command).
//    The drive byte count and the DRQ bit are used to determine how much data to send.
//    Typically, the host requests to send data that is much larger than what the
//    drive wants to accept.  
//
//    The drive may want to receive data in chunks less than a packet size, and the 
//    total number of bytes to satisfy the command may be less or greater than
//    a packet length.  Drive data request is processed as follows:
//    
//    (1) Total number of bytes is less than a packet, and drive wants to receive it in
//    increments.  The rest of the packet must be processed out of the buffer in
//    the size requested by the drive.  This transaction is governed by the
//    DRQ bit in the status register.
//
//    (2) Total number of bytes is greater than a packet, but drive wants it
//    in increments less than a packet lenght.  Full packet(s) are processed out
//    of the buffer using logic from (1) until the drive is satisfied.  This 
//    transaction is governed by the DRQ bit in the status register as well as the
//    byte count from the drive.  Any data residue in the endpoint buffer is sent 
//    to ATAPI_NULL_REGISTER.
//
//    If the host is determined to queue up packets to the buffer after the drive 
//    byte count has been satisfied (DRQ=0), the data is processed out of the endpoint 
//    buffer and discarded by setting the skip bit in the endpoint's byte count
//    register.
//
//    The DRQ bit is valid.
//
//    If a write encounters an error, we return an error and let caller 
//    stall the endpoint.
//
//-----------------------------------------------------------------------------
static BYTE scsiWrite(void)
{

   WORD wDriveDataLen = 0;
   BYTE driveStatus = 0;
   WORD wAmountToWrite = 0;
   WORD wAmountSent = 0;  
   bit bDone = 0;
   bit bShortPacketReceived = 0;
   BYTE cReturnStatus = USBS_PASSED;
   
  while( (!bDone) && dataTransferLen) {
       // Make sure the data is already in the endpoint buffer
    if(!(EP2CS & 0x05)) { // not (empty or stalled)
      if (EP2BC & (wPacketSize - 1)) bShortPacketReceived = 1;

      EP2BCL = 00;         // Release the endpoint buffer to FIFO.

      wAmountSent = 0; 
      while( (wPacketSize > wAmountSent) && (!bDone) && dataTransferLen) {
            // Make sure the write is successful, otherwise get out. -- First wait for the busy bit to clear.
        if (!wDriveDataLen) {
          driveStatus = waitForDRQBit(1);
          readATAPI_STATUS_REG();             // Clear interrupt for next round.
   
          if(driveStatus & ATAPI_STATUS_ERROR_BIT) {
            cReturnStatus = USBS_FAILED;  
            bDone = 1;                    
            break;
          }
               // Check if drive is finished with transaction.
               else if(!(driveStatus & ATAPI_STATUS_DRQ_BIT))
                  {
                  bDone=1;
                  cReturnStatus = USBS_PASSED;
                  }
               else if ((readPIO8(ATA_SECTOR_COUNT_REG) & 2))  // On the first DRQ, check to see if the drive is trying to do IN our OUT (Ho <> Di)
                  {
                  bDone=1;      // No more data
                  cReturnStatus = USBS_PHASE_ERROR;
                  break;
                  }
               else
                  {
                  wDriveDataLen = getDriveDataLen();
                  } // end else
               } // end if !wDriveDataLen

            if (wDriveDataLen > dataTransferLen)      // Host has less data than the drive wants.  Phase error!
               {
               bDone=1;      // No more data
               cReturnStatus = USBS_PHASE_ERROR;
               break;
               }

            // wAmountToWrite is limited by two factors:
            // -- wDriveDataLen -- The amount of data remaining in the drive's most recent request
            // -- wPacketSize-wAmountSent -- The amount of data remaining in our buffer
            wAmountToWrite = min(wPacketSize-wAmountSent, wDriveDataLen);
            wAmountToWrite = min(dataTransferLen, wAmountToWrite);
   
            // Send the packet and adjust counts.  
            if (wAmountToWrite)
               writePIO16(wAmountToWrite+1);
            dataTransferLen -= wAmountToWrite;
            wDriveDataLen -= wAmountToWrite;
            wAmountSent += wAmountToWrite;
            } // end while(within current packet)
         } // end if(data in endpoint buffer)
      } // end while(all data)

   // If the device still wants more data from the host at this point, it's a phase error (case 13)
   driveStatus = waitForDRQBit(0);
   if ((driveStatus & ATAPI_STATUS_DRQ_BIT) || wDriveDataLen)
      cReturnStatus = (USBS_PHASE_ERROR);   
   else if (driveStatus & ATAPI_STATUS_ERROR_BIT)
      cReturnStatus = (USBS_FAILED);   
      
   // If there is still data in our buffer there are several possibilities:
   // 1)  Data in our buffer.  No more data coming from the host.  Reset the endpoint.
   // 2)  Buffer full, more data expected from the host.  STALL.
   // 3)  Data still on the way from the host that will go beyond our buffer size.  STALL.
   // 4)  Data still on the way from the host that will fit within our buffer size.  Wait for the data, then reset the endpoint.
   //       There is no clean way to wait for the data that works with the current TCL scripts.  Just reset the endpoing.
   //
   if (dataTransferLen && !bShortPacketReceived)
      stallEP2OUT();
   // This is done in send_usbs()
   //   else if (!(EP2468STAT & bmEP2EMPTY))         // Discard residue in the buffer if needed.  This is required so that the FIFO will give the buffer back to us (if we DON'T stall)
   //                           // For example, case 11 with 250 bytes of data.
   //      ResetAndArmEp2();     
   return(cReturnStatus);
}

#endif

//-----------------------------------------------------------------------------
// Function:  generalSCSIInCommand()
//
// Input:   none
// Output:  bit flag
//          0 = success, 1 = failure
//
// Global data:
//    CBW from EP2FIFOBUF.
//
// Description:
//    Top level handler for scsi read.  The scsi command packet is 
//    a 12-byte packet extracted from the CBW contained in EP2FIFOBUF 
//    starting from byte 15.  If the command fails, the IN endpoint buffer
//    is stalled and the transaction is failed.
//
//    If the command was processed successfully, data is extracted from the 
//    drive till the byte count from the drive is exhausted.  If the byte count
//    indicated by the drive is less than what is requested by the host, the IN
//    endpoint is stalled, but the transaction is passed.  The remainder of bytes
//    the host still expects is reported as a residue.
//-----------------------------------------------------------------------------
BYTE generalSCSIInCommand()
{
#if DEVICE_TYPE_IS_SCSI

   BYTE result;

   // Clear the bit telling us if we need a STALL to terminat the IRP on the host side   
   bShortPacketSent = 0;

   result = sendSCSICommand();

   // Need to modify this code so that we know if we sent a short packet to terminate the xfer.
   // Although the STALL is required, the ScanLogic driver and Mac driver will not properly handle it.
   if(result != USBS_PASSED)
      {
      failedIn();    // stalls EP6 
      return(USBS_FAILED);
      }

   // no need to do the data xfer phase if the host isn't expecting any data.
   if (!dataTransferLen)
      {
      if (readATAPI_STATUS_REG() & ATAPI_STATUS_DRQ_BIT)
         {
         return(USBS_PHASE_ERROR);       // USBS_PHASE_ERROR -- Hn < Di (case 2) or Hn < Do (case 3)
         }
      else
         return(USBS_PASSED);
      }

   //////////////////////////////////////////////////////////////////
   // Start of data xfer phase
   //////////////////////////////////////////////////////////////////
   if (bUseUdma) 
   {
      result = scsiReadUdma();
   }
   else
   {
      result = scsiReadPio();
   }

   if (dataTransferLen)    
      {
      // Case H(i) > D(i) or H(i) > D(n)
      // "terminate the transfer with a short packet, then STALL the IN endpoint"
      failedIn();       // only stalls EP6, does not return error

      // Pass the result to the next layer up.
      return(result);  
      }

   else
      return(result);       // No residue, just return status

#else
   return(0);
#endif
}   


//-----------------------------------------------------------------------------
// Function:  generalSCSIOutCommand()
//
// Input:   none
// Output:  bit flag
//          0 = success, 1 = failure
//
// Global data:
//    CBW from EP2FIFOBUF.
//
// Local data:
//    cmd      - command op code from CBW
//    result   - return status
//
// Description:
//    The scsi command packet is a 12-byte packet extracted from the CBW 
//    contained in EP2FIFOBUF starting from byte 15.  If the command fails, 
//    the OUT endpoint buffer is stalled and the transaction is failed.
//
//    If the command is successful, data is sent to the drive.
//    When the write encounters an error, the endpoint is stalled and the
//    transaction is failed.
//
//-----------------------------------------------------------------------------
BYTE generalSCSIOutCommand()
{
#if DEVICE_TYPE_IS_SCSI
   // Init local vars.

   BYTE result = USBS_FAILED;

   bUseUdma = 0;

   // if the drive is configured for udma then use udma depending on the
   // scsi command
   if (ActiveLunConfigData.udmaMode)
   {
      bUseUdma = 1;   
   }

   result = sendSCSICommand();

   // If the command failed, stall the endpoint and get out
   if (result != USBS_PASSED)
   {
      stallEP2OUT();
      return(USBS_FAILED);
   }

   if (!dataTransferLen)
      return(result);

   if (bUseUdma)
   {
      return(scsiWriteUdma());
   }
   else
   {
      return(scsiWrite());
   }

#endif

#if DEVICE_TYPE_IS_IDE
   return(0);
#endif
}



#define SENSE_LEN 18
   
///////////////////////////////////////////////////////////////////////////////
#if DEVICE_TYPE_IS_SCSI
///////////////////////////////////////////////////////////////////////////////
WORD getDriveDataLen()
{
    WORD driveDataLen;

    driveDataLen = readPIO8(ATAPI_BYTE_COUNT_MSB) << 8;
    driveDataLen |= readPIO8(ATAPI_BYTE_COUNT_LSB);
    return(driveDataLen);
}


#endif   // DEVICE_TYPE_IS_SCSI

///////////////////////////////////////////////////////////////////////////////////////
// This code must consider a few of the 13 cases:
// Hi <> Do -- Not much we can do about this one.
// Hi == Di -- Thin diagonal
// Hi > Dn  -- The usual error case.  No data.  UDMA will terminate normally by device.  We will get an interrupt.
// Hi > Di  -- Another normal error.  Less data than expected. UDMA will terminate normally by device.  We will get an interrupt.
// Hi < Di  -- $#&* compliance only case.  More data returned than expected.  Device will NOT terminate UDMA, but we should.  We will not get an interrupt.
///////////////////////////////////////////////////////////////////////////////////////
BYTE scsiReadUdma()
{
   #if DEVICE_TYPE_IS_SCSI
   BYTE error = 0;
   
   initUdmaRead();
   
   {
      DWORD dmaLen = dataTransferLen >> 1;

      prepUDMA(((BYTE *) &dmaLen)[1],
               ((BYTE *) &dmaLen)[2],
               ((BYTE *) &dmaLen)[3]);
      readUDMA();

      // Check for partial data buffer.  There are two cases here:
      // -- Host asked for less than a full buffer and he got it.
      if (dataTransferLenLSW & (wPacketSize - 1))
         {
         bShortPacketSent = 1;
         INPKTEND = 0x6;            // EP6 In Packet End
         SYNCDELAY;    
         }
      // -- Host asked for an even number of buffers, but he got a partial buffer.
      // dmalen - GPIFTCLSW = number of xfers done
      else if ((((WORD *) &dmaLen)[1] - GPIFTCLSW) & ((wPacketSize/2)-1))
         {
         bShortPacketSent = 1;
         INPKTEND = 0x6;            // EP6 In Packet End
         SYNCDELAY;
         }
      // Check if 0 length packet is needed (no data transferred at all)
      else if (GPIFTCMSW == ((BYTE *) &dmaLen)[1]
               && GPIFTCLSW == ((WORD *) &dmaLen)[1])
         {
         bShortPacketSent = 1;
         INPKTEND = 0x6;            // EP6 In Packet End
         SYNCDELAY;
         }
   }

   
   // switch the EP back to manual mode
   // EP6FIFOCFG = 0x05;

   // Figure out the residue.  
   // We don't have enough code space to do the math on the upper portion of GPIFTC.  
   // If there is residue in the upper half of GPIFTC, tell the host that the whole thing failed.
   // If there is residue in the lower half, properly tell the host.
   if (!GPIFTCMSW)
   {
      // It may seem wierd to do this in three lines when one will do.
      // If you put all of this code in one line, the compiler translates the single bit (i) into a DWORD before calling
      // a library routine to do the OR.
      BYTE i = (dataTransferLen & 1); 
      dataTransferLen = ((DWORD) GPIFTCLSW << 1);  // Keep the LSBit -- No odd xfers allowed in UDMA mode, extra byte is residue
      dataTransferLenLSW |= i;
   }
    
   // Check for Hi < Di.  We will xfer data until Hi is decremented to 0 but the UDMA i/f will 
   // not have signalled an interupt.
   if (GPIFREADYSTAT & 2)    // Look for DMARQ still active on the drive
      return(USBS_PHASE_ERROR);

   if (waitForBusyBit() == USBS_FAILED) 
   {
      error = readPIO8(ATAPI_ERROR_REG);

      // Upper 4 bits of error contain sense key.  4 is the sense key for parity error
      if ((error & 0xf0) == 0x40)
      {
         slowDownOnUDMAErrors();
      }
      return(USBS_FAILED);
   }
   else
      {
      // successful xfer.  If we're tracking errors, take one away from the error count.
      if (udmaErrorCount)
         udmaErrorCount--;

      attemptFastScsi = 1;
      return(USBS_PASSED);
      }
   #else
      return(USBS_FAILED);
   #endif   // DEVICE_TYPE_IS_SCSI
}
   

// Note:  This routine will never STALL the out pipe.
//        If dataTransferLen remains above 0, it will get stuck rather than stall.
BYTE scsiWriteUdma()
{
   #if DEVICE_TYPE_IS_SCSI
   {
      DWORD dmaLen = (dataTransferLen+1) >> 1;
      initUdmaWrite();
      prepUDMA(((BYTE *) &dmaLen)[1],
               ((BYTE *) &dmaLen)[2],
               ((BYTE *) &dmaLen)[3]);
      writeUDMA();
   }

   // cancel AUTO OUT mode
   EP2FIFOCFG = 0x01;
   SYNCDELAY;
   IFCONFIG = IFCONFIG_DEFAULT;

   // If there's anything in the transfer count it's an error
   // This code doesn't handle partial transfers.  Any failure kills the entire xfer.
   if (! (GPIFTCMSW || GPIFTCLSW))
   {
      dataTransferLen = 0;
   }
   else 
   {
      stallEP2OUT();     // If there is still data to be transferred from the host, send them a STALL.
         
   
      // We don't have enough code space to do the math on the upper portion of GPIFTC.  
      // If there is residue in the upper half of GPIFTC, tell the host that the whole thing failed.
      // If there is residue in the lower half, properly tell the host.
      if (!GPIFTCMSW)
      {
        dataTransferLen &= 1;         // Keep the LSBit -- No odd xfers allowed in UDMA mode, extra byte is residue
        dataTransferLen |= GPIFTCLSW << 1;
      }
   }

   if (GPIFREADYSTAT & 2)    // Look for DMARQ still active on the drive
      return(USBS_PHASE_ERROR);

   // Check status to clear interrupt.
   if (waitForBusyBit() == USBS_FAILED) 
      return(USBS_FAILED);
   else
      {
      attemptFastScsi = 1;
      return(USBS_PASSED);
      }
   #else
      return(USBS_FAILED);
   #endif   // DEVICE_TYPE_IS_SCSI
}


#if DEVICE_TYPE_IS_SCSI
// Wait for busy, then wait for DRQ bit.  Return status register contents.
BYTE waitForDRQBit(bit expectedValue)
{
   BYTE driveStatus;
   BYTE i;

   // Wait for the drive interrupt.
   WAIT_FOR_INTRQ();
   waitForBusyBit();

   // Don't trust the first result after the busy bit goes away.  Read it again.
   // The DELL-07 CDRW has occasional problems if this is not done.
   // The Imation 1208A drive may take >60ms to report proper status.
   // This is solved by looking at the INTRQ bit above!
   for (i = 0; i < 120; i++)
      {
      driveStatus = readATAPI_ALT_STATUS_REG();
      if (driveStatus & ATAPI_STATUS_ERROR_BIT) 
         return(driveStatus);
      if (expectedValue)
         {
         if (driveStatus & ATAPI_STATUS_DRQ_BIT)
            return(driveStatus);
         }
      else if (!(driveStatus & ATAPI_STATUS_DRQ_BIT))
         return(driveStatus);

      if (WAIT_FOR_BUSY_BIT)
         EZUSB_Delay(1);
      else
         return(driveStatus);
      }
   return(driveStatus);
}


////////////////////////////////////////////////////////////////////////////////////////
#endif      // DEVICE_TYPE_IS_SCSI
////////////////////////////////////////////////////////////////////////////////////////

// If UDMA errors are found, slow down.  UDMA errors are commonly found in test setups
// where several adapters are lashed together to attach a 1.8" hard drive or a laptop
// CD-ROM to the tailgate board.
void slowDownOnUDMAErrors()
{
   // Allow up to a 10/1 error ratio.  If it gets above that, slow down or stop using UDMA.
   // The UDMA success code should decrement the udmaErrorCount.
   udmaErrorCount += 10;
   if (udmaErrorCount >= 20)
      {
//      if (ActiveLunConfigData.udmaMode == TRANSFER_MODE_UDMA5)
//         {
//         ActiveLunConfigData.udmaMode = TRANSFER_MODE_UDMA4;
//         configureATATransferMode(ActiveLunConfigData.udmaMode);
//         }
//       else
      if (ActiveLunConfigData.udmaMode == TRANSFER_MODE_UDMA4)
         {
         ActiveLunConfigData.udmaMode = TRANSFER_MODE_UDMA2;
         configureATATransferMode(ActiveLunConfigData.udmaMode);
         }
      else
         {
         ActiveLunConfigData.udmaMode = 0;
         configureATATransferMode(PIO_MODE4);
         }
      udmaErrorCount = 0;
      }
}
   
  

void preloadSCSIRegs()
{
   // Select ATAPI device
   writeATA_DRIVESEL_REG();

   // Make sure the device is ready for the packet
   while(readATAPI_STATUS_REG() & ATAPI_STATUS_BUSY_BIT)
      ;

   // Set "max byte count"
   writePIO8(ATAPI_BYTE_COUNT_LSB, 0xfe);
   writePIO8(ATAPI_BYTE_COUNT_MSB, 0xff);

   // set to 1 here, cleared to 0 in sendScsiCommand() if we're wrong.
   if (ActiveLunConfigData.udmaMode)
      writePIO8(ATAPI_FEATURE_REG, 0x01);
   else
      writePIO8(ATAPI_FEATURE_REG, 0x00);
}

Detected encoding: ASCII (7 bit)2