📄 scsi.c
字号:
// 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 *)pDeviceDscr + (mx2_config_data.Lun0String) - 16, SCSI_IDENTIFY_LEN);
else if (currentLunNum == 1 && mx2_config_data.Lun1String)
mymemmovexx(EP6FIFOBUF+SCSI_INQUIRY_MANUFACTURER, (BYTE xdata *)pDeviceDscr + (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);
}
#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
WRITEDELAY();
}
// -- 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
WRITEDELAY();
}
// 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
WRITEDELAY();
}
}
// 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 = bmWORDWIDE;
WRITEDELAY();
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);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -