//--------------------------------------------------------------------- // // 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;ipWorkspace; // 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; }