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