|
|
1.1 ! root 1: /* ! 2: * AMD_SCSI.m - top-level module for AMD 53C974/79C974 PCI SCSI driver. ! 3: * ! 4: * HISTORY ! 5: * 21 Oct 94 Doug Mitchell at NeXT ! 6: * Created. ! 7: */ ! 8: ! 9: #import "AMD_SCSI.h" ! 10: #import "AMD_Private.h" ! 11: #import "AMD_x86.h" ! 12: #import "AMD_Chip.h" ! 13: #import "AMD_ddm.h" ! 14: #import "AMD_Regs.h" ! 15: #import <mach/message.h> ! 16: #import <driverkit/generalFuncs.h> ! 17: #import <driverkit/interruptMsg.h> ! 18: #import <driverkit/align.h> ! 19: #import <driverkit/kernelDriver.h> ! 20: #import <kernserv/prototypes.h> ! 21: ! 22: static void AMDTimeout(void *arg); ! 23: ! 24: /* ! 25: * Template for command message sent to the I/O thread. ! 26: */ ! 27: static msg_header_t cmdMessageTemplate = { ! 28: 0, // msg_unused ! 29: 1, // msg_simple ! 30: sizeof(msg_header_t), // msg_size ! 31: MSG_TYPE_NORMAL, // msg_type ! 32: PORT_NULL, // msg_local_port ! 33: PORT_NULL, // msg_remote_port - TO ! 34: // BE FILLED IN ! 35: IO_COMMAND_MSG // msg_id ! 36: }; ! 37: ! 38: /* ! 39: * Template for timeout message. ! 40: */ ! 41: static msg_header_t timeoutMsgTemplate = { ! 42: 0, // msg_unused ! 43: 1, // msg_simple ! 44: sizeof(msg_header_t), // msg_size ! 45: MSG_TYPE_NORMAL, // msg_type ! 46: PORT_NULL, // msg_local_port ! 47: PORT_NULL, // msg_remote_port - TO ! 48: // BE FILLED IN ! 49: IO_TIMEOUT_MSG // msg_id ! 50: }; ! 51: ! 52: ! 53: @implementation AMD_SCSI ! 54: ! 55: /* ! 56: * Create and initialize one instance of AMD_SCSI. The work is done by ! 57: * architecture- and chip-specific modules. ! 58: */ ! 59: + (BOOL)probe:deviceDescription ! 60: { ! 61: AMD_SCSI *inst = [self alloc]; ! 62: ! 63: ! 64: if([inst archInit:deviceDescription] == nil) { ! 65: return NO; ! 66: } ! 67: else { ! 68: return YES; ! 69: } ! 70: } ! 71: ! 72: - free ! 73: { ! 74: commandBuf cmdBuf; ! 75: ! 76: /* ! 77: * First kill the I/O thread if running. ! 78: */ ! 79: if(ioThreadRunning) { ! 80: cmdBuf.op = CO_Abort; ! 81: cmdBuf.scsiReq = NULL; ! 82: [self executeCmdBuf:&cmdBuf]; ! 83: } ! 84: ! 85: if(commandLock) { ! 86: [commandLock free]; ! 87: } ! 88: if(mdlFree) { ! 89: IOFree(mdlFree, MDL_SIZE * 2 * sizeof(vm_address_t)); ! 90: } ! 91: return [super free]; ! 92: } ! 93: ! 94: /* ! 95: * Our max DMA size is 64 K, derived from using 18 MDL entries (note the ! 96: * first and last entries can refer to chunks as small as 4 bytes). ! 97: */ ! 98: - (unsigned)maxDmaSize ! 99: { ! 100: return AMD_DMA_PAGE_SIZE * (MDL_SIZE - 2); ! 101: } ! 102: ! 103: /* ! 104: * Return required DMA alignment for current architecture. ! 105: */ ! 106: - (void)getDMAAlignment : (IODMAAlignment *)alignment; ! 107: { ! 108: alignment->readStart = AMD_READ_START_ALIGN; ! 109: alignment->writeStart = AMD_WRITE_START_ALIGN; ! 110: alignment->readLength = AMD_READ_LENGTH_ALIGN; ! 111: alignment->writeLength = AMD_WRITE_LENGTH_ALIGN; ! 112: } ! 113: ! 114: /* ! 115: * Statistics support. ! 116: */ ! 117: - (unsigned int) numQueueSamples ! 118: { ! 119: return totalCommands; ! 120: } ! 121: ! 122: ! 123: - (unsigned int) sumQueueLengths ! 124: { ! 125: return queueLenTotal; ! 126: } ! 127: ! 128: ! 129: - (unsigned int) maxQueueLength ! 130: { ! 131: return maxQueueLen; ! 132: } ! 133: ! 134: ! 135: - (void)resetStats ! 136: { ! 137: totalCommands = 0; ! 138: queueLenTotal = 0; ! 139: maxQueueLen = 0; ! 140: } ! 141: ! 142: /* ! 143: * Do a SCSI command, as specified by an IOSCSIRequest. All the ! 144: * work is done by the I/O thread. ! 145: */ ! 146: - (sc_status_t) executeRequest : (IOSCSIRequest *)scsiReq ! 147: buffer : (void *)buffer ! 148: client : (vm_task_t)client ! 149: { ! 150: commandBuf cmdBuf; ! 151: ! 152: ddm_exp("executeRequest: cmdBuf 0x%x maxTransfer 0x%x\n", ! 153: &cmdBuf, scsiReq->maxTransfer,3,4,5); ! 154: ! 155: bzero(&cmdBuf, sizeof(commandBuf)); ! 156: cmdBuf.op = CO_Execute; ! 157: cmdBuf.scsiReq = scsiReq; ! 158: cmdBuf.buffer = buffer; ! 159: cmdBuf.client = client; ! 160: scsiReq->driverStatus = SR_IOST_INVALID; ! 161: ! 162: [self executeCmdBuf:&cmdBuf]; ! 163: ! 164: ddm_exp("executeRequest: cmdBuf 0x%x complete; driverStatus %s\n", ! 165: &cmdBuf, IOFindNameForValue(scsiReq->driverStatus, ! 166: IOScStatusStrings), 3,4,5); ! 167: return cmdBuf.scsiReq->driverStatus; ! 168: } ! 169: ! 170: ! 171: /* ! 172: * Reset the SCSI bus. All the work is done by the I/O thread. ! 173: */ ! 174: - (sc_status_t)resetSCSIBus ! 175: { ! 176: commandBuf cmdBuf; ! 177: ! 178: ddm_exp("resetSCSIBus: cmdBuf 0x%x\n", &cmdBuf, 2,3,4,5); ! 179: ! 180: cmdBuf.op = CO_Reset; ! 181: cmdBuf.scsiReq = NULL; ! 182: [self executeCmdBuf:&cmdBuf]; ! 183: ddm_exp("resetSCSIBus: cmdBuf 0x%x DONE\n", &cmdBuf, 2,3,4,5); ! 184: return SR_IOST_GOOD; // can not fail ! 185: } ! 186: ! 187: /* ! 188: * The following 6 methods are all called from the I/O thread in ! 189: * IODirectDevice. ! 190: */ ! 191: ! 192: /* ! 193: * Called from the I/O thread when it receives an interrupt message. ! 194: * Currently all work is done by chip-specific module; maybe we should ! 195: * put this method there.... ! 196: */ ! 197: - (void)interruptOccurred ! 198: { ! 199: #if DDM_DEBUG ! 200: /* ! 201: * calculate interrupt service time if enabled. ! 202: */ ! 203: ns_time_t startTime, endTime, elapsedNs; ! 204: unsigned elapsedUs = 0; ! 205: ! 206: if(IODDMMasks[AMD_DDM_INDEX] & DDM_INTR) { ! 207: IOGetTimestamp(&startTime); ! 208: } ! 209: ddm_thr("interruptOccurred: TOP\n", 1,2,3,4,5); ! 210: #endif DDM_DEBUG ! 211: ! 212: [self hwInterrupt]; ! 213: ! 214: #if DDM_DEBUG ! 215: if(IODDMMasks[AMD_DDM_INDEX] & DDM_INTR) { ! 216: IOGetTimestamp(&endTime); ! 217: elapsedNs = endTime - startTime; ! 218: elapsedUs = (unsigned)((elapsedNs + 999ULL) / 1000ULL); ! 219: } ! 220: ddm_intr("interruptOccurred: DONE; elapsed time %d us\n", ! 221: elapsedUs, 2,3,4,5); ! 222: #endif DDM_DEBUG ! 223: } ! 224: ! 225: /* ! 226: * These three should not occur; they are here as error traps. All three are ! 227: * called out from the I/O thread upon receipt of messages which it should ! 228: * not be seeing. ! 229: */ ! 230: - (void)interruptOccurredAt:(int)localNum ! 231: { ! 232: IOLog("%s: interruptOccurredAt:%d\n", [self name], localNum); ! 233: } ! 234: ! 235: - (void)otherOccurred:(int)id ! 236: { ! 237: IOLog("%s: otherOccurred:%d\n", [self name], id); ! 238: } ! 239: ! 240: - (void)receiveMsg ! 241: { ! 242: IOLog("%s: receiveMsg\n", [self name]); ! 243: ! 244: /* ! 245: * We have to let IODirectDevice take care of this (i.e., dequeue the ! 246: * bogus message). ! 247: */ ! 248: [super receiveMsg]; ! 249: } ! 250: ! 251: /* ! 252: * Used in -timeoutOccurred to determine if specified cmdBuf has timed out. ! 253: * Returns YES if timeout, else NO. ! 254: */ ! 255: static inline BOOL ! 256: isCmdTimedOut(commandBuf *cmdBuf, ns_time_t now) ! 257: { ! 258: IOSCSIRequest *scsiReq; ! 259: ns_time_t expire; ! 260: ! 261: scsiReq = cmdBuf->scsiReq; ! 262: expire = cmdBuf->startTime + ! 263: (1000000000ULL * (unsigned long long)scsiReq->timeoutLength); ! 264: return ((now > expire) ? YES : NO); ! 265: } ! 266: ! 267: /* ! 268: * Called from the I/O thread when it receives a timeout ! 269: * message. We send these messages ourself from AMDTimeout(). ! 270: */ ! 271: - (void)timeoutOccurred ! 272: { ! 273: ns_time_t now; ! 274: BOOL cmdTimedOut = NO; ! 275: commandBuf *cmdBuf; ! 276: commandBuf *nextCmdBuf; ! 277: ! 278: ddm_thr("timeoutOccurred: TOP\n", 1,2,3,4,5); ! 279: IOGetTimestamp(&now); ! 280: ! 281: /* ! 282: * Scan activeCmd and disconnectQ looking for tardy I/Os. ! 283: */ ! 284: if(activeCmd) { ! 285: cmdBuf = activeCmd; ! 286: if(isCmdTimedOut(cmdBuf, now)) { ! 287: ddm_thr("activeCmd TIMEOUT, cmd 0x%x\n", ! 288: cmdBuf, 2,3,4,5); ! 289: activeCmd = NULL; ! 290: ASSERT(cmdBuf->scsiReq != NULL); ! 291: cmdBuf->scsiReq->driverStatus = SR_IOST_IOTO; ! 292: [self ioComplete:cmdBuf]; ! 293: cmdTimedOut = YES; ! 294: } ! 295: } ! 296: ! 297: cmdBuf = (commandBuf *)queue_first(&disconnectQ); ! 298: while(!queue_end(&disconnectQ, (queue_entry_t)cmdBuf)) { ! 299: if(isCmdTimedOut(cmdBuf, now)) { ! 300: ddm_thr("disconnected cmd TIMEOUT, cmd 0x%x\n", ! 301: cmdBuf, 2,3,4,5); ! 302: ! 303: /* ! 304: * Remove cmdBuf from disconnectQ and ! 305: * complete it. ! 306: */ ! 307: nextCmdBuf = (commandBuf *)queue_next(&cmdBuf->link); ! 308: queue_remove(&disconnectQ, cmdBuf, commandBuf *, link); ! 309: ASSERT(cmdBuf->scsiReq != NULL); ! 310: cmdBuf->scsiReq->driverStatus = SR_IOST_IOTO; ! 311: [self ioComplete:cmdBuf]; ! 312: cmdBuf = nextCmdBuf; ! 313: cmdTimedOut = YES; ! 314: } ! 315: else { ! 316: cmdBuf = (commandBuf *)queue_next(&cmdBuf->link); ! 317: } ! 318: } ! 319: ! 320: /* ! 321: * Reset bus. This also completes all I/Os in disconnectQ with ! 322: * status CS_Reset. ! 323: */ ! 324: if(cmdTimedOut) { ! 325: [self logRegs]; ! 326: [self threadResetBus:NULL]; ! 327: } ! 328: ddm_thr("timeoutOccurred: DONE\n", 1,2,3,4,5); ! 329: } ! 330: ! 331: /* ! 332: * Process all commands in commandQ. At most one of these will become ! 333: * activeCmd. The remainder of CO_Execute commands go to pendingQ. Other ! 334: * types of commands are executed immediately. ! 335: */ ! 336: - (void)commandRequestOccurred ! 337: { ! 338: commandBuf *cmdBuf; ! 339: commandBuf *pendCmd; ! 340: ! 341: ddm_thr("commandRequestOccurred: top\n", 1,2,3,4,5); ! 342: [commandLock lock]; ! 343: while(!queue_empty(&commandQ)) { ! 344: cmdBuf = (commandBuf *) queue_first(&commandQ); ! 345: queue_remove(&commandQ, cmdBuf, commandBuf *, link); ! 346: [commandLock unlock]; ! 347: ! 348: switch(cmdBuf->op) { ! 349: case CO_Reset: ! 350: /* ! 351: * Note all active and disconnected commands will ! 352: * be terminted. ! 353: */ ! 354: [self threadResetBus:"Reset Command Received"]; ! 355: [self ioComplete:cmdBuf]; ! 356: break; ! 357: ! 358: case CO_Abort: ! 359: /* ! 360: * 1. Abort all active, pending, and disconnected ! 361: * commands. ! 362: * 2. Notify caller of completion. ! 363: * 3. Self-terminate. ! 364: */ ! 365: [self swAbort:SR_IOST_INT]; ! 366: pendCmd = (commandBuf *)queue_first(&pendingQ); ! 367: while(!queue_end(&pendingQ, ! 368: (queue_entry_t)pendCmd)) { ! 369: pendCmd->scsiReq->driverStatus = SR_IOST_INT; ! 370: [self ioComplete:pendCmd]; ! 371: pendCmd = (commandBuf *) ! 372: queue_next(&pendCmd->link); ! 373: } ! 374: [cmdBuf->cmdLock lock]; ! 375: [cmdBuf->cmdLock unlockWith:CMD_COMPLETE]; ! 376: IOExitThread(); ! 377: /* not reached */ ! 378: ! 379: case CO_Execute: ! 380: [self threadExecuteRequest:cmdBuf]; ! 381: break; ! 382: ! 383: } ! 384: [commandLock lock]; ! 385: } ! 386: [commandLock unlock]; ! 387: ddm_thr("commandRequestOccurred: DONE\n", 1,2,3,4,5); ! 388: return; ! 389: } ! 390: ! 391: /* ! 392: * Power management methods. All we care about is power off, when we must ! 393: * reset the SCSI bus due to the Compaq BIOS's lack of a SCSI reset, which ! 394: * causes a hang if we have set up targets for sync data transfer mode. ! 395: */ ! 396: - (IOReturn)getPowerState:(PMPowerState *)state_p ! 397: { ! 398: ddm_exp("getPowerState called\n", 1,2,3,4,5); ! 399: return IO_R_UNSUPPORTED; ! 400: } ! 401: ! 402: - (IOReturn)setPowerState:(PMPowerState)state ! 403: { ! 404: #ifdef DEBUG ! 405: IOLog("%s: received setPowerState: with %x\n", [self name], ! 406: (unsigned)state); ! 407: #endif DEBUG ! 408: if (state == PM_OFF) { ! 409: [self scsiReset]; ! 410: return IO_R_SUCCESS; ! 411: } ! 412: return IO_R_UNSUPPORTED; ! 413: } ! 414: ! 415: - (IOReturn)getPowerManagement:(PMPowerManagementState *)state_p ! 416: { ! 417: ddm_exp("getPowerManagement called\n", 1,2,3,4,5); ! 418: return IO_R_UNSUPPORTED; ! 419: } ! 420: ! 421: - (IOReturn)setPowerManagement:(PMPowerManagementState)state ! 422: { ! 423: ddm_exp("setPowerManagement called\n", 1,2,3,4,5); ! 424: return IO_R_UNSUPPORTED; ! 425: } ! 426: ! 427: #if AMD_ENABLE_GET_SET ! 428: ! 429: - (IOReturn)setIntValues:(unsigned *)parameterArray ! 430: forParameter:(IOParameterName)parameterName ! 431: count:(unsigned int)count ! 432: { ! 433: if(strcmp(parameterName, AMD_AUTOSENSE) == 0) { ! 434: if (count != 1) { ! 435: return IO_R_INVALID_ARG; ! 436: } ! 437: autoSenseEnable = (parameterArray[0] ? 1 : 0); ! 438: IOLog("%s: autoSense %s\n", [self name], ! 439: (autoSenseEnable ? "Enabled" : "Disabled")); ! 440: return IO_R_SUCCESS; ! 441: } ! 442: else if(strcmp(parameterName, AMD_CMD_QUEUE) == 0) { ! 443: if (count != 1) { ! 444: return IO_R_INVALID_ARG; ! 445: } ! 446: cmdQueueEnable = (parameterArray[0] ? 1 : 0); ! 447: IOLog("%s: cmdQueue %s\n", [self name], ! 448: (cmdQueueEnable ? "Enabled" : "Disabled")); ! 449: return IO_R_SUCCESS; ! 450: } ! 451: else if(strcmp(parameterName, AMD_SYNC) == 0) { ! 452: if (count != 1) { ! 453: return IO_R_INVALID_ARG; ! 454: } ! 455: syncModeEnable = (parameterArray[0] ? 1 : 0); ! 456: IOLog("%s: syncMode %s\n", [self name], ! 457: (syncModeEnable ? "Enabled" : "Disabled")); ! 458: return IO_R_SUCCESS; ! 459: } ! 460: else if(strcmp(parameterName, AMD_FAST_SCSI) == 0) { ! 461: if (count != 1) { ! 462: return IO_R_INVALID_ARG; ! 463: } ! 464: fastModeEnable = (parameterArray[0] ? 1 : 0); ! 465: IOLog("%s: fastMode %s\n", [self name], ! 466: (fastModeEnable ? "Enabled" : "Disabled")); ! 467: return IO_R_SUCCESS; ! 468: } ! 469: else if(strcmp(parameterName, AMD_RESET_TARGETS) == 0) { ! 470: int target; ! 471: perTargetData *perTargetPtr; ! 472: ! 473: if (count != 0) { ! 474: return IO_R_INVALID_ARG; ! 475: } ! 476: ! 477: /* ! 478: * Re-enable sync and command queueing. The ! 479: * disable bits persist after a reset. ! 480: */ ! 481: for(target=0; target<SCSI_NTARGETS; target++) { ! 482: perTargetPtr = &perTarget[target]; ! 483: perTargetPtr->cmdQueueDisable = 0; ! 484: perTargetPtr->syncDisable = 0; ! 485: perTargetPtr->maxQueue = 0; ! 486: } ! 487: IOLog("%s: Per Target disable flags cleared\n", [self name]); ! 488: return IO_R_SUCCESS; ! 489: } ! 490: else { ! 491: return [super setIntValues:parameterArray ! 492: forParameter:parameterName ! 493: count:count]; ! 494: } ! 495: } ! 496: ! 497: - (IOReturn)getIntValues : (unsigned *)parameterArray ! 498: forParameter : (IOParameterName)parameterName ! 499: count : (unsigned *)count; // in/out ! 500: { ! 501: if(strcmp(parameterName, AMD_AUTOSENSE) == 0) { ! 502: if(*count != 1) { ! 503: return IO_R_INVALID_ARG; ! 504: } ! 505: parameterArray[0] = autoSenseEnable; ! 506: return IO_R_SUCCESS; ! 507: } ! 508: else if(strcmp(parameterName, AMD_CMD_QUEUE) == 0) { ! 509: if(*count != 1) { ! 510: return IO_R_INVALID_ARG; ! 511: } ! 512: parameterArray[0] = cmdQueueEnable; ! 513: return IO_R_SUCCESS; ! 514: } ! 515: else if(strcmp(parameterName, AMD_SYNC) == 0) { ! 516: if(*count != 1) { ! 517: return IO_R_INVALID_ARG; ! 518: } ! 519: parameterArray[0] = syncModeEnable; ! 520: return IO_R_SUCCESS; ! 521: } ! 522: else if(strcmp(parameterName, AMD_FAST_SCSI) == 0) { ! 523: if(*count != 1) { ! 524: return IO_R_INVALID_ARG; ! 525: } ! 526: parameterArray[0] = fastModeEnable; ! 527: return IO_R_SUCCESS; ! 528: } ! 529: else { ! 530: return [super getIntValues : parameterArray ! 531: forParameter : parameterName ! 532: count : count]; ! 533: ! 534: } ! 535: } ! 536: ! 537: ! 538: #endif AMD_ENABLE_GET_SET ! 539: ! 540: @end /* AMD_SCSI */ ! 541: ! 542: @implementation AMD_SCSI(Private) ! 543: ! 544: /* ! 545: * Private chip- and architecture-independent methods. ! 546: */ ! 547: ! 548: /* ! 549: * Pass one commandBuf to the I/O thread; wait for completion. ! 550: * Normal completion status is in cmdBuf->scsiReq->driverStatus; ! 551: * a non-zero return from this function indicates a Mach IPC error. ! 552: * ! 553: * This method allocates and frees cmdBuf->cmdLock. ! 554: */ ! 555: - (IOReturn)executeCmdBuf : (commandBuf *)cmdBuf ! 556: { ! 557: msg_header_t msg = cmdMessageTemplate; ! 558: kern_return_t krtn; ! 559: IOReturn rtn = IO_R_SUCCESS; ! 560: ! 561: cmdBuf->cmdPendingSense = NULL; ! 562: cmdBuf->active = 0; ! 563: cmdBuf->cmdLock = [[NXConditionLock alloc] initWith:CMD_PENDING]; ! 564: [commandLock lock]; ! 565: queue_enter(&commandQ, cmdBuf, commandBuf *, link); ! 566: [commandLock unlock]; ! 567: ! 568: /* ! 569: * Create a Mach message and send it in order to wake up the ! 570: * I/O thread. ! 571: */ ! 572: msg.msg_remote_port = interruptPortKern; ! 573: krtn = msg_send_from_kernel(&msg, MSG_OPTION_NONE, 0); ! 574: if(krtn) { ! 575: IOLog("%s: msg_send_from_kernel() returned %d\n", ! 576: [self name], krtn); ! 577: rtn = IO_R_IPC_FAILURE; ! 578: goto out; ! 579: } ! 580: ! 581: /* ! 582: * Wait for I/O complete. ! 583: */ ! 584: ddm_exp("executeCmdBuf: waiting for completion on cmdBuf 0x%x\n", ! 585: cmdBuf, 2,3,4,5); ! 586: [cmdBuf->cmdLock lockWhen:CMD_COMPLETE]; ! 587: out: ! 588: [cmdBuf->cmdLock free]; ! 589: return rtn; ! 590: } ! 591: ! 592: /* ! 593: * Abort all active and disconnected commands with specified status. No ! 594: * hardware action. Currently used by threadResetBus and during processing ! 595: * of a CO_Abort command. ! 596: */ ! 597: - (void)swAbort : (sc_status_t)status ! 598: { ! 599: commandBuf *cmdBuf; ! 600: commandBuf *nextCmdBuf; ! 601: ! 602: ddm_thr("swAbort\n", 1,2,3,4,5); ! 603: if(activeCmd) { ! 604: activeCmd->scsiReq->driverStatus = status; ! 605: [self ioComplete:activeCmd]; ! 606: activeCmd = NULL; ! 607: } ! 608: cmdBuf = (commandBuf *)queue_first(&disconnectQ); ! 609: while(!queue_end(&disconnectQ, (queue_entry_t)cmdBuf)) { ! 610: queue_remove(&disconnectQ, cmdBuf, commandBuf *, link); ! 611: nextCmdBuf = (commandBuf *) ! 612: queue_next(&cmdBuf->link); ! 613: cmdBuf->scsiReq->driverStatus = status; ! 614: [self ioComplete:cmdBuf]; ! 615: cmdBuf = nextCmdBuf; ! 616: } ! 617: #ifdef DEBUG ! 618: /* ! 619: * activeArray "should be" empty...if not, make sure it is for debug. ! 620: */ ! 621: { ! 622: int target, lun; ! 623: int active; ! 624: ! 625: for(target=0; target<SCSI_NTARGETS; target++) { ! 626: for(lun=0; lun<SCSI_NLUNS; lun++) { ! 627: active = activeArray[target][lun]; ! 628: if(active) { ! 629: IOLog("swAbort: activeArray[%d][%d] = %d\n", ! 630: target, lun, active); ! 631: activeCount -= active; ! 632: activeArray[target][lun] = 0; ! 633: } ! 634: } ! 635: } ! 636: if(activeCount != 0) { ! 637: IOLog("swAbort: activeCount = %d\n", activeCount); ! 638: activeCount = 0; ! 639: } ! 640: } ! 641: #endif DEBUG ! 642: } ! 643: ! 644: /* ! 645: * Abort all active and disconnected commands with status SR_IOST_RESET. ! 646: * Reset hardware and SCSI bus. If there is a command in pendingQ, start ! 647: * it up. ! 648: */ ! 649: - (void)threadResetBus : (const char *)reason ! 650: { ! 651: [self swAbort:SR_IOST_RESET]; ! 652: [self hwReset : reason]; ! 653: [self busFree]; ! 654: } ! 655: ! 656: /* ! 657: * Commence processing of the specified command. ! 658: * ! 659: * If activeCmd is non-NULL or cmdBufOK says we can't process this command, ! 660: * we just enqueue the command on the end of pendingQ. ! 661: */ ! 662: - (void)threadExecuteRequest : (commandBuf *)cmdBuf ! 663: { ! 664: #if DDM_DEBUG ! 665: unsigned char target = cmdBuf->scsiReq->target; ! 666: unsigned char lun = cmdBuf->scsiReq->lun; ! 667: #endif DDM_DEBUG ! 668: ! 669: if(activeCmd != NULL) { ! 670: ddm_thr("threadExecuteRequest: ACTIVE; adding 0x%x to " ! 671: "pendingQ\n", cmdBuf, 2,3,4,5); ! 672: queue_enter(&pendingQ, cmdBuf, commandBuf *, link); ! 673: return; ! 674: } ! 675: else if([self cmdBufOK:cmdBuf] == NO) { ! 676: ddm_thr("threadExecuteRequest: !cmdBufOK; adding 0x%x to " ! 677: "pendingQ\n", cmdBuf, 2,3,4,5); ! 678: queue_enter(&pendingQ, cmdBuf, commandBuf *, link); ! 679: return; ! 680: } ! 681: ! 682: ddm_thr("calling hwStart: cmdBuf 0x%x activeArray[%d][%d] = %d\n", ! 683: cmdBuf, target, lun, activeArray[target][lun], 5); ! 684: ! 685: switch([self hwStart:cmdBuf]) { ! 686: case HWS_OK: // cool ! 687: case HWS_BUSY: // h/w can't take cmd now ! 688: break; ! 689: case HWS_REJECT: // hw ready for new cmd ! 690: ddm_thr("threadExecuteRequest: calling busFree\n", 1,2,3,4,5); ! 691: [self busFree]; ! 692: } ! 693: ! 694: ddm_thr("threadExecuteRequest(0x%x): DONE\n", cmdBuf, 2,3,4,5); ! 695: } ! 696: ! 697: /* ! 698: * Methods called by hardware-dependent modules. ! 699: */ ! 700: ! 701: #if TEST_QUEUE_FULL ! 702: int testQueueFull; ! 703: #endif TEST_QUEUE_FULL ! 704: ! 705: /* ! 706: * Called when a transaction associated with cmdBuf is complete. Notify ! 707: * waiting thread. If cmdBuf->scsiReq exists (i.e., this is not a reset ! 708: * or an abort), scsiReq->driverStatus must be valid. If cmdBuf is active, ! 709: * caller must remove from activeCmd. We decrement activeArray[][] counter ! 710: * if appropriate. ! 711: */ ! 712: - (void)ioComplete:(commandBuf *)cmdBuf ! 713: { ! 714: ns_time_t currentTime; ! 715: IOSCSIRequest *scsiReq = cmdBuf->scsiReq; ! 716: int target; ! 717: int lun; ! 718: ! 719: if(cmdBuf->cmdPendingSense != NULL) { ! 720: /* ! 721: * This was an autosense request. ! 722: */ ! 723: ! 724: commandBuf *origCmdBuf = cmdBuf->cmdPendingSense; ! 725: esense_reply_t *alignedSense = cmdBuf->buffer; ! 726: ! 727: ASSERT(cmdBuf->scsiReq != NULL); ! 728: ddm_thr("autosense buf 0x%x complete for cmd 0x%x sense " ! 729: "key 0x%x\n", ! 730: cmdBuf, origCmdBuf, alignedSense->er_sensekey, 4,5); ! 731: if(cmdBuf->scsiReq->driverStatus == SR_IOST_GOOD) { ! 732: /* ! 733: * Copy aligned sense data to caller's buffer. ! 734: */ ! 735: alignedSense = cmdBuf->buffer; ! 736: origCmdBuf->scsiReq->senseData = *alignedSense; ! 737: origCmdBuf->scsiReq->driverStatus = SR_IOST_CHKSV; ! 738: } ! 739: else { ! 740: IOLog("AMD53C974: Autosense request for target %d" ! 741: " FAILED (%s)\n", ! 742: cmdBuf->scsiReq->target, ! 743: IOFindNameForValue(scsiReq->driverStatus, ! 744: IOScStatusStrings)); ! 745: origCmdBuf->scsiReq->driverStatus = SR_IOST_CHKSNV; ! 746: } ! 747: ! 748: /* ! 749: * Free all of the allocated memory associated with ! 750: * this autosense request. ! 751: */ ! 752: [self deactivateCmd:cmdBuf]; ! 753: IOFree(cmdBuf->scsiReq, sizeof(*cmdBuf->scsiReq)); ! 754: IOFree(cmdBuf->unalignedSense, ! 755: sizeof(esense_reply_t) + (2 * AMD_READ_START_ALIGN)); ! 756: IOFree(cmdBuf, sizeof(commandBuf)); ! 757: ! 758: /* ! 759: * Now complete the I/O for the original commandBuf. ! 760: */ ! 761: [origCmdBuf->cmdLock lock]; ! 762: [origCmdBuf->cmdLock unlockWith:YES]; ! 763: return; ! 764: } ! 765: if(scsiReq != NULL) { ! 766: IOGetTimestamp(¤tTime); ! 767: scsiReq->totalTime = currentTime - cmdBuf->startTime; ! 768: scsiReq->bytesTransferred = ! 769: scsiReq->maxTransfer - cmdBuf->currentByteCount; ! 770: ! 771: /* ! 772: * Catch bad SCSI status now. ! 773: */ ! 774: if(scsiReq->driverStatus == SR_IOST_GOOD) { ! 775: #if TEST_QUEUE_FULL ! 776: if(testQueueFull && ! 777: (activeArray[scsiReq->target][scsiReq->lun] > 1)) { ! 778: scsiReq->scsiStatus = STAT_QUEUE_FULL; ! 779: testQueueFull = 0; ! 780: } ! 781: #endif TEST_QUEUE_FULL ! 782: switch(scsiReq->scsiStatus) { ! 783: case STAT_GOOD: ! 784: break; ! 785: case STAT_CHECK: ! 786: if(autoSenseEnable) { ! 787: /* ! 788: * Generate an autosense request, enqueue ! 789: * on pendingQ. ! 790: */ ! 791: [self generateAutoSense:cmdBuf]; ! 792: if(cmdBuf->active) { ! 793: [self deactivateCmd:cmdBuf]; ! 794: } ! 795: return; ! 796: } ! 797: else { ! 798: scsiReq->driverStatus = SR_IOST_CHKSNV; ! 799: } ! 800: break; ! 801: ! 802: case STAT_QUEUE_FULL: ! 803: /* ! 804: * Avoid notifying client of this condition; ! 805: * update perTarget.maxQueue and place this ! 806: * request on pendingQ. We'll try this ! 807: * again when we ioComplete at least one ! 808: * command in this target's queue. ! 809: */ ! 810: if(cmdBuf->queueTag == QUEUE_TAG_NONTAGGED) { ! 811: /* ! 812: * Huh? We're not doing command ! 813: * queueing... ! 814: */ ! 815: scsiReq->driverStatus = SR_IOST_BADST; ! 816: break; ! 817: } ! 818: target = scsiReq->target; ! 819: lun = scsiReq->lun; ! 820: if(cmdBuf->active) { ! 821: [self deactivateCmd:cmdBuf]; ! 822: } ! 823: perTarget[target].maxQueue = ! 824: activeArray[target][lun]; ! 825: ddm_thr("Target %d QUEUE FULL, maxQueue %d\n", ! 826: target, perTarget[target].maxQueue, ! 827: 3,4,5); ! 828: queue_enter(&pendingQ, cmdBuf, commandBuf *, ! 829: link); ! 830: return; ! 831: ! 832: default: ! 833: scsiReq->driverStatus = SR_IOST_BADST; ! 834: break; ! 835: } ! 836: } ! 837: } ! 838: if(cmdBuf->active) { ! 839: /* ! 840: * Note that the active flag is false for non-CO_Execute ! 841: * commands and commands aborted from pendingQ. ! 842: */ ! 843: [self deactivateCmd:cmdBuf]; ! 844: } ! 845: ! 846: #if DDM_DEBUG ! 847: { ! 848: const char *status; ! 849: unsigned moved; ! 850: ! 851: if(scsiReq != NULL) { ! 852: status = IOFindNameForValue(scsiReq->driverStatus, ! 853: IOScStatusStrings); ! 854: moved = scsiReq->bytesTransferred; ! 855: } ! 856: else { ! 857: status = "Complete"; ! 858: moved = 0; ! 859: } ! 860: ddm_thr("ioComplete: cmdBuf 0x%x status %s bytesXfr 0x%x\n", ! 861: cmdBuf, status, moved,4,5); ! 862: } ! 863: #endif DDM_DEBUG ! 864: ! 865: [cmdBuf->cmdLock lock]; ! 866: [cmdBuf->cmdLock unlockWith:YES]; ! 867: } ! 868: ! 869: /* ! 870: * Generate autosense request for specified cmdBuf, place it ! 871: * at head of pendingQ. ! 872: */ ! 873: - (void)generateAutoSense : (commandBuf *)cmdBuf ! 874: { ! 875: IOSCSIRequest *scsiReq = cmdBuf->scsiReq; ! 876: commandBuf *senseCmdBuf; ! 877: IOSCSIRequest *senseScsiReq; ! 878: cdb_6_t *cdbp; ! 879: ! 880: senseCmdBuf = IOMalloc(sizeof(commandBuf)); ! 881: senseScsiReq = IOMalloc(sizeof(IOSCSIRequest)); ! 882: bzero(senseCmdBuf, sizeof(commandBuf)); ! 883: bzero(senseScsiReq, sizeof(IOSCSIRequest)); ! 884: ! 885: /* ! 886: * commandBuf fields.... ! 887: */ ! 888: senseCmdBuf->cmdPendingSense = cmdBuf; ! 889: senseCmdBuf->op = CO_Execute; ! 890: senseCmdBuf->scsiReq = senseScsiReq; ! 891: ! 892: /* ! 893: * Get aligned sense buffer. ! 894: */ ! 895: senseCmdBuf->unalignedSense = IOMalloc(sizeof(esense_reply_t) + ! 896: (2 * AMD_READ_START_ALIGN)); ! 897: senseCmdBuf->buffer = IOAlign(void *, ! 898: senseCmdBuf->unalignedSense, ! 899: AMD_READ_START_ALIGN); ! 900: senseCmdBuf->client = IOVmTaskSelf(); ! 901: ! 902: /* ! 903: * Now IOSCSIRequest fields for request sense. ! 904: */ ! 905: senseScsiReq->target = scsiReq->target; ! 906: senseScsiReq->lun = scsiReq->lun; ! 907: senseScsiReq->read = YES; ! 908: senseScsiReq->maxTransfer = sizeof(esense_reply_t); ! 909: senseScsiReq->timeoutLength = 10; ! 910: senseScsiReq->disconnect = 0; ! 911: ! 912: cdbp = &senseScsiReq->cdb.cdb_c6; ! 913: cdbp->c6_opcode = C6OP_REQSENSE; ! 914: cdbp->c6_lun = scsiReq->lun; ! 915: cdbp->c6_len = sizeof(esense_reply_t); ! 916: senseScsiReq->driverStatus = SR_IOST_INVALID; ! 917: ! 918: /* ! 919: * This goes at the head of pendingQ; hopefully it'll be the ! 920: * next command out to the bus. ! 921: */ ! 922: ddm_thr("generateAutoSense: autosense buf 0x%x enqueued for " ! 923: "cmdBuf 0x%x\n", senseCmdBuf, cmdBuf, 3,4,5); ! 924: queue_enter_first(&pendingQ, senseCmdBuf, commandBuf *, link); ! 925: ! 926: } ! 927: ! 928: /* ! 929: * I/O associated with activeCmd has disconnected. Place it on disconnectQ ! 930: * and enable another transaction. ! 931: */ ! 932: - (void)disconnect ! 933: { ! 934: ddm_thr("DISCONNECT: cmdBuf 0x%x target %d lun %d tag %d\n", ! 935: activeCmd, activeCmd->scsiReq->target, ! 936: activeCmd->scsiReq->lun, activeCmd->queueTag, 5); ! 937: queue_enter(&disconnectQ, ! 938: activeCmd, ! 939: commandBuf *, ! 940: link); ! 941: #if DDM_DEBUG ! 942: if((activeCmd->currentByteCount != activeCmd->scsiReq->maxTransfer) && ! 943: (activeCmd->currentByteCount != 0)) { ! 944: ddm_thr("disconnect after partial DMA (max 0x%d curr 0x%x)\n", ! 945: activeCmd->scsiReq->maxTransfer, ! 946: activeCmd->currentByteCount, 3,4,5); ! 947: } ! 948: #endif DDM_DEBUG ! 949: /* ! 950: * Record this time so that activeCmd can be billed for ! 951: * disconnect latency at reselect time. ! 952: */ ! 953: IOGetTimestamp(&activeCmd->disconnectTime); ! 954: activeCmd = NULL; ! 955: /* [self busFree]; NO! fsm does this at end of hwInterrupt! */ ! 956: } ! 957: ! 958: /* ! 959: * Specified target, lun, and queueTag is trying to reselect. If we have ! 960: * a commandBuf for this TLQ nexus on disconnectQ, remove it, make it the ! 961: * current activeCmd, and return YES. Else return NO. ! 962: * A value of zero for queueTag indicates a nontagged command (zero is never ! 963: * used as the queue tag value for a tagged command). ! 964: */ ! 965: - (BOOL)reselect : (unsigned char)target_id ! 966: lun : (unsigned char)lun ! 967: queueTag : (unsigned char)queueTag ! 968: { ! 969: commandBuf *cmdBuf; ! 970: IOSCSIRequest *scsiReq; ! 971: ns_time_t currentTime; ! 972: ! 973: cmdBuf = (commandBuf *)queue_first(&disconnectQ); ! 974: while(!queue_end(&disconnectQ, (queue_t)cmdBuf)) { ! 975: ! 976: scsiReq = cmdBuf->scsiReq; ! 977: if((scsiReq->target == target_id) && ! 978: (scsiReq->lun == lun) && ! 979: (cmdBuf->queueTag == queueTag)) { ! 980: ddm_thr("RESELECT: target %d lun %d tag %d FOUND;" ! 981: "cmdBuf 0x%x\n", ! 982: target_id, lun, queueTag, cmdBuf, 5); ! 983: queue_remove(&disconnectQ, ! 984: cmdBuf, ! 985: commandBuf *, ! 986: link); ! 987: activeCmd = cmdBuf; ! 988: ! 989: /* ! 990: * Bill this operation for latency time. ! 991: */ ! 992: IOGetTimestamp(¤tTime); ! 993: scsiReq->latentTime += ! 994: (currentTime - activeCmd->disconnectTime); ! 995: return(YES); ! 996: } ! 997: /* ! 998: * Try next element in queue. ! 999: */ ! 1000: cmdBuf = (commandBuf *)cmdBuf->link.next; ! 1001: } ! 1002: ! 1003: /* ! 1004: * Hmm...this is not good! We don't want to talk to this target. ! 1005: */ ! 1006: IOLog("%s: ILLEGAL RESELECT target %d lun %d tag %d\n", ! 1007: [self name], target_id, lun, queueTag); ! 1008: return(NO); ! 1009: } ! 1010: ! 1011: /* ! 1012: * Determine if activeArray[][], maxQueue, cmdQueueEnable, and a ! 1013: * command's target and lun show that it's OK to start processing cmdBuf. ! 1014: * Returns YES if copacetic. ! 1015: */ ! 1016: - (BOOL)cmdBufOK : (commandBuf *)cmdBuf ! 1017: { ! 1018: IOSCSIRequest *scsiReq = cmdBuf->scsiReq; ! 1019: unsigned target = scsiReq->target; ! 1020: unsigned lun = scsiReq->lun; ! 1021: unsigned char active; ! 1022: unsigned char maxQ; ! 1023: ! 1024: active = activeArray[target][lun]; ! 1025: if(active == 0) { ! 1026: /* ! 1027: * Trivial quiescent case, always OK. ! 1028: */ ! 1029: return YES; ! 1030: } ! 1031: if((cmdQueueEnable == 0) || ! 1032: (perTarget[target].cmdQueueDisable)) { ! 1033: /* ! 1034: * No command queueing (either globally or for this target), ! 1035: * only one at a time. ! 1036: */ ! 1037: return NO; ! 1038: } ! 1039: maxQ = perTarget[target].maxQueue; ! 1040: if(maxQ == 0) { ! 1041: /* ! 1042: * We don't know what the target's limit is; go for it. ! 1043: */ ! 1044: return YES; ! 1045: } ! 1046: if(active >= maxQ) { ! 1047: /* ! 1048: * T/L's queue full; hold off. ! 1049: */ ! 1050: return NO; ! 1051: } ! 1052: else { ! 1053: return YES; ! 1054: } ! 1055: } ! 1056: ! 1057: /* ! 1058: * The bus has gone free. Start up a command from pendingQ, if any, and ! 1059: * if allowed by cmdQueueEnable and activeArray[][]. ! 1060: */ ! 1061: - (void)busFree ! 1062: { ! 1063: commandBuf *cmdBuf; ! 1064: ! 1065: ASSERT(activeCmd == NULL); ! 1066: if(queue_empty(&pendingQ)) { ! 1067: ddm_thr("busFree: pendingQ empty\n", 1,2,3,4,5); ! 1068: return; ! 1069: } ! 1070: ! 1071: /* ! 1072: * Attempt to find a commandBuf in pendingQ which we are in a position ! 1073: * to process. ! 1074: */ ! 1075: cmdBuf = (commandBuf *)queue_first(&pendingQ); ! 1076: while(!queue_end(&pendingQ, (queue_entry_t)cmdBuf)) { ! 1077: if([self cmdBufOK:cmdBuf]) { ! 1078: queue_remove(&pendingQ, cmdBuf, commandBuf *, link); ! 1079: ddm_thr("busFree: starting pending cmd 0x%x\n", cmdBuf, ! 1080: 2,3,4,5); ! 1081: [self threadExecuteRequest:cmdBuf]; ! 1082: return; ! 1083: } ! 1084: else { ! 1085: cmdBuf = (commandBuf *)queue_next(&cmdBuf->link); ! 1086: } ! 1087: } ! 1088: ddm_thr("busFree: pendingQ non-empty, no commands available\n", ! 1089: 1,2,3,4,5); ! 1090: } ! 1091: ! 1092: /* ! 1093: * Abort activeCmd (if any) and any disconnected I/Os (if any) and reset ! 1094: * the bus due to gross hardware failure. ! 1095: * If activeCmd is valid, its scsiReq->driverStatus will be set to 'status'. ! 1096: */ ! 1097: - (void)hwAbort : (sc_status_t)status ! 1098: reason : (const char *)reason ! 1099: { ! 1100: if(activeCmd) { ! 1101: activeCmd->scsiReq->driverStatus = status; ! 1102: [self ioComplete:activeCmd]; ! 1103: activeCmd = NULL; ! 1104: } ! 1105: [self logRegs]; ! 1106: [self threadResetBus:reason]; ! 1107: } ! 1108: ! 1109: /* ! 1110: * Called by chip level to indicate that a command has gone out to the ! 1111: * hardware. ! 1112: */ ! 1113: - (void)activateCommand : (commandBuf *)cmdBuf ! 1114: { ! 1115: unsigned char target; ! 1116: unsigned char lun; ! 1117: ! 1118: /* ! 1119: * Start timeout timer for this I/O. The timer request is cancelled ! 1120: * in ioComplete. ! 1121: */ ! 1122: cmdBuf->timeoutPort = interruptPortKern; ! 1123: #if LONG_TIMEOUT ! 1124: cmdBuf->scsiReq->timeoutLength = OUR_TIMEOUT; ! 1125: #endif LONG_TIMEOUT ! 1126: IOScheduleFunc(AMDTimeout, cmdBuf, cmdBuf->scsiReq->timeoutLength); ! 1127: ! 1128: /* ! 1129: * This is the only place where an activeArray[][] counter is ! 1130: * incremented (and, hence, the only place where cmdBuf->active is ! 1131: * set). The only other place activeCmd is set to non-NULL ! 1132: * is in reselect:lun:queueTag. ! 1133: */ ! 1134: activeCmd = cmdBuf; ! 1135: target = cmdBuf->scsiReq->target; ! 1136: lun = cmdBuf->scsiReq->lun; ! 1137: activeArray[target][lun]++; ! 1138: activeCount++; ! 1139: cmdBuf->active = 1; ! 1140: ! 1141: /* ! 1142: * Accumulate statistics. ! 1143: */ ! 1144: maxQueueLen = MAX(maxQueueLen, activeCount); ! 1145: queueLenTotal += activeCount; ! 1146: totalCommands++; ! 1147: ddm_thr("activateCommand: cmdBuf 0x%x target %d lun %d\n", ! 1148: cmdBuf, target, lun, 4,5); ! 1149: } ! 1150: ! 1151: /* ! 1152: * Remove specified cmdBuf from "active" status. Update activeArray, ! 1153: * activeCount, and unschedule pending timer. ! 1154: */ ! 1155: - (void)deactivateCmd : (commandBuf *)cmdBuf ! 1156: { ! 1157: IOSCSIRequest *scsiReq = cmdBuf->scsiReq; ! 1158: int target, lun; ! 1159: ! 1160: ASSERT(scsiReq != NULL); ! 1161: target = scsiReq->target; ! 1162: lun = scsiReq->lun; ! 1163: ddm_thr("deactivate cmdBuf 0x%x target %d lun %d activeArray %d\n", ! 1164: cmdBuf, target, lun, activeArray[target][lun], 5); ! 1165: ASSERT(activeArray[target][lun] != 0); ! 1166: activeArray[target][lun]--; ! 1167: ASSERT(activeCount != 0); ! 1168: activeCount--; ! 1169: ! 1170: /* ! 1171: * Cancel pending timeout request. Commands which timed out don't ! 1172: * have a timer request pending anymore. ! 1173: */ ! 1174: if(scsiReq->driverStatus != SR_IOST_IOTO) { ! 1175: IOUnscheduleFunc(AMDTimeout, cmdBuf); ! 1176: } ! 1177: cmdBuf->active = 0; ! 1178: } ! 1179: ! 1180: @end /* AMD_SCSI(Private) */ ! 1181: ! 1182: /* ! 1183: * Handle timeouts. We just send a timeout message to the I/O thread ! 1184: * so it wakes up. ! 1185: */ ! 1186: static void AMDTimeout(void *arg) ! 1187: { ! 1188: commandBuf *cmdBuf = arg; ! 1189: msg_header_t msg = timeoutMsgTemplate; ! 1190: ! 1191: ddm_err("AMDTimeout: cmdBuf 0x%x target %d\n", cmdBuf, ! 1192: cmdBuf->scsiReq->target, 3,4,5); ! 1193: if(!cmdBuf->active) { ! 1194: /* ! 1195: * Should never happen... ! 1196: */ ! 1197: IOLog("AMD53C974: Timeout on non-active cmdBuf\n"); ! 1198: return; ! 1199: } ! 1200: msg.msg_remote_port = cmdBuf->timeoutPort; ! 1201: IOLog("AMD53C974: SCSI Timeout\n"); ! 1202: (void) msg_send_from_kernel(&msg, MSG_OPTION_NONE, 0); ! 1203: } ! 1204:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.