587 lines
15 KiB
C
587 lines
15 KiB
C
//---------------------------------------------------------------------
|
|
//
|
|
// File: SCSIFNC.C
|
|
//
|
|
// N5380 Scsi Functions file. Contains higher level scsi functions.
|
|
//
|
|
// Revisions:
|
|
// 09-01-92 KJB First.
|
|
// 03-02-93 KJB/JAP Wait for phase change before doing i/o.
|
|
// 03-11-93 JAP Changed retcode equates to reflect new names.
|
|
// 03-12-93 KJB FinishCommandInterrupt now calls CardDisableInterrupt
|
|
// 03-19-93 JAP Implemented condition build FAR and NEAR pointers
|
|
// 03-23-93 KJB Changed for new functional interface.
|
|
// 03-24-93 KJB ScsiStartCommandInterrupt can now return
|
|
// RET_STATUS_MISSED_INTERRUPT, in which case caller
|
|
// should pretend interrupt happened and call
|
|
// FinishCommandInterrupt.
|
|
// 03-25-93 JAP Fixed up typedef and prototype inconsistencies
|
|
// 03-31-93 JAP/KJB Added code to handle data overflow:
|
|
// DATAIN: target sends more bytes than we have
|
|
// been asked to receive
|
|
// DATAOUT: target requests more bytes than we have
|
|
// been asked to send
|
|
// 04-05-93 KJB DEBUG_LEVEL used by DebugPrint for NT.
|
|
// 04-05-93 KJB Changed DoIo, now it will not return
|
|
// DATA_OVERRUN when there is no data to transfer.
|
|
// 04-09-93 KJB Check for phase mismatch before returning that
|
|
// we missed an interrupt.
|
|
// 05-13-93 KJB Added CardParseCommandString for card specific
|
|
// standard string parsing across platforms.
|
|
// Changed CardCheckAdapter to accept an
|
|
// Initialization info from command line, ie
|
|
// force bi-directional ports, etc.
|
|
// All functions that used to take an PBASE_REGISTER
|
|
// parameter now take PWORKSPACE. CardCheckAdapter
|
|
// takes the both the PBASE_REGISTER and the
|
|
// PWORKSPACE parameters. Auto Request Sense is
|
|
// now supported.
|
|
// 05-13-93 KJB Added RequestSenseValid field to TSRB.
|
|
// 05-16-93 KJB Fixed bug: finishcommandinterrupt was returning
|
|
// RET_STATUS_PENDING when length of xfer was 0.
|
|
// Now return RET_STATUS_ERROR when Status != 0.
|
|
//
|
|
//---------------------------------------------------------------------
|
|
|
|
#include CARDTXXX_H
|
|
|
|
//
|
|
// Local functions
|
|
//
|
|
void ScsiDoRequestSense(PTSRB t);
|
|
|
|
//
|
|
// ScsiSendCommand
|
|
//
|
|
// Selects a target and sends a scsi command during command phase.
|
|
//
|
|
|
|
USHORT ScsiSendCommand (PADAPTER_INFO g, UCHAR target,
|
|
UCHAR lun, PUCHAR pcmd, UCHAR cmdlen)
|
|
{
|
|
USHORT rval;
|
|
ULONG tmp;
|
|
|
|
// select the target
|
|
|
|
if (rval = N5380Select (g, target, lun)) {
|
|
if (rval != RET_STATUS_SELECTION_TIMEOUT) {
|
|
DebugPrint((DEBUG_LEVEL,"ScsiSendCommand-0 Error: %x\n",rval));
|
|
}
|
|
return rval;
|
|
}
|
|
|
|
// set the phase to Command
|
|
|
|
if (rval = N5380SetPhase (g, PHASE_COMMAND)) {
|
|
DebugPrint((DEBUG_LEVEL,"ScsiSendCommand-1 Error: %x\n",rval));
|
|
return rval;
|
|
}
|
|
|
|
// send the command bytes
|
|
|
|
if (rval = CardWriteBytesCommand (g, pcmd, (ULONG)cmdlen,
|
|
&tmp, PHASE_COMMAND)) {
|
|
DebugPrint((DEBUG_LEVEL,"ScsiSendCommand-2 Error: %x\n",rval));
|
|
return rval;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//
|
|
// ScsiDoCommand
|
|
//
|
|
// Executes a complete scsi command: all phase sequences without using
|
|
// interrupts.
|
|
//
|
|
|
|
USHORT ScsiDoCommand (PTSRB t)
|
|
{
|
|
USHORT rval;
|
|
PADAPTER_INFO g = t->pWorkspace;
|
|
|
|
// select the target and send the command bytes
|
|
|
|
if (rval = ScsiSendCommand (g, t->Target, t->Lun, t->pCommand,
|
|
t->CommandLen)) {
|
|
DebugPrint((DEBUG_LEVEL,"ScsiDoCommand-0 Error: %x\n",rval));
|
|
goto done;
|
|
}
|
|
|
|
if (rval = ScsiFinishCommandInterrupt (t)) {
|
|
if (rval!=RET_STATUS_SUCCESS) {
|
|
DebugPrint((DEBUG_LEVEL,"ScsiDoCommand-1 Error: %x\n",rval));
|
|
}
|
|
}
|
|
|
|
done:
|
|
t->ReturnCode = rval;
|
|
|
|
return rval;
|
|
}
|
|
|
|
|
|
//
|
|
// ScsiStartCommandInterrupt
|
|
//
|
|
// Executes a scsi command up to the end of command phase. After this, the
|
|
// interrupt will come in and ScsiFinishCommandInterrupt should be called to
|
|
// complete the data, status, and message phases.
|
|
//
|
|
|
|
USHORT ScsiStartCommandInterrupt (PTSRB t)
|
|
{
|
|
USHORT rval;
|
|
PADAPTER_INFO g = t->pWorkspace;
|
|
|
|
// select the target and send the command bytes
|
|
|
|
if (rval = ScsiSendCommand (g, t->Target, t->Lun, t->pCommand,
|
|
t->CommandLen)) {
|
|
DebugPrint((DEBUG_LEVEL,"ScsiStartCommandInterrupt-0 Error: %x\n",rval));
|
|
goto done;
|
|
}
|
|
|
|
// enable the interrupt
|
|
|
|
CardEnableInterrupt (g);
|
|
|
|
// if request is already up, we may have missed the interrupt, it is done
|
|
|
|
if (N5380PortTest (g, N5380_CURRENT_STATUS, CS_REQ)) {
|
|
|
|
// and we are not still in command phase
|
|
|
|
if (!N5380PortTest(g, N5380_DMA_STATUS, DS_PHASE_MATCH)) {
|
|
rval = RET_STATUS_MISSED_INTERRUPT;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
rval = RET_STATUS_PENDING;
|
|
|
|
done:
|
|
t->ReturnCode = rval;
|
|
return rval;
|
|
}
|
|
|
|
//
|
|
// ScsiFinishComamndInterrupt
|
|
//
|
|
// Called to finish a command that has been started by ScsiStartCommandInterupt.
|
|
// This function completes the data, status, and message phases.
|
|
//
|
|
|
|
USHORT ScsiFinishCommandInterrupt (PTSRB t)
|
|
{
|
|
USHORT rval = 0;
|
|
USHORT rval_stat = 0;
|
|
PADAPTER_INFO g = t->pWorkspace;
|
|
|
|
|
|
// set actual transfer length to 0
|
|
|
|
t->ActualDataLen = 0;
|
|
|
|
// set request sense valid flag to FALSE
|
|
|
|
t->Flags.RequestSenseValid = FALSE;
|
|
|
|
// is there a data phase??
|
|
|
|
if (t->DataLen) {
|
|
|
|
// read/write the data if there is a data phase
|
|
|
|
rval = ScsiDoIo (t);
|
|
|
|
}
|
|
|
|
// if no errors, return RET_STATUS_SUCCESS.
|
|
|
|
if (!rval) {
|
|
rval = RET_STATUS_SUCCESS;
|
|
}
|
|
|
|
// get the stat and message bytes
|
|
|
|
if (rval_stat = ScsiGetStat (g, &t->Status)) {
|
|
DebugPrint((DEBUG_LEVEL,"ScsiFinishCommandInterrupt-0 Error: %x\n",rval_stat));
|
|
rval = rval_stat;
|
|
goto done;
|
|
}
|
|
|
|
// if not any other error, return a general status error to indicate
|
|
// that the status byte was bad.
|
|
|
|
if (!rval_stat) {
|
|
|
|
// no errors get status, was there a status check condition?
|
|
|
|
if (t->Status == 0x02) {
|
|
|
|
if (t->Flags.DoRequestSense) {
|
|
ScsiDoRequestSense(t);
|
|
}
|
|
|
|
}
|
|
|
|
if (t->Status) {
|
|
|
|
// return with error when there was a non-zero status
|
|
|
|
rval = RET_STATUS_ERROR;
|
|
}
|
|
|
|
}
|
|
|
|
if (rval!=RET_STATUS_SUCCESS) {
|
|
DebugPrint((DEBUG_LEVEL,"ScsiFinishCommandInterrupt-1 Error: %x\n",rval));
|
|
}
|
|
|
|
done:
|
|
// disable the interrupt
|
|
|
|
CardDisableInterrupt(g);
|
|
|
|
// for now, we never return pending
|
|
|
|
t->ReturnCode = rval;
|
|
return rval;
|
|
}
|
|
|
|
//
|
|
// ScsiDoRequestSense
|
|
//
|
|
// Do a request sense and store the information in the current tsrb.
|
|
// Works on the stack, does not harm the current tsrb.
|
|
//
|
|
VOID ScsiDoRequestSense(PTSRB t)
|
|
{
|
|
PADAPTER_INFO g = t->pWorkspace;
|
|
// allocate on stack ok, since we don't use interrupt for the sense cmd
|
|
TSRB tsrb;
|
|
PTSRB t0 = &tsrb;
|
|
UCHAR pSenseCmd[6];
|
|
USHORT rval;
|
|
|
|
pSenseCmd[0] = 0x03;
|
|
pSenseCmd[1] = 0x00;
|
|
pSenseCmd[2] = 0x00;
|
|
pSenseCmd[3] = 0x00;
|
|
pSenseCmd[4] = t->SenseDataLen;
|
|
pSenseCmd[5] = 0x00;
|
|
|
|
// copy most of the tsrb information from the current tsrb
|
|
|
|
*t0 = *t;
|
|
|
|
// get the sense information
|
|
|
|
t0->Flags.DoRequestSense = FALSE; // don't request sense info here
|
|
t0->pCommand = pSenseCmd;
|
|
t0->CommandLen = 6;
|
|
t0->Dir = TSRB_DIR_IN;
|
|
t0->pData = t->pSenseData;
|
|
t0->DataLen = t->SenseDataLen;
|
|
|
|
rval = CardDoCommand(t0); // don't use interrupts
|
|
|
|
if (rval == RET_STATUS_SUCCESS) {
|
|
t->Flags.RequestSenseValid = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// ScsiWriteBytesSlow
|
|
//
|
|
// This functions writes bytes to the scsi bus using the slow req/ack
|
|
// handshake. Faster methods are generally avaiable, but they are dependent
|
|
// on how the card inplements the dma capabilities of the 5380. This
|
|
// is a sure-fire slow method that works. It is great to bring up new cards.
|
|
//
|
|
|
|
USHORT ScsiWriteBytesSlow (PADAPTER_INFO g, PUCHAR pbytes,
|
|
ULONG len, PULONG pActualLen, UCHAR phase)
|
|
{
|
|
ULONG i;
|
|
USHORT rval = 0;
|
|
UCHAR tmp;
|
|
|
|
for (i=0;i<len;i++) {
|
|
|
|
// wait for request to be asserted
|
|
|
|
if (rval = N5380GetPhase (g, &tmp)) {
|
|
DebugPrint((DEBUG_LEVEL,"ScsiWriteBytesSlow-0 Error: %x\n",rval));
|
|
goto done;
|
|
}
|
|
|
|
// see if phase match
|
|
|
|
if (phase != tmp) {
|
|
rval = RET_STATUS_DATA_OVERRUN;
|
|
goto done;
|
|
}
|
|
|
|
if (rval = N5380PutByte (g, TIMEOUT_REQUEST, pbytes[i])) {
|
|
DebugPrint((DEBUG_LEVEL,"ScsiWriteBytesSlow-1 Error: %x\n",rval));
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
*pActualLen = i;
|
|
return rval;
|
|
}
|
|
|
|
|
|
//
|
|
// ScsiReadBytesSlow
|
|
//
|
|
// This functions reads bytes to the scsi bus using the slow req/ack
|
|
// handshake. Faster methods are generally avaiable, but they are dependent
|
|
// on how the card inplements the dma capabilities of the 5380. This
|
|
// is a sure-fire slow method that works. It is great to bring up new cards.
|
|
//
|
|
|
|
USHORT ScsiReadBytesSlow (PADAPTER_INFO g, PUCHAR pbytes,
|
|
ULONG len, PULONG pActualLen, UCHAR phase)
|
|
{
|
|
ULONG i;
|
|
USHORT rval = 0;
|
|
UCHAR tmp;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
// wait for request to be asserted
|
|
|
|
if (rval = N5380GetPhase (g, &tmp)) {
|
|
DebugPrint((DEBUG_LEVEL,"ScsiReadBytesSlow-0 Error: %x\n",rval));
|
|
goto done;
|
|
}
|
|
|
|
// see if phase match
|
|
|
|
if (phase != tmp) {
|
|
rval = RET_STATUS_DATA_OVERRUN;
|
|
goto done;
|
|
}
|
|
|
|
if (rval = N5380GetByte (g, TIMEOUT_REQUEST, &pbytes[i])) {
|
|
DebugPrint((DEBUG_LEVEL,"ScsiReadBytesSlow-1 Error: %x\n",rval));
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
*pActualLen = i;
|
|
return rval;
|
|
}
|
|
|
|
|
|
//
|
|
// ScsiDoIo
|
|
//
|
|
// This function does the I/O during a data phase.
|
|
//
|
|
|
|
USHORT ScsiDoIo (PTSRB t)
|
|
{
|
|
USHORT rval;
|
|
UCHAR tmp;
|
|
UCHAR phase;
|
|
PADAPTER_INFO g = t->pWorkspace;
|
|
|
|
// wait for next phase, errors in phase will be caught below
|
|
|
|
if (rval = N5380GetPhase (g, &tmp)) {
|
|
goto done;
|
|
}
|
|
|
|
if (t->DataLen && tmp != PHASE_DATAIN && tmp != PHASE_DATAOUT) {
|
|
|
|
// phase is not data in/out and we were expecting data, len !=0
|
|
|
|
rval = RET_STATUS_DATA_OVERRUN;
|
|
goto done;
|
|
}
|
|
|
|
|
|
// phase is now either data in or data out
|
|
|
|
if (t->Dir == TSRB_DIR_UNKNOWN) {
|
|
|
|
// must be read/write, use phase bits to determine it
|
|
|
|
if (tmp == PHASE_DATAOUT) {
|
|
t->Dir = TSRB_DIR_OUT;
|
|
}
|
|
else if (tmp == PHASE_DATAIN) {
|
|
t->Dir = TSRB_DIR_IN;
|
|
}
|
|
|
|
// else: pass thru, don't transfer any data, must be in status phase
|
|
|
|
}
|
|
|
|
|
|
if (t->Dir == TSRB_DIR_OUT) {
|
|
|
|
// data write
|
|
|
|
// set the phase to data out
|
|
|
|
if (rval = N5380SetPhase (g, PHASE_DATAOUT)) {
|
|
return RET_STATUS_ERROR;
|
|
}
|
|
|
|
// send the bytes
|
|
|
|
if (rval = CardWriteBytesFast (g, t->pData, t->DataLen,
|
|
&t->ActualDataLen, PHASE_DATAOUT)) {
|
|
DebugPrint((DEBUG_LEVEL,"ScsiDoIo-0 Error: %x\n",rval));
|
|
return rval;
|
|
}
|
|
|
|
// Check for Data Overflow.
|
|
|
|
while ((N5380GetPhase (g,&phase) == 0) &&
|
|
(phase == PHASE_DATAOUT)) {
|
|
|
|
// DATA OVERFLOW:
|
|
// Target requests more bytes than we have been asked to send.
|
|
// Send a dummy byte of 0 until we are out of DATAOUT phase.
|
|
|
|
ULONG tmpDataLen;
|
|
UCHAR dummy = 0;
|
|
|
|
if (rval = ScsiWriteBytesSlow (g, &dummy, 1,
|
|
&tmpDataLen, PHASE_DATAOUT)) {
|
|
DebugPrint((DEBUG_LEVEL,"ScsiDoIo-2 Error: %x\n",rval));
|
|
return rval;
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (t->Dir == TSRB_DIR_IN) {
|
|
|
|
// data read
|
|
|
|
// set the phase to data in
|
|
|
|
if (rval = N5380SetPhase (g, PHASE_DATAIN)) {
|
|
return RET_STATUS_ERROR;
|
|
}
|
|
|
|
// read the bytes
|
|
|
|
if (rval = CardReadBytesFast (g, t->pData, t->DataLen,
|
|
&t->ActualDataLen, PHASE_DATAIN)) {
|
|
DebugPrint((DEBUG_LEVEL,"ScsiDoIo-1 Error: %x\n",rval));
|
|
return rval;
|
|
}
|
|
|
|
// Check for Data Overflow.
|
|
|
|
while ((N5380GetPhase(g,&phase) == 0) &&
|
|
phase == PHASE_DATAIN) {
|
|
|
|
// DATA OVERFLOW:
|
|
// Target sends more bytes than we have been asked to receive.
|
|
// Swallow up extra bytes until we are out of DATAIN phase.
|
|
|
|
ULONG tmpDataLen;
|
|
UCHAR dummy;
|
|
|
|
if (rval = ScsiReadBytesSlow (g, &dummy, 1,
|
|
&tmpDataLen, PHASE_DATAIN)) {
|
|
DebugPrint((DEBUG_LEVEL,"ScsiDoIo-3 Error: %x\n",rval));
|
|
return rval;
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
return rval;
|
|
}
|
|
|
|
|
|
//
|
|
// ScsiGetStat
|
|
//
|
|
// This function gets the status and message bytes.
|
|
//
|
|
|
|
USHORT ScsiGetStat (PADAPTER_INFO g, PUCHAR pstatus)
|
|
{
|
|
UCHAR tmp;
|
|
USHORT rval;
|
|
|
|
// set the phase to Status Phase
|
|
|
|
if (rval = N5380SetPhase (g, PHASE_STATUS)) {
|
|
DebugPrint((DEBUG_LEVEL,"ScsiGetStat-0 Error: %x\n",rval));
|
|
return rval;
|
|
}
|
|
|
|
// wait for request to be asserted
|
|
|
|
if (rval = N5380GetPhase (g,&tmp)) {
|
|
DebugPrint((DEBUG_LEVEL,"ScsiGetStat-1 Error: %x\n",rval));
|
|
return rval;
|
|
}
|
|
|
|
// see if phase match
|
|
|
|
if (PHASE_STATUS != tmp) {
|
|
return RET_STATUS_PHASE_SEQ_FAILURE;
|
|
}
|
|
|
|
// get the status byte
|
|
|
|
if (rval = N5380GetByte (g, TIMEOUT_REQUEST, pstatus)) {
|
|
DebugPrint((DEBUG_LEVEL,"ScsiGetStat-2 Error: %x\n",rval));
|
|
return rval;
|
|
}
|
|
|
|
// set the phase to Message In Phase
|
|
|
|
if (rval = N5380SetPhase (g, PHASE_MSGIN)) {
|
|
DebugPrint((DEBUG_LEVEL,"ScsiGetStat-3 Error: %x\n",rval));
|
|
return rval;
|
|
}
|
|
|
|
// wait for request to be asserted
|
|
|
|
if (rval = N5380GetPhase (g,&tmp)) {
|
|
DebugPrint((DEBUG_LEVEL,"ScsiGetStat-4 Error: %x\n",rval));
|
|
return rval;
|
|
}
|
|
|
|
// see if phase match
|
|
|
|
if (PHASE_MSGIN != tmp) {
|
|
return RET_STATUS_PHASE_SEQ_FAILURE;
|
|
}
|
|
|
|
// get the msg byte, throw it away
|
|
|
|
if (rval = N5380GetByte (g, TIMEOUT_REQUEST, &tmp)) {
|
|
DebugPrint((DEBUG_LEVEL,"ScsiGetStat-5 Error: %x\n",rval));
|
|
return rval;
|
|
}
|
|
|
|
// set the phase to NULL to up N5380 back to normal
|
|
|
|
if (rval = N5380SetPhase (g, PHASE_NULL)) {
|
|
DebugPrint((DEBUG_LEVEL,"ScsiGetStat-6 Error: %x\n",rval));
|
|
return rval;
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|