NT4/private/ntos/miniport/trantor/source/scsifnc.c
2020-09-30 17:12:29 +02:00

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