📄 scsi.c
字号:
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);
}
//-----------------------------------------------------------------------------
// 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;
AUTOPTRH2 = MSB(EP2FIFOBUF+CBW_DATA_START);
AUTOPTRL2 = 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
AUTOPTRL2 = 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 = 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
}
}
AUTOPTRH2 = 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.
//-----------------------------------------------------------------------------
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;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -