📄 scsi.c
字号:
{
// Make sure the data is already in the endpoint buffer
if(!(EP2CS & (bmEPEMPTY | bmEPSTALL)))
{
if (EP2BC & (wPacketSize - 1))
bShortPacketReceived = 1;
EP2BCL = 00; // Release the endpoint buffer to FIFO.
// Get the amount of data the drive is willing to accept
if(!wDriveDataLen)
wDriveDataLen = getDriveDataLen();
wAmountSent = 0;
while( (wPacketSize > wAmountSent) && (!bDone) && dataTransferLen)
{
// 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.
writePIO16(ATAPI_DATA_REG, wAmountToWrite+1);
dataTransferLen -= wAmountToWrite;
wDriveDataLen -= wAmountToWrite;
wAmountSent += wAmountToWrite;
// Make sure the write is successful, otherwise get out. -- First wait for the busy bit to clear.
while(readATAPI_STATUS_REG() & ATAPI_STATUS_BUSY_BIT)
;
// 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.
driveStatus = readATAPI_STATUS_REG();
if(driveStatus & ATAPI_STATUS_ERROR_BIT)
{
cReturnStatus = USBS_FAILED;
bDone = 1;
}
else
{
// Check if drive is finished with transaction.
if(!(driveStatus & ATAPI_STATUS_DRQ_BIT))
{
bDone=1;
cReturnStatus = USBS_PASSED;
}
else if(!wDriveDataLen)
wDriveDataLen = getDriveDataLen();
} // end else
} // end while(within current packet)
} // end if(data in endpoint buffer)
} // end while(all data)
} // end if (drive has data)
// If the device still wants more data from the host at this point, it's a phase error (case 13)
if ((readATAPI_STATUS_REG() & ATAPI_STATUS_DRQ_BIT) || wDriveDataLen)
cReturnStatus = (USBS_PHASE_ERROR);
// 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);
}
//-----------------------------------------------------------------------------
// Function: sendSCSICommand()
//
// Input:
// bit useInterrupt
// 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(char xdata *cmdbuf)
{
BYTE driveStatus;
prepareForATAPICommand();
CLEAR_INTRQ; // Clear the interrupt bit.
// Make sure the device is ready for the packet
while (readATAPI_STATUS_REG() & ATAPI_STATUS_BUSY_BIT)
;
// Send the "ATAPI packet" command
writePIO8(ATAPI_COMMAND_REG, ATAPI_COMMAND_ATAPI_PACKET);
// Wait for the register block to be non-busy and to request data
do
driveStatus = readPIO8(ATAPI_STATUS_REG);
while( driveStatus & ATAPI_STATUS_BUSY_BIT);
if(driveStatus & ATAPI_STATUS_ERROR_BIT)
return(USBS_FAILED);
// Write 6 words of command
{
BYTE i;
WORD cmd_data;
for(i=0; i<12; i+=2)
{
cmd_data = (BYTE) (cmdbuf[i+1]);
cmd_data = (cmd_data << 8) | (BYTE) (cmdbuf[i]);
writePIO8(ATAPI_DATA_REG, cmd_data);
}
}
if (useUdma)
return(USBS_PASSED);
{
// Wait for the register block to be non-busy
for (driveStatus=ATAPI_STATUS_BUSY_BIT; driveStatus & ATAPI_STATUS_BUSY_BIT; )
{
// Read the alt status register so we don't trigger any events
driveStatus = readPIO8(ATAPI_ALT_STATUS_REG);
}
}
if (readATAPI_STATUS_REG() & ATAPI_STATUS_ERROR_BIT) // Reading the status reg clears the interrupt
return(USBS_FAILED);
else
return(USBS_PASSED);
}
static void prepareForATAPICommand()
{
// Select ATAPI device
writePIO8(ATAPI_DRIVESEL_REG, 0xa0);
// Make sure the device is ready for the packet
while(readATAPI_STATUS_REG() & ATAPI_STATUS_BUSY_BIT)
;
// configure the transfer mode (PIO or UDMA) using the feature register
if (useUdma)
{
writePIO8(ATAPI_FEATURE_REG, 0x01);
}
else
{
// This disables disconnect/reconnect, synchronous, overlapped and DMA features.
writePIO8(ATAPI_FEATURE_REG, 0x00);
}
// Set "max byte count"
writePIO8(ATAPI_BYTE_COUNT_LSB, 0xff);
writePIO8(ATAPI_BYTE_COUNT_MSB, 0xff);
}
//-----------------------------------------------------------------------------
// Function: inDataFromDrive()
//
// Input: none
// 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 inDataFromDrive()
{
BYTE driveStatus;
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 bDone = 0;
bit cReturnStatus = USBS_FAILED;
// Clear the interrupt bit
CLEAR_INTRQ;
// See if drive is finished processing command and
// is ready for data.
do
{
driveStatus = readPIO8(ATAPI_STATUS_REG);
}
while( driveStatus & ATAPI_STATUS_BUSY_BIT ); // DO-WHILE!!!
// If the drive doesn't have data for us, take off!
if(!(driveStatus & ATAPI_STATUS_DRQ_BIT) )
return (USBS_PASSED);
while( (!(driveStatus & ATAPI_STATUS_ERROR_BIT)) && (!bDone) && dataTransferLen)
{
// Get the amount of data the drive is willing to accept
if(!wDriveDataLen)
wDriveDataLen = getDriveDataLen();
// Wait for an available buffer.
waitForInBuffer();
while( (wInSize < wPacketSize) && (!bDone) && dataTransferLen)
{
// Get data from drive to endpoint.
wReadLen = min(wPacketSize-wInSize, wDriveDataLen);
wReadLen = min(dataTransferLen, wReadLen); // added to cover Hi<Di case
readPIO16(ATAPI_DATA_REG, wReadLen+1); // add 1 in case readLen is odd
// Adjust counts.
wDriveDataLen -= wReadLen;
dataTransferLen -= wReadLen;
wInSize += wReadLen;
// Wait for the register block to be non-busy
while(readATAPI_STATUS_REG() & ATAPI_STATUS_BUSY_BIT)
;
// 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.
driveStatus = readATAPI_STATUS_REG();
// Check if we're done or the drive still has data to transfer.
if(driveStatus & ATAPI_STATUS_ERROR_BIT)
{
bDone = 1;
cReturnStatus = USBS_FAILED;
}
else if(!(driveStatus & ATAPI_STATUS_DRQ_BIT) )
{
bDone=1; // No more data
cReturnStatus = USBS_PASSED;
}
else
if(!wDriveDataLen)
wDriveDataLen = getDriveDataLen();
}
// 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.
EP8BCH = MSB(wInSize);
EP8BCL = LSB(wInSize);
if (wInSize < wPacketSize)
bShortPacketSent = 1;
wInSize = 0;
}
// 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 ((readATAPI_STATUS_REG() & ATAPI_STATUS_DRQ_BIT) || wDriveDataLen)
return (USBS_PHASE_ERROR);
return(cReturnStatus);
}
static bit inDataFromDriveUdma()
{
BYTE driveStatus;
BYTE error;
initUdmaRead();
readUDMA((dataTransferLen + 1) >> 1);
// 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;
}
// switch the EP back to manual mode
EP8FIFOCFG = 0x05;
driveStatus = readATAPI_STATUS_REG();
// Check if stall is needed
if (dataTransferLenLSW & (wPacketSize - 1))
bShortPacketSent = 1;
if (driveStatus & ATAPI_STATUS_ERROR_BIT)
{
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)
{
// Allow up to a 10/1 error ratio. If it gets above that, slow down or stop using UDMA.
udmaErrorCount += 10;
if (udmaErrorCount >= 20)
{
if (udmaMode == TRANSFER_MODE_UDMA4)
{
udmaMode = TRANSFER_MODE_UDMA2;
configureATATransferMode(udmaMode);
}
else
{
udmaMode = 0;
configureATATransferMode(PIO_MODE4);
}
udmaErrorCount = 0;
}
}
return(USBS_FAILED);
}
else if (driveStatus & ATAPI_STATUS_DRQ_BIT)
return(USBS_PHASE_ERROR);
else
{
if (udmaErrorCount)
udmaErrorCount--;
return(USBS_PASSED);
}
}
// Note: This routine will never STALL the out pipe.
// If dataTransferLen remains above 0, it will get stuck rather than stall.
static BYTE scsiWriteUdma()
{
BYTE driveStatus;
writeUDMA((dataTransferLen+1) >> 1);
// 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;
}
// Check status to clear interrupt.
driveStatus = readATAPI_STATUS_REG();
if (driveStatus & ATAPI_STATUS_ERROR_BIT)
return(USBS_FAILED);
else if (driveStatus & ATAPI_STATUS_DRQ_BIT)
return(USBS_PHASE_ERROR);
else
return(USBS_PASSED);
}
////////////////////////////////////////////////////////////////////////////////////////
#endif // DEVICE_TYPE_IS_SCSI
////////////////////////////////////////////////////////////////////////////////////////
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -