xref: /netbsd-src/external/bsd/pdisk/dist/DoSCSICommand.c (revision 48a628ae0434c4247b560ad8f2eb1dc06d0dd070)
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
IsIllegalRequest(OSErr scsiStatus,const SCSI_Sense_Data * senseDataPtr)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
IsNoMedia(OSErr scsiStatus,const SCSI_Sense_Data * senseDataPtr)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
DoSCSICommand(DeviceIdent scsiDevice,ConstStr255Param currentAction,const SCSI_CommandPtr callerSCSICommand,Ptr dataBuffer,ByteCount dataLength,UInt32 scsiFlags,ByteCount * actualTransferCount,SCSI_Sense_Data * sensePtr,StringPtr senseMessage)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
DoOriginalSCSICommand(DeviceIdent scsiDevice,const SCSI_CommandPtr theSCSICommand,uint16_t cmdBlockLength,Ptr dataBuffer,ByteCount dataLength,UInt32 scsiFlags,ByteCount * actualTransferCount,SCSI_Sense_Data * sensePtr)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
OriginalSCSI(DeviceIdent scsiDevice,const SCSI_CommandPtr scsiCommand,UInt8 scsiCommandLen,Ptr dataBuffer,ByteCount dataLength,UInt32 scsiFlags,ByteCount * actualTransferCount,UInt8 * scsiStatusBytePtr)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
GetCommandLength(const SCSI_CommandPtr cmdPtr)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
IsVirtualMemoryRunning(void)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
AllocatePB()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