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.
8 * This function returns "autosense" in the SCSI_Sense_Data area. This will
9 * be formatted in the senseMessage string.
13 * Copyright 1992, 1993, 1997, 1998 by Apple Computer, Inc.
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.
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.
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.
33 #include "DoScsiCommand.h"
40 #define kSCSICommandTimeout (5 * 1000L) /* Five seconds */
42 * This is the maximum number of times we try to grab the SCSI Bus
44 #define kMaxSCSIRetries 40 /* 10 seconds, 4 times/sec */
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).
50 #define kScsiStatBSY (1 << 6)
53 #define kScsiStatSEL (1 << 1)
55 #define ScsiBusBusy() ((SCSIStat() & (kScsiStatBSY | kScsiStatSEL)) != 0)
72 SCSIExecIOPB
*gSCSIExecIOPBPtr
;
73 UInt32 gSCSIExecIOPBPtrLen
;
77 // Forward declarations
79 UInt16
GetCommandLength(const SCSI_CommandPtr cmdPtr
);
80 Boolean
IsVirtualMemoryRunning(void);
83 DeviceIdent scsiDevice
,
84 const SCSI_CommandPtr scsiCommand
,
89 ByteCount
*actualTransferCount
,
93 OSErr
DoOriginalSCSICommand(
94 DeviceIdent scsiDevice
,
95 const SCSI_CommandPtr theSCSICommand
,
96 unsigned short cmdBlockLength
,
100 ByteCount
*actualTransferCount
,
101 SCSI_Sense_Data
*sensePtr
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
117 const SCSI_Sense_Data
*senseDataPtr
121 #define SENSE (*senseDataPtr)
124 if (scsiStatus
== scsiNonZeroStatus
125 && (SENSE
.senseKey
& kScsiSenseKeyMask
) == kScsiSenseIllegalReq
126 && SENSE
.additionalSenseLength
>= 4) {
127 switch ((SENSE
.additionalSenseCode
<< 8) | SENSE
.additionalSenseQualifier
) {
130 case 0x2022: /* Obsolete */
143 * This returns TRUE if the command failed with Device Not Ready (No Media Present)
148 const SCSI_Sense_Data
*senseDataPtr
152 #define SENSE (*senseDataPtr)
155 if (scsiStatus
== scsiNonZeroStatus
156 && (SENSE
.senseKey
& kScsiSenseKeyMask
) == kScsiSenseNotReady
157 && SENSE
.additionalSenseLength
>= 4) {
158 switch ((SENSE
.additionalSenseCode
<< 8) | SENSE
.additionalSenseQualifier
) {
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.
179 * If sensePtr[0] is non-zero, there is a message.
183 DeviceIdent scsiDevice
,
184 ConstStr255Param currentAction
,
185 const SCSI_CommandPtr callerSCSICommand
,
187 ByteCount dataLength
,
189 ByteCount
*actualTransferCount
,
190 SCSI_Sense_Data
*sensePtr
,
191 StringPtr senseMessage
195 SCSI_Command theSCSICommand
;
196 unsigned short cmdBlockLength
;
198 // SpinSpinner(&gCurrentInfoPtr->spinnerRecord);
199 // ShowProgressAction(currentAction);
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).
206 theSCSICommand
= *callerSCSICommand
;
207 theSCSICommand
.scsi
[1] &= ~0xE0;
208 theSCSICommand
.scsi
[1] |= (scsiDevice
.LUN
& 0x03) << 5;
209 cmdBlockLength
= GetCommandLength(&theSCSICommand
);
210 if (senseMessage
!= NULL
)
212 if (sensePtr
!= NULL
)
213 sensePtr
->errorCode
= 0;
214 if (scsiDevice
.bus
== kOriginalSCSIBusAdaptor
) {
215 status
= DoOriginalSCSICommand(
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
;
234 * Fiddle the flags so they're the least disruptive possible.
236 PB
.scsiFlags
= scsiFlags
| (scsiSIMQNoFreeze
| scsiDontDisconnect
);
237 if (sensePtr
!= NULL
) {
238 PB
.scsiSensePtr
= (UInt8
*) sensePtr
;
239 PB
.scsiSenseLength
= sizeof *sensePtr
;
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
;
249 status
= SCSIAction((SCSI_PB
*) &PB
);
251 status
= PB
.scsiResult
;
252 if (status
== scsiSelectTimeout
)
253 status
= scsiDeviceNotThere
;
254 if (actualTransferCount
!= NULL
) {
256 * Make sure that the actual transfer count does not exceed
257 * the allocation count (some devices spit extra data at us!)
259 *actualTransferCount
= dataLength
- PB
.scsiDataResidual
;
260 if (*actualTransferCount
> dataLength
)
261 *actualTransferCount
= dataLength
;
265 if (status
== scsiNonZeroStatus
267 && sensePtr
->errorCode
!= 0
268 && senseMessage
!= NULL
) {
269 // FormatSenseMessage(sensePtr, senseMessage);
270 // ShowProgressAction(senseMessage);
277 * Do a command with autosense using the original SCSI manager.
280 DoOriginalSCSICommand(
281 DeviceIdent scsiDevice
,
282 const SCSI_CommandPtr theSCSICommand
,
283 unsigned short cmdBlockLength
,
285 ByteCount dataLength
,
287 ByteCount
*actualTransferCount
,
288 SCSI_Sense_Data
*sensePtr
292 UInt8 scsiStatusByte
;
293 SCSI_Command scsiStatusCommand
;
295 status
= OriginalSCSI(
305 if (status
== scsiNonZeroStatus
306 && scsiStatusByte
== kScsiStatusCheckCondition
307 && sensePtr
!= NULL
) {
308 CLEAR(scsiStatusCommand
);
310 scsiStatusCommand
.scsi6
.opcode
= kScsiCmdRequestSense
;
311 scsiStatusCommand
.scsi
[1] |= (scsiDevice
.LUN
& 0x03) << 5;
312 scsiStatusCommand
.scsi6
.len
= sizeof *sensePtr
;
313 status
= OriginalSCSI(
316 sizeof scsiStatusCommand
.scsi6
,
323 if (status
!= noErr
&& status
!= scsiDataRunError
) {
325 if (gDebugOnError
&& scsiStatusByte
!= kScsiStatusCheckCondition
) {
328 pstrcpy(work
, "\pAutosense failed ");
329 AppendSigned(work
, status
);
330 AppendChar(work
, ' ');
331 AppendHexLeadingZeros(work
, scsiStatusByte
, 2);
335 sensePtr
->errorCode
= 0;
336 status
= scsiAutosenseFailed
;
339 status
= scsiNonZeroStatus
;
348 DeviceIdent scsiDevice
,
349 const SCSI_CommandPtr scsiCommand
,
350 UInt8 scsiCommandLen
,
352 ByteCount dataLength
,
354 ByteCount
*actualTransferCount
,
355 UInt8
*scsiStatusBytePtr
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 unsigned long watchdog
; /* Timeout after this */
364 unsigned long myTransferCount
; /* Gets TIB loop counter */
365 short scsiStatusByte
; /* Gets SCSIComplete result */
366 short scsiMsgByte
; /* Gets SCSIComplete result */
367 Boolean bufferHoldFlag
;
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
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.
379 SCSIInstr tib
[4]; /* Current TIB */
382 bufferHoldFlag
= FALSE
;
383 scsiStatusByte
= 0xFF;
387 * If there is a data transfer, setup the tib.
389 if (dataBuffer
!= NULL
) {
390 tib
[0].scOpcode
= scInc
;
391 tib
[0].scParam1
= (unsigned long) dataBuffer
;
393 tib
[1].scOpcode
= scAdd
;
394 tib
[1].scParam1
= (unsigned long) &myTransferCount
;
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
;
403 if (IsVirtualMemoryRunning() && dataBuffer
!= NULL
) {
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.
410 HoldMemory(dataBuffer
, dataLength
),
411 "\pCan't lock data buffer in physical memory"
414 HoldMemory(dataBuffer
, dataLength
);
416 bufferHoldFlag
= TRUE
;
419 * Arbitrate for the scsi bus. This will fail if some other device is
420 * accessing the bus at this time (which is unlikely).
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,
427 for (totalTries
= 0; totalTries
< kMaxSCSIRetries
; totalTries
++) {
428 for (getTries
= 0; getTries
< 4; getTries
++) {
430 * Wait for the bus to go free.
432 watchdog
= TickCount() + 300; /* 5 second timeout */
433 while (ScsiBusBusy()) {
434 if (/*gStopNow || StopNow() ||*/ TickCount() > watchdog
) {
440 * The bus is free, try to grab it
442 for (iCount
= 0; iCount
< 4; iCount
++) {
443 if ((status
= SCSIGet()) == noErr
)
446 if (status
== noErr
) {
447 break; /* Success: we have the bus */
450 * The bus became busy again. Try to wait for it to go free.
453 /*gStopNow == FALSE && StopNow() == FALSE &&*/ iCount
< 100 && ScsiBusBusy();
456 } /* The getTries loop */
457 if (status
!= noErr
) {
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).
468 * We now own the SCSI bus. Try to select the device.
470 if ((status
= SCSISelect(scsiDevice
.targetID
)) != noErr
) {
473 * We get scBadParmsErr if we try to arbitrate for the initiator.
475 case scBadParmsErr
: status
= scsiTIDInvalid
; break;
476 case scCommErr
: status
= scsiDeviceNotThere
; break;
477 case scArbNBErr
: status
= scsiBusy
; break;
478 case scSequenceErr
: status
= scsiRequestInvalid
; break;
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.
489 status
= SCSICmd((Ptr
) scsiCommand
, scsiCommandLen
);
490 if (status
!= noErr
) {
492 case scCommErr
: status
= scsiCommandTimeout
; break;
493 case scPhaseErr
: status
= scsiSequenceFailed
; break;
496 if (status
== noErr
&& dataBuffer
!= NULL
) {
498 * This command requires a data transfer.
500 if (scsiFlags
== scsiDirectionOut
) {
501 status
= SCSIWrite((Ptr
) tib
);
503 status
= SCSIRead((Ptr
) tib
);
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;
513 * SCSIComplete "runs" the bus-phase algorithm until the bitter end,
514 * returning the status and command-completion message bytes..
516 completionStatus
= SCSIComplete(
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;
528 if (completionStatus
== noErr
&& scsiStatusByte
== kScsiStatusBusy
) {
530 * ScsiComplete is happy. If the device is busy,
531 * pause for 1/4 second and try again.
533 watchdog
= TickCount() + 15;
534 while (TickCount() < watchdog
)
536 continue; /* Do next totalTries attempt */
539 * This is the normal exit (success) or final failure exit.
542 } /* totalTries loop */
545 if (bufferHoldFlag
) {
546 (void) UnholdMemory(dataBuffer
, dataLength
);
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.
553 if (actualTransferCount
!= NULL
) {
554 *actualTransferCount
= myTransferCount
;
555 if (*actualTransferCount
> dataLength
) {
556 *actualTransferCount
= dataLength
;
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.
563 if (scsiStatusByte
== kScsiStatusGood
564 && scsiMsgByte
== kScsiStatusCheckCondition
) {
565 scsiStatusByte
= kScsiStatusCheckCondition
;
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;
576 && (scsiFlags
& scsiDirectionMask
) != scsiDirectionNone
577 && myTransferCount
!= dataLength
) {
578 status
= scsiDataRunError
;
580 if (scsiStatusBytePtr
!= NULL
) {
581 *scsiStatusBytePtr
= scsiStatusByte
;
589 const SCSI_CommandPtr cmdPtr
592 unsigned short result
;
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.
599 switch (cmdPtr
->scsi6
.opcode
& 0xE0) {
600 case (0 << 5): result
= 6; break;
602 case (2 << 5): result
= 10; break;
603 case (5 << 5): result
= 12; break;
604 default: result
= 0; break;
611 IsVirtualMemoryRunning(void)
616 status
= Gestalt(gestaltVMAttr
, &response
);
618 * VM is active iff Gestalt succeeded and the response is appropriate.
620 return (status
== noErr
&& ((response
& (1 << gestaltVMPresent
)) != 0));
628 SCSIBusInquiryPB busInquiryPB
;
629 #define PB (busInquiryPB)
631 if (gSCSIExecIOPBPtr
== NULL
) {
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
);
638 status
= PB
.scsiResult
;
639 if (PB
.scsiHiBusID
== 0xFF) {
642 gSCSIHiBusID
= PB
.scsiHiBusID
;
644 gSCSIExecIOPBPtrLen
= PB
.scsiMaxIOpbSize
;
645 if (gSCSIExecIOPBPtrLen
!= 0)
646 gSCSIExecIOPBPtr
= (SCSIExecIOPB
*) NewPtrClear(gSCSIExecIOPBPtrLen
);