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