Home | History | Annotate | Line # | Download | only in dist
      1 /*
      2  * DoScsiCommand.c
      3  *
      4  * This is the common entry to the original and asynchronous SCSI Manager calls:
      5  * if the asynchronous SCSI Manager is requested, it calls it. Otherwise, it
      6  * calls the original SCSI Manager and executes Request Sense if necessary.
      7  *
      8  * This function returns "autosense" in the SCSI_Sense_Data area. This will
      9  * be formatted in the senseMessage string.
     10  */
     11 
     12 /*
     13  * Copyright 1992, 1993, 1997, 1998 by Apple Computer, Inc.
     14  *              All Rights Reserved
     15  *
     16  * Permission to use, copy, modify, and distribute this software and
     17  * its documentation for any purpose and without fee is hereby granted,
     18  * provided that the above copyright notice appears in all copies and
     19  * that both the copyright notice and this permission notice appear in
     20  * supporting documentation.
     21  *
     22  * APPLE COMPUTER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
     23  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     24  * FOR A PARTICULAR PURPOSE.
     25  *
     26  * IN NO EVENT SHALL APPLE COMPUTER BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
     27  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
     28  * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
     29  * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
     30  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     31  */
     32 
     33 #include "DoScsiCommand.h"
     34 #include "util.h"
     35 
     36 
     37 //
     38 // Defines
     39 //
     40 #define kSCSICommandTimeout     (5 * 1000L)         /* Five seconds             */
     41 /*
     42  * This is the maximum number of times we try to grab the SCSI Bus
     43  */
     44 #define kMaxSCSIRetries         40                  /* 10 seconds, 4 times/sec  */
     45 /*
     46  * This test is TRUE if the SCSI bus status indicates "busy" (which is the case
     47  * if either the BSY or SEL bit is set).
     48  */
     49 #ifndef kScsiStatBSY
     50 #define kScsiStatBSY            (1 << 6)
     51 #endif
     52 #ifndef kScsiStatSEL
     53 #define kScsiStatSEL            (1 << 1)
     54 #endif
     55 #define ScsiBusBusy()       ((SCSIStat() & (kScsiStatBSY | kScsiStatSEL)) != 0)
     56 
     57 
     58 //
     59 // Types
     60 //
     61 
     62 
     63 //
     64 // Global Constants
     65 //
     66 
     67 
     68 //
     69 // Global Variables
     70 //
     71 int             gSCSIHiBusID;
     72 SCSIExecIOPB    *gSCSIExecIOPBPtr;
     73 UInt32          gSCSIExecIOPBPtrLen;
     74 
     75 
     76 //
     77 // Forward declarations
     78 //
     79 UInt16 GetCommandLength(const SCSI_CommandPtr cmdPtr);
     80 Boolean IsVirtualMemoryRunning(void);
     81 
     82 OSErr OriginalSCSI(
     83     DeviceIdent             scsiDevice,
     84     const SCSI_CommandPtr   scsiCommand,
     85     UInt8                   scsiCommandLen,
     86     Ptr                     dataBuffer,
     87     ByteCount               dataLength,
     88     UInt32                  scsiFlags,
     89     ByteCount               *actualTransferCount,
     90     UInt8                   *scsiStatusByte
     91 );
     92 
     93 OSErr DoOriginalSCSICommand(
     94     DeviceIdent             scsiDevice,
     95     const SCSI_CommandPtr   theSCSICommand,
     96     uint16_t          cmdBlockLength,
     97     Ptr                     dataBuffer,
     98     ByteCount               dataLength,
     99     UInt32                  scsiFlags,
    100     ByteCount               *actualTransferCount,
    101     SCSI_Sense_Data         *sensePtr
    102 );
    103 
    104 
    105 //
    106 // Routines
    107 //
    108 
    109 /*
    110  * This returns TRUE if the command failed with "Illegal Request." We need this
    111  * so we can ignore LogSense or ReadDefectData if the device doesn't support
    112  * these functions.
    113  */
    114 Boolean
    115 IsIllegalRequest(
    116     OSErr                   scsiStatus,
    117     const SCSI_Sense_Data   *senseDataPtr
    118     )
    119 {
    120     Boolean                 result;
    121 #define SENSE   (*senseDataPtr)
    122 
    123     result = FALSE;
    124     if (scsiStatus == scsiNonZeroStatus
    125      && (SENSE.senseKey & kScsiSenseKeyMask) == kScsiSenseIllegalReq
    126      && SENSE.additionalSenseLength >= 4) {
    127 	switch ((SENSE.additionalSenseCode << 8) | SENSE.additionalSenseQualifier) {
    128 	case 0x0000:
    129 	case 0x2000:
    130 	case 0x2022:    /* Obsolete */
    131 	result = TRUE;
    132 	break;
    133 	default:
    134 	break;
    135 	}
    136     }
    137     return (result);
    138 #undef SENSE
    139 }
    140 
    141 
    142 /*
    143  * This returns TRUE if the command failed with Device Not Ready (No Media Present)
    144  */
    145 Boolean
    146 IsNoMedia(
    147     OSErr                   scsiStatus,
    148     const SCSI_Sense_Data   *senseDataPtr
    149     )
    150 {
    151     Boolean                 result;
    152 #define SENSE   (*senseDataPtr)
    153 
    154     result = FALSE;
    155     if (scsiStatus == scsiNonZeroStatus
    156      && (SENSE.senseKey & kScsiSenseKeyMask) == kScsiSenseNotReady
    157      && SENSE.additionalSenseLength >= 4) {
    158 	switch ((SENSE.additionalSenseCode << 8) | SENSE.additionalSenseQualifier) {
    159 	case 0x0000:
    160 	case 0x3A00:
    161 	result = TRUE;
    162 	break;
    163 	default:
    164 	break;
    165 	}
    166     }
    167     return (result);
    168 #undef SENSE
    169 }
    170 
    171 
    172 /*
    173  * Do one SCSI Command. If the device returns Check Condition, issue Request Sense
    174  * (original SCSI Manager only) and interpret the sense data. The original SCSI
    175  * command status is in SCB.status. If it is statusErr or scsiNonZeroStatus,
    176  * the sense data is in SCB.sense and the Request Sense status is in
    177  * SCB.requestSenseStatus.
    178  *
    179  * If sensePtr[0] is non-zero, there is a message.
    180  */
    181 OSErr
    182 DoSCSICommand(
    183     DeviceIdent             scsiDevice,
    184     ConstStr255Param        currentAction,
    185     const SCSI_CommandPtr   callerSCSICommand,
    186     Ptr                     dataBuffer,
    187     ByteCount               dataLength,
    188     UInt32                  scsiFlags,
    189     ByteCount               *actualTransferCount,
    190     SCSI_Sense_Data         *sensePtr,
    191     StringPtr               senseMessage
    192     )
    193 {
    194     OSErr                   status;
    195     SCSI_Command            theSCSICommand;
    196     uint16_t          cmdBlockLength;
    197 
    198 //      SpinSpinner(&gCurrentInfoPtr->spinnerRecord);
    199 //      ShowProgressAction(currentAction);
    200     /*
    201      * Store the LUN information in the command block - this is needed
    202      * for devices that only examine the command block for LUN values.
    203      * (On SCSI-II, the asynchronous SCSI Manager also includes the
    204      * LUN in the identify message).
    205      */
    206     theSCSICommand = *callerSCSICommand;
    207     theSCSICommand.scsi[1] &= ~0xE0;
    208     theSCSICommand.scsi[1] |= (scsiDevice.LUN & 0x03) << 5;
    209     cmdBlockLength = GetCommandLength(&theSCSICommand);
    210     if (senseMessage != NULL)
    211 	senseMessage[0] = 0;
    212     if (sensePtr != NULL)
    213 	sensePtr->errorCode = 0;
    214     if (scsiDevice.bus == kOriginalSCSIBusAdaptor) {
    215 	status = DoOriginalSCSICommand(
    216 	    scsiDevice,
    217 	    &theSCSICommand,
    218 	    cmdBlockLength,
    219 	    dataBuffer,
    220 	    dataLength,
    221 	    scsiFlags,
    222 	    actualTransferCount,
    223 	    sensePtr
    224 	    );
    225     }
    226     else {
    227 	clear_memory(gSCSIExecIOPBPtr, gSCSIExecIOPBPtrLen);
    228 #define PB  (*gSCSIExecIOPBPtr)
    229 	PB.scsiPBLength = gSCSIExecIOPBPtrLen;
    230 	PB.scsiFunctionCode = SCSIExecIO;
    231 	PB.scsiDevice = scsiDevice;
    232 	PB.scsiTimeout = kSCSICommandTimeout;
    233 	/*
    234 	 * Fiddle the flags so they're the least disruptive possible.
    235 	 */
    236 	PB.scsiFlags = scsiFlags | (scsiSIMQNoFreeze | scsiDontDisconnect);
    237 	if (sensePtr != NULL) {
    238 	PB.scsiSensePtr = (UInt8 *) sensePtr;
    239 	PB.scsiSenseLength = sizeof *sensePtr;
    240 	}
    241 	BlockMoveData(&theSCSICommand, &PB.scsiCDB.cdbBytes[0], cmdBlockLength);
    242 	PB.scsiCDBLength = cmdBlockLength;
    243 	if (dataBuffer != NULL) {
    244 	PB.scsiDataPtr = (UInt8 *) dataBuffer;
    245 	PB.scsiDataLength = dataLength;
    246 	PB.scsiDataType = scsiDataBuffer;
    247 	PB.scsiTransferType = scsiTransferPolled;
    248 	}
    249 	status = SCSIAction((SCSI_PB *) &PB);
    250 	if (status == noErr)
    251 	status = PB.scsiResult;
    252 	if (status == scsiSelectTimeout)
    253 	status = scsiDeviceNotThere;
    254 	if (actualTransferCount != NULL) {
    255 	/*
    256 	 * Make sure that the actual transfer count does not exceed
    257 	 * the allocation count (some devices spit extra data at us!)
    258 	 */
    259 	*actualTransferCount = dataLength - PB.scsiDataResidual;
    260 	if (*actualTransferCount > dataLength)
    261 	    *actualTransferCount = dataLength;
    262 	}
    263 #undef PB
    264     }
    265     if (status == scsiNonZeroStatus
    266      && sensePtr != NULL
    267      && sensePtr->errorCode != 0
    268      && senseMessage != NULL) {
    269 //          FormatSenseMessage(sensePtr, senseMessage);
    270 //          ShowProgressAction(senseMessage);
    271     }
    272     return (status);
    273 }
    274 
    275 
    276 /*
    277  * Do a command with autosense using the original SCSI manager.
    278  */
    279 OSErr
    280 DoOriginalSCSICommand(
    281     DeviceIdent             scsiDevice,
    282     const SCSI_CommandPtr   theSCSICommand,
    283     uint16_t          cmdBlockLength,
    284     Ptr                     dataBuffer,
    285     ByteCount               dataLength,
    286     UInt32                  scsiFlags,
    287     ByteCount               *actualTransferCount,
    288     SCSI_Sense_Data         *sensePtr
    289     )
    290 {
    291     OSErr                   status;
    292     UInt8                   scsiStatusByte;
    293     SCSI_Command            scsiStatusCommand;
    294 
    295     status = OriginalSCSI(
    296 	    scsiDevice,
    297 	    theSCSICommand,
    298 	    cmdBlockLength,
    299 	    dataBuffer,
    300 	    dataLength,
    301 	    scsiFlags,
    302 	    actualTransferCount,
    303 	    &scsiStatusByte
    304 	);
    305     if (status == scsiNonZeroStatus
    306      && scsiStatusByte == kScsiStatusCheckCondition
    307      && sensePtr != NULL) {
    308 	CLEAR(scsiStatusCommand);
    309 	CLEAR(*sensePtr);
    310 	scsiStatusCommand.scsi6.opcode = kScsiCmdRequestSense;
    311 	scsiStatusCommand.scsi[1] |= (scsiDevice.LUN & 0x03) << 5;
    312 	scsiStatusCommand.scsi6.len = sizeof *sensePtr;
    313 	status = OriginalSCSI(
    314 	    scsiDevice,
    315 	    &scsiStatusCommand,
    316 	    sizeof scsiStatusCommand.scsi6,
    317 	    (Ptr) sensePtr,
    318 	    sizeof *sensePtr,
    319 	    scsiDirectionIn,
    320 	    NULL,
    321 	    &scsiStatusByte
    322 	    );
    323 	if (status != noErr && status != scsiDataRunError) {
    324 #ifdef notdef
    325 	if (gDebugOnError && scsiStatusByte != kScsiStatusCheckCondition) {
    326 	    Str255          work;
    327 
    328 	    pstrcpy(work, "\pAutosense failed ");
    329 	    AppendSigned(work, status);
    330 	    AppendChar(work, ' ');
    331 	    AppendHexLeadingZeros(work, scsiStatusByte, 2);
    332 	    DebugStr(work);
    333 	}
    334 #endif
    335 	sensePtr->errorCode = 0;
    336 	status = scsiAutosenseFailed;
    337 	}
    338 	else {
    339 	status = scsiNonZeroStatus;
    340 	}
    341     }
    342     return (status);
    343 }
    344 
    345 
    346 OSErr
    347 OriginalSCSI(
    348     DeviceIdent             scsiDevice,
    349     const SCSI_CommandPtr   scsiCommand,
    350     UInt8                   scsiCommandLen,
    351     Ptr                     dataBuffer,
    352     ByteCount               dataLength,
    353     UInt32                  scsiFlags,
    354     ByteCount               *actualTransferCount,
    355     UInt8                   *scsiStatusBytePtr
    356     )
    357 {
    358     OSErr                   status;             /* Final status             */
    359     OSErr                   completionStatus;   /* Status from ScsiComplete */
    360     short                   totalTries;         /* Get/Select retries       */
    361     short                   getTries;           /* Get retries              */
    362     short                   iCount;             /* Bus free counter         */
    363     uint32_t           watchdog;           /* Timeout after this       */
    364     uint32_t           myTransferCount;    /* Gets TIB loop counter    */
    365     short                   scsiStatusByte;     /* Gets SCSIComplete result */
    366     short                   scsiMsgByte;        /* Gets SCSIComplete result */
    367     Boolean                 bufferHoldFlag;
    368     /*
    369      * The TIB has the following format:
    370      *  [0] scInc   user buffer         transferQuantum or transferSize
    371      *  [1] scAdd   &theTransferCount   1
    372      *  [2] scLoop  -> tib[0]           transferSize / transferQuantum
    373      *  [3] scStop
    374      * The intent of this is to return, in actualTransferCount, the number
    375      * of times we cycled through the tib[] loop. This will be the actual
    376      * transfer count if transferQuantum equals one, or the number of
    377      * "blocks" if transferQuantum is the length of one sector.
    378      */
    379     SCSIInstr               tib[4];             /* Current TIB              */
    380 
    381     status = noErr;
    382     bufferHoldFlag = FALSE;
    383     scsiStatusByte = 0xFF;
    384     scsiMsgByte = 0xFF;
    385     myTransferCount = 0;
    386     /*
    387      * If there is a data transfer, setup the tib.
    388      */
    389     if (dataBuffer != NULL) {
    390 	tib[0].scOpcode = scInc;
    391 	tib[0].scParam1 = (uint32_t) dataBuffer;
    392 	tib[0].scParam2 = 1;
    393 	tib[1].scOpcode = scAdd;
    394 	tib[1].scParam1 = (uint32_t) &myTransferCount;
    395 	tib[1].scParam2 = 1;
    396 	tib[2].scOpcode = scLoop;
    397 	tib[2].scParam1 = (-2 * sizeof (SCSIInstr));
    398 	tib[2].scParam2 = dataLength / tib[0].scParam2;
    399 	tib[3].scOpcode = scStop;
    400 	tib[3].scParam1 = 0;
    401 	tib[3].scParam2 = 0;
    402     }
    403     if (IsVirtualMemoryRunning() && dataBuffer != NULL) {
    404 	/*
    405 	 * Lock down the user buffer, if any. In a real-world application
    406 	 * or driver, this would be done before calling the SCSI interface.
    407 	 */
    408 #ifdef notdef
    409 	FailOSErr(
    410 	HoldMemory(dataBuffer, dataLength),
    411 	"\pCan't lock data buffer in physical memory"
    412 	);
    413 #else
    414 	HoldMemory(dataBuffer, dataLength);
    415 #endif
    416 	bufferHoldFlag = TRUE;
    417     }
    418     /*
    419      * Arbitrate for the scsi bus.  This will fail if some other device is
    420      * accessing the bus at this time (which is unlikely).
    421      *
    422      *** Do not set breakpoints or call any functions that may require device
    423      *** I/O (such as display code that accesses font resources between
    424      *** SCSIGet and SCSIComplete,
    425      *
    426      */
    427     for (totalTries = 0; totalTries < kMaxSCSIRetries; totalTries++) {
    428 	for (getTries = 0; getTries < 4; getTries++) {
    429 	    /*
    430 	     * Wait for the bus to go free.
    431 	     */
    432 	    watchdog = TickCount() + 300;       /* 5 second timeout         */
    433 	    while (ScsiBusBusy()) {
    434 		if (/*gStopNow || StopNow() ||*/ TickCount() > watchdog) {
    435 		    status = scsiBusy;
    436 		    goto exit;
    437 		}
    438 	    }
    439 	    /*
    440 	     * The bus is free, try to grab it
    441 	     */
    442 	    for (iCount = 0; iCount < 4; iCount++) {
    443 		if ((status = SCSIGet()) == noErr)
    444 		    break;
    445 	    }
    446 	    if (status == noErr) {
    447 		break;                          /* Success: we have the bus */
    448 	    }
    449 	    /*
    450 	     * The bus became busy again. Try to wait for it to go free.
    451 	     */
    452 	    for (iCount = 0;
    453 		/*gStopNow == FALSE && StopNow() == FALSE &&*/ iCount < 100 && ScsiBusBusy();
    454 		iCount++)
    455 		;
    456 	} /* The getTries loop */
    457 	if (status != noErr) {
    458 	    /*
    459 	     * The SCSI Manager thinks the bus is not busy and not selected,
    460 	     * but "someone" has set its internal semaphore that signals
    461 	     * that the SCSI Manager itself is busy. The application will have
    462 	     * to handle this problem. (We tried getTries * 4 times).
    463 	     */
    464 	    status = scsiBusy;
    465 	    goto exit;
    466 	}
    467 	/*
    468 	 * We now own the SCSI bus. Try to select the device.
    469 	 */
    470 	if ((status = SCSISelect(scsiDevice.targetID)) != noErr) {
    471 	    switch (status) {
    472 	    /*
    473 	     * We get scBadParmsErr if we try to arbitrate for the initiator.
    474 	     */
    475 	    case scBadParmsErr: status = scsiTIDInvalid;        break;
    476 	    case scCommErr:     status = scsiDeviceNotThere;    break;
    477 	    case scArbNBErr:    status = scsiBusy;              break;
    478 	    case scSequenceErr: status = scsiRequestInvalid;    break;
    479 	    }
    480 	    goto exit;
    481 	}
    482 	/*
    483 	 * From this point on, we must exit through SCSIComplete() even if an
    484 	 * error is detected. Send a command to the selected device. There are
    485 	 * several failure modes, including an illegal command (such as a
    486 	 * write to a read-only device). If the command failed because of
    487 	 * "device busy", we will try it again.
    488 	 */
    489 	status = SCSICmd((Ptr) scsiCommand, scsiCommandLen);
    490 	if (status != noErr) {
    491 	    switch (status) {
    492 	    case scCommErr:     status = scsiCommandTimeout;    break;
    493 	    case scPhaseErr:    status = scsiSequenceFailed;    break;
    494 	    }
    495 	}
    496 	if (status == noErr && dataBuffer != NULL) {
    497 	    /*
    498 	     * This command requires a data transfer.
    499 	     */
    500 	    if (scsiFlags == scsiDirectionOut) {
    501 		status = SCSIWrite((Ptr) tib);
    502 	    } else {
    503 		status = SCSIRead((Ptr) tib);
    504 	    }
    505 	    switch (status) {
    506 	    case scCommErr:     status = scsiCommandTimeout;        break;
    507 	    case scBadParmsErr: status = scsiRequestInvalid;        break;
    508 	    case scPhaseErr:    status = noErr; /* Don't care */    break;
    509 	    case scCompareErr:                  /* Can't happen */  break;
    510 	    }
    511 	}
    512 	/*
    513 	 * SCSIComplete "runs" the bus-phase algorithm until the bitter end,
    514 	 * returning the status and command-completion message bytes..
    515 	 */
    516 	completionStatus = SCSIComplete(
    517 	    &scsiStatusByte,
    518 	    &scsiMsgByte,
    519 	    5 * 60L
    520 	    );
    521 	if (status == noErr && completionStatus != noErr) {
    522 	    switch (completionStatus) {
    523 	    case scCommErr:         status = scsiCommandTimeout;    break;
    524 	    case scPhaseErr:        status = scsiSequenceFailed;    break;
    525 	    case scComplPhaseErr:   status = scsiSequenceFailed;    break;
    526 	    }
    527 	}
    528 	if (completionStatus == noErr && scsiStatusByte == kScsiStatusBusy) {
    529 	    /*
    530 	     * ScsiComplete is happy. If the device is busy,
    531 	     * pause for 1/4 second and try again.
    532 	     */
    533 	    watchdog = TickCount() + 15;
    534 	    while (TickCount() < watchdog)
    535 		;
    536 	    continue;               /* Do next totalTries attempt       */
    537 	}
    538 	/*
    539 	 * This is the normal exit (success) or final failure exit.
    540 	 */
    541 	break;
    542     } /* totalTries loop */
    543 exit:
    544 
    545     if (bufferHoldFlag) {
    546 	(void) UnholdMemory(dataBuffer, dataLength);
    547     }
    548     /*
    549      * Return the number of bytes transferred to the caller. If the caller
    550      * supplied an actual count and the count is no greater than the maximum,
    551      * ignore any phase errors.
    552      */
    553     if (actualTransferCount != NULL) {
    554 	*actualTransferCount = myTransferCount;
    555 	if (*actualTransferCount > dataLength) {
    556 	    *actualTransferCount = dataLength;
    557 	}
    558     }
    559     /*
    560      * Also, there is a bug in the combination of System 7.0.1 and the 53C96
    561      * that may cause the real SCSI Status Byte to be in the Message byte.
    562      */
    563     if (scsiStatusByte == kScsiStatusGood
    564 	    && scsiMsgByte == kScsiStatusCheckCondition) {
    565 	scsiStatusByte = kScsiStatusCheckCondition;
    566     }
    567     if (status == noErr) {
    568 	switch (scsiStatusByte) {
    569 	case kScsiStatusGood:                               break;
    570 	case kScsiStatusBusy:   status = scsiBusy;          break;
    571 	case 0xFF:              status = scsiProvideFail;   break;
    572 	default:                status = scsiNonZeroStatus; break;
    573 	}
    574     }
    575     if (status == noErr
    576 	    && (scsiFlags & scsiDirectionMask) != scsiDirectionNone
    577 	    && myTransferCount != dataLength) {
    578 	status = scsiDataRunError;
    579     }
    580     if (scsiStatusBytePtr != NULL) {
    581 	*scsiStatusBytePtr = scsiStatusByte;
    582     }
    583     return (status);
    584 }
    585 
    586 
    587 UInt16
    588 GetCommandLength(
    589     const SCSI_CommandPtr   cmdPtr
    590     )
    591 {
    592     uint16_t          result;
    593     /*
    594      * Look at the "group code" in the command operation. Return zero
    595      * error for the reserved (3, 4) and vendor-specific command (6, 7)
    596      * command groups. Otherwise, set the command length from the group code
    597      * value as specified in the SCSI-II spec.
    598      */
    599     switch (cmdPtr->scsi6.opcode & 0xE0) {
    600     case (0 << 5):  result = 6;     break;
    601     case (1 << 5):
    602     case (2 << 5):  result = 10;    break;
    603     case (5 << 5):  result = 12;    break;
    604     default:        result = 0;     break;
    605     }
    606     return (result);
    607 }
    608 
    609 
    610 Boolean
    611 IsVirtualMemoryRunning(void)
    612 {
    613     OSErr                       status;
    614     long                        response;
    615 
    616     status = Gestalt(gestaltVMAttr, &response);
    617     /*
    618      * VM is active iff Gestalt succeeded and the response is appropriate.
    619      */
    620     return (status == noErr && ((response & (1 << gestaltVMPresent)) != 0));
    621 }
    622 
    623 
    624 void
    625 AllocatePB()
    626 {
    627     OSErr           status;
    628     SCSIBusInquiryPB    busInquiryPB;
    629 #define PB          (busInquiryPB)
    630 
    631     if (gSCSIExecIOPBPtr == NULL) {
    632 	CLEAR(PB);
    633 	PB.scsiPBLength = sizeof PB;
    634 	PB.scsiFunctionCode = SCSIBusInquiry;
    635 	PB.scsiDevice.bus = 0xFF;       /* Get info about the XPT */
    636 	status = SCSIAction((SCSI_PB *) &PB);
    637 	if (status == noErr)
    638 	    status = PB.scsiResult;
    639 	if (PB.scsiHiBusID == 0xFF) {
    640 	    gSCSIHiBusID = -1;
    641 	} else {
    642 	    gSCSIHiBusID = PB.scsiHiBusID;
    643 	}
    644 	gSCSIExecIOPBPtrLen = PB.scsiMaxIOpbSize;
    645 	if (gSCSIExecIOPBPtrLen != 0)
    646 	    gSCSIExecIOPBPtr = (SCSIExecIOPB *) NewPtrClear(gSCSIExecIOPBPtrLen);
    647     }
    648 #undef PB
    649 }
    650