#include "DoScsiCommand.h"
#include "util.h"
#define kSCSICommandTimeout (5 * 1000L)
#define kMaxSCSIRetries 40
#ifndef kScsiStatBSY
#define kScsiStatBSY (1 << 6)
#endif
#ifndef kScsiStatSEL
#define kScsiStatSEL (1 << 1)
#endif
#define ScsiBusBusy() ((SCSIStat() & (kScsiStatBSY | kScsiStatSEL)) != 0)
int gSCSIHiBusID;
SCSIExecIOPB *gSCSIExecIOPBPtr;
UInt32 gSCSIExecIOPBPtrLen;
UInt16 GetCommandLength(const SCSI_CommandPtr cmdPtr);
Boolean IsVirtualMemoryRunning(void);
OSErr OriginalSCSI(
DeviceIdent scsiDevice,
const SCSI_CommandPtr scsiCommand,
UInt8 scsiCommandLen,
Ptr dataBuffer,
ByteCount dataLength,
UInt32 scsiFlags,
ByteCount *actualTransferCount,
UInt8 *scsiStatusByte
);
OSErr DoOriginalSCSICommand(
DeviceIdent scsiDevice,
const SCSI_CommandPtr theSCSICommand,
unsigned short cmdBlockLength,
Ptr dataBuffer,
ByteCount dataLength,
UInt32 scsiFlags,
ByteCount *actualTransferCount,
SCSI_Sense_Data *sensePtr
);
Boolean
IsIllegalRequest(
OSErr scsiStatus,
const SCSI_Sense_Data *senseDataPtr
)
{
Boolean result;
#define SENSE (*senseDataPtr)
result = FALSE;
if (scsiStatus == scsiNonZeroStatus
&& (SENSE.senseKey & kScsiSenseKeyMask) == kScsiSenseIllegalReq
&& SENSE.additionalSenseLength >= 4) {
switch ((SENSE.additionalSenseCode << 8) | SENSE.additionalSenseQualifier) {
case 0x0000:
case 0x2000:
case 0x2022:
result = TRUE;
break;
default:
break;
}
}
return (result);
#undef SENSE
}
Boolean
IsNoMedia(
OSErr scsiStatus,
const SCSI_Sense_Data *senseDataPtr
)
{
Boolean result;
#define SENSE (*senseDataPtr)
result = FALSE;
if (scsiStatus == scsiNonZeroStatus
&& (SENSE.senseKey & kScsiSenseKeyMask) == kScsiSenseNotReady
&& SENSE.additionalSenseLength >= 4) {
switch ((SENSE.additionalSenseCode << 8) | SENSE.additionalSenseQualifier) {
case 0x0000:
case 0x3A00:
result = TRUE;
break;
default:
break;
}
}
return (result);
#undef SENSE
}
OSErr
DoSCSICommand(
DeviceIdent scsiDevice,
ConstStr255Param currentAction,
const SCSI_CommandPtr callerSCSICommand,
Ptr dataBuffer,
ByteCount dataLength,
UInt32 scsiFlags,
ByteCount *actualTransferCount,
SCSI_Sense_Data *sensePtr,
StringPtr senseMessage
)
{
OSErr status;
SCSI_Command theSCSICommand;
unsigned short cmdBlockLength;
theSCSICommand = *callerSCSICommand;
theSCSICommand.scsi[1] &= ~0xE0;
theSCSICommand.scsi[1] |= (scsiDevice.LUN & 0x03) << 5;
cmdBlockLength = GetCommandLength(&theSCSICommand);
if (senseMessage != NULL)
senseMessage[0] = 0;
if (sensePtr != NULL)
sensePtr->errorCode = 0;
if (scsiDevice.bus == kOriginalSCSIBusAdaptor) {
status = DoOriginalSCSICommand(
scsiDevice,
&theSCSICommand,
cmdBlockLength,
dataBuffer,
dataLength,
scsiFlags,
actualTransferCount,
sensePtr
);
}
else {
clear_memory(gSCSIExecIOPBPtr, gSCSIExecIOPBPtrLen);
#define PB (*gSCSIExecIOPBPtr)
PB.scsiPBLength = gSCSIExecIOPBPtrLen;
PB.scsiFunctionCode = SCSIExecIO;
PB.scsiDevice = scsiDevice;
PB.scsiTimeout = kSCSICommandTimeout;
PB.scsiFlags = scsiFlags | (scsiSIMQNoFreeze | scsiDontDisconnect);
if (sensePtr != NULL) {
PB.scsiSensePtr = (UInt8 *) sensePtr;
PB.scsiSenseLength = sizeof *sensePtr;
}
BlockMoveData(&theSCSICommand, &PB.scsiCDB.cdbBytes[0], cmdBlockLength);
PB.scsiCDBLength = cmdBlockLength;
if (dataBuffer != NULL) {
PB.scsiDataPtr = (UInt8 *) dataBuffer;
PB.scsiDataLength = dataLength;
PB.scsiDataType = scsiDataBuffer;
PB.scsiTransferType = scsiTransferPolled;
}
status = SCSIAction((SCSI_PB *) &PB);
if (status == noErr)
status = PB.scsiResult;
if (status == scsiSelectTimeout)
status = scsiDeviceNotThere;
if (actualTransferCount != NULL) {
*actualTransferCount = dataLength - PB.scsiDataResidual;
if (*actualTransferCount > dataLength)
*actualTransferCount = dataLength;
}
#undef PB
}
if (status == scsiNonZeroStatus
&& sensePtr != NULL
&& sensePtr->errorCode != 0
&& senseMessage != NULL) {
}
return (status);
}
OSErr
DoOriginalSCSICommand(
DeviceIdent scsiDevice,
const SCSI_CommandPtr theSCSICommand,
unsigned short cmdBlockLength,
Ptr dataBuffer,
ByteCount dataLength,
UInt32 scsiFlags,
ByteCount *actualTransferCount,
SCSI_Sense_Data *sensePtr
)
{
OSErr status;
UInt8 scsiStatusByte;
SCSI_Command scsiStatusCommand;
status = OriginalSCSI(
scsiDevice,
theSCSICommand,
cmdBlockLength,
dataBuffer,
dataLength,
scsiFlags,
actualTransferCount,
&scsiStatusByte
);
if (status == scsiNonZeroStatus
&& scsiStatusByte == kScsiStatusCheckCondition
&& sensePtr != NULL) {
CLEAR(scsiStatusCommand);
CLEAR(*sensePtr);
scsiStatusCommand.scsi6.opcode = kScsiCmdRequestSense;
scsiStatusCommand.scsi[1] |= (scsiDevice.LUN & 0x03) << 5;
scsiStatusCommand.scsi6.len = sizeof *sensePtr;
status = OriginalSCSI(
scsiDevice,
&scsiStatusCommand,
sizeof scsiStatusCommand.scsi6,
(Ptr) sensePtr,
sizeof *sensePtr,
scsiDirectionIn,
NULL,
&scsiStatusByte
);
if (status != noErr && status != scsiDataRunError) {
#ifdef notdef
if (gDebugOnError && scsiStatusByte != kScsiStatusCheckCondition) {
Str255 work;
pstrcpy(work, "\pAutosense failed ");
AppendSigned(work, status);
AppendChar(work, ' ');
AppendHexLeadingZeros(work, scsiStatusByte, 2);
DebugStr(work);
}
#endif
sensePtr->errorCode = 0;
status = scsiAutosenseFailed;
}
else {
status = scsiNonZeroStatus;
}
}
return (status);
}
OSErr
OriginalSCSI(
DeviceIdent scsiDevice,
const SCSI_CommandPtr scsiCommand,
UInt8 scsiCommandLen,
Ptr dataBuffer,
ByteCount dataLength,
UInt32 scsiFlags,
ByteCount *actualTransferCount,
UInt8 *scsiStatusBytePtr
)
{
OSErr status;
OSErr completionStatus;
short totalTries;
short getTries;
short iCount;
unsigned long watchdog;
unsigned long myTransferCount;
short scsiStatusByte;
short scsiMsgByte;
Boolean bufferHoldFlag;
SCSIInstr tib[4];
status = noErr;
bufferHoldFlag = FALSE;
scsiStatusByte = 0xFF;
scsiMsgByte = 0xFF;
myTransferCount = 0;
if (dataBuffer != NULL) {
tib[0].scOpcode = scInc;
tib[0].scParam1 = (unsigned long) dataBuffer;
tib[0].scParam2 = 1;
tib[1].scOpcode = scAdd;
tib[1].scParam1 = (unsigned long) &myTransferCount;
tib[1].scParam2 = 1;
tib[2].scOpcode = scLoop;
tib[2].scParam1 = (-2 * sizeof (SCSIInstr));
tib[2].scParam2 = dataLength / tib[0].scParam2;
tib[3].scOpcode = scStop;
tib[3].scParam1 = 0;
tib[3].scParam2 = 0;
}
if (IsVirtualMemoryRunning() && dataBuffer != NULL) {
#ifdef notdef
FailOSErr(
HoldMemory(dataBuffer, dataLength),
"\pCan't lock data buffer in physical memory"
);
#else
HoldMemory(dataBuffer, dataLength);
#endif
bufferHoldFlag = TRUE;
}
for (totalTries = 0; totalTries < kMaxSCSIRetries; totalTries++) {
for (getTries = 0; getTries < 4; getTries++) {
watchdog = TickCount() + 300;
while (ScsiBusBusy()) {
if ( TickCount() > watchdog) {
status = scsiBusy;
goto exit;
}
}
for (iCount = 0; iCount < 4; iCount++) {
if ((status = SCSIGet()) == noErr)
break;
}
if (status == noErr) {
break;
}
for (iCount = 0;
iCount < 100 && ScsiBusBusy();
iCount++)
;
}
if (status != noErr) {
status = scsiBusy;
goto exit;
}
if ((status = SCSISelect(scsiDevice.targetID)) != noErr) {
switch (status) {
case scBadParmsErr: status = scsiTIDInvalid; break;
case scCommErr: status = scsiDeviceNotThere; break;
case scArbNBErr: status = scsiBusy; break;
case scSequenceErr: status = scsiRequestInvalid; break;
}
goto exit;
}
status = SCSICmd((Ptr) scsiCommand, scsiCommandLen);
if (status != noErr) {
switch (status) {
case scCommErr: status = scsiCommandTimeout; break;
case scPhaseErr: status = scsiSequenceFailed; break;
}
}
if (status == noErr && dataBuffer != NULL) {
if (scsiFlags == scsiDirectionOut) {
status = SCSIWrite((Ptr) tib);
} else {
status = SCSIRead((Ptr) tib);
}
switch (status) {
case scCommErr: status = scsiCommandTimeout; break;
case scBadParmsErr: status = scsiRequestInvalid; break;
case scPhaseErr: status = noErr; break;
case scCompareErr: break;
}
}
completionStatus = SCSIComplete(
&scsiStatusByte,
&scsiMsgByte,
5 * 60L
);
if (status == noErr && completionStatus != noErr) {
switch (completionStatus) {
case scCommErr: status = scsiCommandTimeout; break;
case scPhaseErr: status = scsiSequenceFailed; break;
case scComplPhaseErr: status = scsiSequenceFailed; break;
}
}
if (completionStatus == noErr && scsiStatusByte == kScsiStatusBusy) {
watchdog = TickCount() + 15;
while (TickCount() < watchdog)
;
continue;
}
break;
}
exit:
if (bufferHoldFlag) {
(void) UnholdMemory(dataBuffer, dataLength);
}
if (actualTransferCount != NULL) {
*actualTransferCount = myTransferCount;
if (*actualTransferCount > dataLength) {
*actualTransferCount = dataLength;
}
}
if (scsiStatusByte == kScsiStatusGood
&& scsiMsgByte == kScsiStatusCheckCondition) {
scsiStatusByte = kScsiStatusCheckCondition;
}
if (status == noErr) {
switch (scsiStatusByte) {
case kScsiStatusGood: break;
case kScsiStatusBusy: status = scsiBusy; break;
case 0xFF: status = scsiProvideFail; break;
default: status = scsiNonZeroStatus; break;
}
}
if (status == noErr
&& (scsiFlags & scsiDirectionMask) != scsiDirectionNone
&& myTransferCount != dataLength) {
status = scsiDataRunError;
}
if (scsiStatusBytePtr != NULL) {
*scsiStatusBytePtr = scsiStatusByte;
}
return (status);
}
UInt16
GetCommandLength(
const SCSI_CommandPtr cmdPtr
)
{
unsigned short result;
switch (cmdPtr->scsi6.opcode & 0xE0) {
case (0 << 5): result = 6; break;
case (1 << 5):
case (2 << 5): result = 10; break;
case (5 << 5): result = 12; break;
default: result = 0; break;
}
return (result);
}
Boolean
IsVirtualMemoryRunning(void)
{
OSErr status;
long response;
status = Gestalt(gestaltVMAttr, &response);
return (status == noErr && ((response & (1 << gestaltVMPresent)) != 0));
}
void
AllocatePB()
{
OSErr status;
SCSIBusInquiryPB busInquiryPB;
#define PB (busInquiryPB)
if (gSCSIExecIOPBPtr == NULL) {
CLEAR(PB);
PB.scsiPBLength = sizeof PB;
PB.scsiFunctionCode = SCSIBusInquiry;
PB.scsiDevice.bus = 0xFF;
status = SCSIAction((SCSI_PB *) &PB);
if (status == noErr)
status = PB.scsiResult;
if (PB.scsiHiBusID == 0xFF) {
gSCSIHiBusID = -1;
} else {
gSCSIHiBusID = PB.scsiHiBusID;
}
gSCSIExecIOPBPtrLen = PB.scsiMaxIOpbSize;
if (gSCSIExecIOPBPtrLen != 0)
gSCSIExecIOPBPtr = (SCSIExecIOPB *) NewPtrClear(gSCSIExecIOPBPtrLen);
}
#undef PB
}