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//
71int             gSCSIHiBusID;
72SCSIExecIOPB    *gSCSIExecIOPBPtr;
73UInt32          gSCSIExecIOPBPtrLen;
74
75
76//
77// Forward declarations
78//
79UInt16 GetCommandLength(const SCSI_CommandPtr cmdPtr);
80Boolean IsVirtualMemoryRunning(void);
81
82OSErr 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
93OSErr DoOriginalSCSICommand(
94    DeviceIdent             scsiDevice,
95    const SCSI_CommandPtr   theSCSICommand,
96    unsigned short          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 */
114Boolean
115IsIllegalRequest(
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 */
145Boolean
146IsNoMedia(
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 */
181OSErr
182DoSCSICommand(
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    unsigned short          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 */
279OSErr
280DoOriginalSCSICommand(
281    DeviceIdent             scsiDevice,
282    const SCSI_CommandPtr   theSCSICommand,
283    unsigned short          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
346OSErr
347OriginalSCSI(
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    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;
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 = (unsigned long) dataBuffer;
392	tib[0].scParam2 = 1;
393	tib[1].scOpcode = scAdd;
394	tib[1].scParam1 = (unsigned long) &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 */
543exit:
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
587UInt16
588GetCommandLength(
589    const SCSI_CommandPtr   cmdPtr
590    )
591{
592    unsigned short          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
610Boolean
611IsVirtualMemoryRunning(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
624void
625AllocatePB()
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