//-----------------------------------------------------------------------------
// 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);
}
| Vorgefundene Kodierung: ASCII (7 bit) | 2
|