|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. ! 3: * ! 4: * @APPLE_LICENSE_HEADER_START@ ! 5: * ! 6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights ! 7: * Reserved. This file contains Original Code and/or Modifications of ! 8: * Original Code as defined in and that are subject to the Apple Public ! 9: * Source License Version 1.1 (the "License"). You may not use this file ! 10: * except in compliance with the License. Please obtain a copy of the ! 11: * License at http://www.apple.com/publicsource and read it before using ! 12: * this file. ! 13: * ! 14: * The Original Code and all software distributed under the License are ! 15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER ! 16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ! 17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ! 18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the ! 19: * License for the specific language governing rights and limitations ! 20: * under the License. ! 21: * ! 22: * @APPLE_LICENSE_HEADER_END@ ! 23: */ ! 24: ! 25: /* Sym8xxClient.m created by russb2 on Sat 30-May-1998 */ ! 26: ! 27: #import "Sym8xxController.h" ! 28: ! 29: static u_int8_t xferMsgSync[] = {0x01, 0x03, 0x01, 0x0c, 0x10}; ! 30: static u_int8_t xferMsgAsync[] = {0x01, 0x03, 0x01, 0x00, 0x00}; ! 31: static u_int8_t xferMsgWide16[] = {0x01, 0x02, 0x03, 0x01}; ! 32: static u_int8_t cdbLength[8] = { 6, 10, 10, 0, 0, 12, 0, 0 }; ! 33: ! 34: @implementation Sym8xxController(Client) ! 35: ! 36: /*-----------------------------------------------------------------------------* ! 37: * Client thread routines. ! 38: * ! 39: * This module processes I/O requests from driverKit. It does most of the resource ! 40: * allocation and command preparation. Once a command is prepared it is queued ! 41: * to the driver's I/O Thread for execution. ! 42: * ! 43: *-----------------------------------------------------------------------------*/ ! 44: - (sc_status_t) executeRequest:(IOSCSIRequest *)scsiReq buffer:(void *)buffer client:(vm_task_t)client ! 45: { ! 46: SRB *srb; ! 47: Nexus *nexus, *nexusPhys; ! 48: u_int32_t len; ! 49: ns_time_t startTime, endTime; ! 50: ! 51: IOGetTimestamp( &startTime ); ! 52: ! 53: /* ! 54: * If a SCSI Bus reset is detected, we hold-off command processing until the targets have ! 55: * had a chance to recover. ! 56: */ ! 57: while ( resetQuiesceTimer ) ! 58: { ! 59: [resetQuiesceSem lock]; ! 60: } ! 61: [resetQuiesceSem unlock]; ! 62: ! 63: /* ! 64: * Allocate and initialize a SRB structure. ! 65: * Note: This routine clears the SRB and initializes srb->srbPhys ! 66: * which contains the physical address of the srb. ! 67: */ ! 68: srb = [self Sym8xxAllocSRB]; ! 69: if ( srb == NULL ) ! 70: { ! 71: return -1; ! 72: } ! 73: ! 74: nexus = &srb->nexus; ! 75: nexusPhys = &srb->srbPhys->nexus; ! 76: ! 77: /* ! 78: * Set client data buffer pointers in the SRB ! 79: */ ! 80: srb->xferClient = client; ! 81: srb->xferBuffer = (vm_offset_t) buffer; ! 82: srb->xferCount = scsiReq->maxTransfer; ! 83: ! 84: /* ! 85: * Set request sense buffer pointers in the SRB ! 86: */ ! 87: if ( !scsiReq->ignoreChkcond ) ! 88: { ! 89: srb->senseData = (vm_offset_t) &scsiReq->senseData; ! 90: srb->senseDataLength = sizeof(esense_reply_t); ! 91: } ! 92: ! 93: srb->srbCmd = ksrbCmdExecuteReq; ! 94: srb->srbState = ksrbStateCDBDone; ! 95: ! 96: srb->target = scsiReq->target; ! 97: srb->lun = scsiReq->lun; ! 98: ! 99: /* ! 100: * Setup timeout. (250ms ticks) ! 101: */ ! 102: if ( scsiReq->timeoutLength ) ! 103: { ! 104: srb->srbTimeout = (scsiReq->timeoutLength * 1000) / kSCSITimerIntervalMS + 1; ! 105: } ! 106: ! 107: srb->directionMask = (scsiReq->read) ? 0x01000000 : 0x00000000; ! 108: ! 109: /* ! 110: * Setup the Nexus struct. This part of the SRB is read/written both by the ! 111: * script and the driver. ! 112: */ ! 113: nexus->targetParms.target = srb->target; ! 114: ! 115: nexus->cdb.ppData = EndianSwap32((u_int32_t)&nexusPhys->cdbData); ! 116: ! 117: len = cdbLength[scsiReq->cdb.cdb_opcode >> 5]; ! 118: if ( len == 0 ) len = scsiReq->cdbLength; ! 119: ! 120: nexus->cdb.length = EndianSwap32( len ); ! 121: nexus->cdbData = scsiReq->cdb; ! 122: ! 123: /* ! 124: * Setup SCSI Messages to send on inital selection of the target. ! 125: * Note: A SCSI tag for command-queuing requests is allocated ! 126: * when messages are generated. ! 127: */ ! 128: srb->srbRequestFlags |= (scsiReq->disconnect) ? ksrbRFDisconnectAllowed : 0; ! 129: srb->srbRequestFlags |= (!scsiReq->syncDisable) ? ksrbRFXferSyncAllowed : 0; ! 130: srb->srbRequestFlags |= (!scsiReq->cmdQueueDisable) ? ksrbRFCmdQueueAllowed : 0; ! 131: ! 132: [self Sym8xxCalcMsgs:srb]; ! 133: ! 134: /* ! 135: * Setup initial data transfer list (SGList) ! 136: */ ! 137: nexus->ppSGList = (SGEntry *)EndianSwap32((u_int32_t)&nexusPhys->sgListData[2]); ! 138: [self Sym8xxUpdateSGList: srb ]; ! 139: ! 140: /* ! 141: * Queue command to I/O Thread and wait for completion. ! 142: */ ! 143: [self Sym8xxSendCommand: srb]; ! 144: ! 145: /* ! 146: * If the command timed-out then issue a Maibox abort to clear ! 147: * the request from the target. ! 148: * ! 149: * Note: We lock the abortBdrSem to insure there is only one abort ! 150: * active at a time. ! 151: */ ! 152: if ( srb->srbCmd == ksrbCmdProcessTimeout ) ! 153: { ! 154: [abortBdrSem lock]; ! 155: srb->srbCmd = ksrbCmdAbortReq; ! 156: [self Sym8xxSendCommand: srb]; ! 157: [abortBdrSem unlock]; ! 158: } ! 159: ! 160: /* ! 161: * Release the tag for the request. ! 162: */ ! 163: [self Sym8xxFreeTag: srb]; ! 164: ! 165: /* ! 166: * Transfer final request status from the SRB to the original request ! 167: */ ! 168: IOGetTimestamp( &endTime ); ! 169: scsiReq->totalTime = endTime - startTime; ! 170: scsiReq->driverStatus = srb->srbSCSIResult; ! 171: scsiReq->scsiStatus = srb->srbSCSIStatus; ! 172: scsiReq->bytesTransferred = srb->xferDone; ! 173: ! 174: [self Sym8xxFreeSRB: srb]; ! 175: ! 176: return scsiReq->driverStatus; ! 177: } ! 178: ! 179: /*-----------------------------------------------------------------------------* ! 180: * Requests from Blue Box. ! 181: * ! 182: * Note: Hopefully this kludge of having multiple variants of executeRequest ! 183: * will go away soon! ! 184: *-----------------------------------------------------------------------------*/ ! 185: - (sc_status_t) executeRequest : (IOSCSIRequest *) scsiReq ! 186: ioMemoryDescriptor : (IOMemoryDescriptor *) ioMemoryDescriptor ! 187: { ! 188: return [self executeRequest:scsiReq buffer:(void *)ioMemoryDescriptor client:(vm_task_t) -1]; ! 189: } ! 190: ! 191: ! 192: /*-----------------------------------------------------------------------------* ! 193: * This routine queues an SRB to reset the SCSI Bus ! 194: * ! 195: *-----------------------------------------------------------------------------*/ ! 196: - (sc_status_t) resetSCSIBus ! 197: { ! 198: SRB *srb; ! 199: sc_status_t scsiResult; ! 200: ! 201: srb = [self Sym8xxAllocSRB]; ! 202: if ( srb == NULL ) ! 203: { ! 204: return -1; ! 205: } ! 206: ! 207: srb->srbCmd = ksrbCmdResetSCSIBus; ! 208: [self Sym8xxSendCommand: srb]; ! 209: ! 210: scsiResult = srb->srbSCSIResult; ! 211: [self Sym8xxFreeSRB: srb]; ! 212: ! 213: return scsiResult; ! 214: } ! 215: ! 216: ! 217: /*-----------------------------------------------------------------------------* ! 218: * This routine queues a command on the driver's I/O Thread, wakes up ! 219: * the I/O Thread and then waits for the command to complete. ! 220: * ! 221: *-----------------------------------------------------------------------------*/ ! 222: - (void) Sym8xxSendCommand: (SRB *) srb ! 223: { ! 224: kern_return_t kr; ! 225: ! 226: msg_header_t msg = ! 227: { ! 228: 0, // msg_unused ! 229: 1, // msg_simple ! 230: sizeof(msg_header_t), // msg_size ! 231: MSG_TYPE_NORMAL, // msg_type ! 232: PORT_NULL, // msg_local_port ! 233: PORT_NULL, // msg_remote_port - TO BE FILLED IN ! 234: IO_COMMAND_MSG // msg_id ! 235: }; ! 236: ! 237: srb->srbCmdLock = [[NXConditionLock alloc] initWith: ksrbCmdPending]; ! 238: ! 239: [srbPendingQLock lock]; ! 240: queue_enter( &srbPendingQ, srb, SRB *, srbQ ); ! 241: [srbPendingQLock unlock]; ! 242: ! 243: msg.msg_remote_port = interruptPortKern; ! 244: kr = msg_send_from_kernel(&msg, MSG_OPTION_NONE, 0); ! 245: if( kr != KERN_SUCCESS ) ! 246: { ! 247: goto executeCmd_error; ! 248: } ! 249: ! 250: [srb->srbCmdLock lockWhen: ksrbCmdComplete]; ! 251: [srb->srbCmdLock free]; ! 252: ! 253: executeCmd_error: ! 254: ; ! 255: return; ! 256: } ! 257: ! 258: ! 259: /*-----------------------------------------------------------------------------* ! 260: * This routine provides our data alignment/length restrictions to the ! 261: * super class. ! 262: * ! 263: *-----------------------------------------------------------------------------*/ ! 264: - (void)getDMAAlignment:(IODMAAlignment *)alignment ! 265: { ! 266: alignment->readStart = 1; ! 267: alignment->writeStart = 1; ! 268: alignment->readLength = 1; ! 269: alignment->writeLength = 2; ! 270: } ! 271: ! 272: /*-----------------------------------------------------------------------------* ! 273: * This routine returns the number of targets we support. ! 274: * ! 275: *-----------------------------------------------------------------------------*/ ! 276: - (int) numberOfTargets ! 277: { ! 278: return MAX_SCSI_TARGETS; ! 279: } ! 280: ! 281: /*-----------------------------------------------------------------------------* ! 282: * This routine creates SCSI messages to send during the initial connection ! 283: * to the target. It is called during client request processing and also by ! 284: * the I/O thread when a request sense operation is required. ! 285: * ! 286: * Outbound messages are setup in the MsgOut buffer in the Nexus structure of ! 287: * the SRB. ! 288: * ! 289: *-----------------------------------------------------------------------------*/ ! 290: - (void) Sym8xxCalcMsgs: (SRB *)srb ! 291: { ! 292: Nexus *nexus; ! 293: Nexus *nexusPhys; ! 294: u_int32_t msgIndex; ! 295: BOOL fCmdQueue; ! 296: BOOL fNegotiateSync; ! 297: BOOL fNegotiateWide; ! 298: u_int32_t targetFlags; ! 299: u_int32_t reqFlags; ! 300: u_int8_t *xferMsg = NULL; ! 301: ! 302: nexus = &srb->nexus; ! 303: nexusPhys = &srb->srbPhys->nexus; ! 304: ! 305: reqFlags = srb->srbRequestFlags; ! 306: ! 307: /* ! 308: * Setup Identify message ! 309: */ ! 310: msgIndex = 0; ! 311: nexus->msg.ppData = EndianSwap32((u_int32_t)&nexusPhys->msgData); ! 312: nexus->msgData[msgIndex++] = srb->lun | (( reqFlags & ksrbRFDisconnectAllowed ) ? 0xC0 : 0x80); ! 313: ! 314: targetFlags = targets[srb->target].flags; ! 315: ! 316: /* ! 317: * Setup Tag message if cmdQueueing is supported. ! 318: * ! 319: * Note: On target flags: ! 320: * kTFxxxxSupported - Inquiry data indicates the function is supported. ! 321: * kTFxxxxAllowed - The function is not explicity disabled for this target. ! 322: * kRFxxxxAllowed - The function is not explicitly disabled by the command ! 323: */ ! 324: fCmdQueue = ( (targetFlags & kTFCmdQueueSupported) ! 325: && (targetFlags & kTFCmdQueueAllowed) ! 326: && (reqFlags & ksrbRFCmdQueueAllowed) ); ! 327: ! 328: /* ! 329: * Allocate tag for request. ! 330: * ! 331: * For non-tagged requests a pseudo-tag is created consisting of target*16+lun. For tagged ! 332: * requests a tag in the range 128-255 is allocated. ! 333: * ! 334: * If a pseudo-tag is inuse for a non-tagged command or there are no tags available for ! 335: * a tagged request, then the command is blocked until a tag becomes available. ! 336: * ! 337: * Note: If we are being called during request sense processing (srbState != ksrbStateCDBDone) ! 338: * then a tag has already been allocated to the request. ! 339: */ ! 340: if ( srb->srbState == ksrbStateCDBDone ) ! 341: { ! 342: srb->tag = srb->nexus.tag = [self Sym8xxAllocTag:(SRB *)srb CmdQueue:(BOOL)fCmdQueue]; ! 343: } ! 344: ! 345: if ( fCmdQueue ) ! 346: { ! 347: nexus->msgData[msgIndex++] = 0x20; ! 348: nexus->msgData[msgIndex++] = srb->nexus.tag; ! 349: } ! 350: ! 351: /* ! 352: * Setup to negotiate for Wide (16-bit) data transfers ! 353: * ! 354: * Note: There is no provision to negotiate back to narrow transfers although ! 355: * SCSI does support this. ! 356: */ ! 357: fNegotiateWide = (targetFlags & kTFXferWide16Supported) ! 358: && (targetFlags & kTFXferWide16Allowed) ! 359: && !(targetFlags & kTFXferWide16); ! 360: ! 361: if ( fNegotiateWide ) ! 362: { ! 363: srb->srbRequestFlags |= ksrbRFNegotiateWide; ! 364: bcopy( xferMsgWide16, &nexus->msgData[msgIndex], sizeof(xferMsgWide16) ); ! 365: msgIndex += sizeof(xferMsgWide16); ! 366: } ! 367: ! 368: /* ! 369: * Setup to negotiate for Synchronous data transfers. ! 370: * ! 371: * Note: We can negotiate back to async based on the flags in the command. ! 372: */ ! 373: ! 374: fNegotiateSync = (targetFlags & kTFXferSyncSupported) ! 375: && (targetFlags & kTFXferSyncAllowed) ! 376: && ( ((reqFlags & ksrbRFXferSyncAllowed) != 0) ^ ((targetFlags & kTFXferSync) != 0) ) ; ! 377: ! 378: if ( fNegotiateSync ) ! 379: { ! 380: srb->srbRequestFlags |= ksrbRFNegotiateSync; ! 381: xferMsg = (reqFlags & ksrbRFXferSyncAllowed) ? xferMsgSync : xferMsgAsync; ! 382: bcopy( xferMsg, &nexus->msgData[msgIndex], sizeof(xferMsgSync) ); ! 383: msgIndex += sizeof(xferMsgSync); ! 384: } ! 385: ! 386: /* ! 387: * If we are negotiating for both Sync and Wide data transfers, we setup both messages ! 388: * in the Nexus msgOut buffer. However, after each message the script needs to wait for ! 389: * a reply message from the target. In this case, we set the msgOut length to include ! 390: * bytes upto the end of the Wide message. When we get the reply from the target, the ! 391: * routine handling the WDTR will setup the Nexus pointers/counts to send the remaining ! 392: * message bytes. See Sym8xxExecute.m(Sym8xxNegotiateWDTR). ! 393: */ ! 394: srb->srbMsgLength = msgIndex; ! 395: ! 396: if ( fNegotiateSync && fNegotiateWide ) msgIndex -= sizeof(xferMsgSync); ! 397: ! 398: nexus->msg.length = EndianSwap32( msgIndex ); ! 399: } ! 400: ! 401: /*-----------------------------------------------------------------------------* ! 402: * This routine sets up the data transfer SG list for the client's buffer in the ! 403: * Nexus structure. ! 404: * ! 405: * The SGList actually consists of script instructions. The script will branch ! 406: * to the SGList when the target enters data transfer phase. When the SGList completes ! 407: * it will either execute a script INT instruction if there are more segments of the ! 408: * user buffer that need to be transferred or will execute a script RETURN instruction ! 409: * to return to the script. ! 410: * ! 411: * The first two slots in the SGList are reserved for partial data transfers. See ! 412: * Sym8xxExecute.m(Sym8xxAdjustDataPtrs). ! 413: * ! 414: *-----------------------------------------------------------------------------*/ ! 415: - (BOOL) Sym8xxUpdateSGList: (SRB *) srb ! 416: { ! 417: BOOL rc; ! 418: ! 419: if ( srb->xferClient != (vm_task_t)-1 ) ! 420: { ! 421: rc = [self Sym8xxUpdateSGListVirt: srb]; ! 422: } ! 423: else ! 424: { ! 425: rc = [self Sym8xxUpdateSGListDesc: srb]; ! 426: } ! 427: return rc; ! 428: } ! 429: ! 430: /*-----------------------------------------------------------------------------* ! 431: * Build SG list based on a single virtual address range/length ! 432: * ! 433: *-----------------------------------------------------------------------------*/ ! 434: - (BOOL) Sym8xxUpdateSGListVirt: (SRB *) srb ! 435: { ! 436: u_int32_t offset; ! 437: u_int32_t physAddr; ! 438: u_int32_t bytesLeft; ! 439: u_int32_t bytesOnPage; ! 440: u_int32_t i; ! 441: u_int32_t len = 0; ! 442: IOReturn rc = IO_R_SUCCESS; ! 443: ! 444: offset = srb->xferOffset; ! 445: bytesLeft = srb->xferCount - srb->xferOffset; ! 446: i = 2; ! 447: ! 448: while ( (bytesLeft > 0) && (i < MAX_SGLIST_ENTRIES-1)) ! 449: { ! 450: ! 451: rc = IOPhysicalFromVirtual( (vm_task_t) srb->xferClient, ! 452: (vm_address_t) (srb->xferBuffer+offset), ! 453: (u_int32_t *) &physAddr ); ! 454: ! 455: if ( rc != IO_R_SUCCESS ) ! 456: { ! 457: break; ! 458: } ! 459: ! 460: /* ! 461: * Note: The script instruction(s) to transfer data to/from the scsi bus ! 462: * have the same format as a typical SGList with the transfer length ! 463: * as the first word and the physical transfer address as the second. ! 464: * The data transfer direction is specified by a bit or'd into the ! 465: * high byte of the SG entry's length field. ! 466: */ ! 467: srb->nexus.sgListData[i].physAddr = EndianSwap32( physAddr ); ! 468: ! 469: bytesOnPage = page_size - ((srb->xferBuffer + offset) & (page_size - 1)); ! 470: len = ( bytesLeft < bytesOnPage ) ? bytesLeft : bytesOnPage; ! 471: ! 472: srb->nexus.sgListData[i].length = EndianSwap32( len | srb->directionMask ); ! 473: ! 474: bytesLeft -= len; ! 475: offset += len; ! 476: i++; ! 477: } ! 478: ! 479: if ( !bytesLeft ) ! 480: { ! 481: srb->nexus.sgListData[i].length = EndianSwap32( 0x90080000 ); ! 482: srb->nexus.sgListData[i].physAddr = EndianSwap32( 0x00000000 ); ! 483: } ! 484: else ! 485: { ! 486: srb->nexus.sgListData[i].length = EndianSwap32( 0x98080000 ); ! 487: srb->nexus.sgListData[i].physAddr = EndianSwap32( A_sglist_complete ); ! 488: } ! 489: ! 490: srb->xferOffsetPrev = srb->xferOffset; ! 491: srb->xferOffset = offset; ! 492: ! 493: return ((rc != IO_R_SUCCESS) ? NO : YES) ; ! 494: } ! 495: ! 496: /*-----------------------------------------------------------------------------* ! 497: * Build SG list based on an IOMemoryDescriptor object. ! 498: * ! 499: *-----------------------------------------------------------------------------*/ ! 500: - (BOOL) Sym8xxUpdateSGListDesc: (SRB *) srb ! 501: { ! 502: ! 503: PhysicalRange range; ! 504: u_int32_t actRanges; ! 505: u_int32_t offset; ! 506: u_int32_t bytesLeft; ! 507: u_int32_t i; ! 508: IOReturn rc = YES; ! 509: ! 510: offset = srb->xferOffset; ! 511: bytesLeft = srb->xferCount - srb->xferOffset; ! 512: i = 2; ! 513: ! 514: [(id)srb->xferBuffer setPosition: offset]; ! 515: ! 516: while ( (bytesLeft > 0) && (i < MAX_SGLIST_ENTRIES-1)) ! 517: { ! 518: [(id)srb->xferBuffer getPhysicalRanges: 1 ! 519: maxByteCount: 0x00FFFFFF ! 520: newPosition: &offset ! 521: actualRanges: &actRanges ! 522: physicalRanges: &range]; ! 523: ! 524: if ( actRanges != 1 ) ! 525: { ! 526: rc = NO; ! 527: break; ! 528: } ! 529: ! 530: /* ! 531: * Note: The script instruction(s) to transfer data to/from the scsi bus ! 532: * have the same format as a typical SGList with the transfer length ! 533: * as the first word and the physical transfer address as the second. ! 534: * The data transfer direction is specified by a bit or'd into the ! 535: * high byte of the SG entry's length field. ! 536: */ ! 537: srb->nexus.sgListData[i].physAddr = EndianSwap32( (u_int32_t)range.address ); ! 538: srb->nexus.sgListData[i].length = EndianSwap32( range.length | srb->directionMask ); ! 539: ! 540: bytesLeft -= range.length; ! 541: i++; ! 542: } ! 543: ! 544: if ( !bytesLeft ) ! 545: { ! 546: srb->nexus.sgListData[i].length = EndianSwap32( 0x90080000 ); ! 547: srb->nexus.sgListData[i].physAddr = EndianSwap32( 0x00000000 ); ! 548: } ! 549: else ! 550: { ! 551: srb->nexus.sgListData[i].length = EndianSwap32( 0x98080000 ); ! 552: srb->nexus.sgListData[i].physAddr = EndianSwap32( A_sglist_complete ); ! 553: } ! 554: ! 555: srb->xferOffsetPrev = srb->xferOffset; ! 556: srb->xferOffset = offset; ! 557: ! 558: return rc; ! 559: } ! 560: ! 561: ! 562: /*-----------------------------------------------------------------------------* ! 563: * This routine allocates a SCSI Tag value for a request. For non-tagged requests ! 564: * a pseudo-tag is generated with the value target*16+lun. ! 565: * ! 566: * If all tags are in-use or a pseudo tag is in-use, the request is blocked until ! 567: * the tag becomes available. ! 568: * ! 569: *-----------------------------------------------------------------------------*/ ! 570: - (u_int32_t) Sym8xxAllocTag:(SRB *) srb CmdQueue:(BOOL)fCmdQueue ! 571: { ! 572: u_int32_t i; ! 573: u_int32_t tagIndex; ! 574: u_int32_t tagMask; ! 575: ! 576: while ( 1 ) ! 577: { ! 578: if ( fCmdQueue ) ! 579: { ! 580: for ( i = MIN_SCSI_TAG; i < MAX_SCSI_TAG; i ++ ) ! 581: { ! 582: tagIndex = i / 32; ! 583: tagMask = 1 << (i % 32); ! 584: if ( !(tags[tagIndex] & tagMask) ) ! 585: { ! 586: tags[tagIndex] |= tagMask; ! 587: return i; ! 588: } ! 589: } ! 590: /* ! 591: * This semaphore gets unlocked whenever a tag gets returned to the pool. Any ! 592: * requests waiting for a tag will wake-up and try to allocate a tag. If they ! 593: * fail they will return here and will be put back to sleep. ! 594: */ ! 595: [cmdQTagSem lock]; ! 596: } ! 597: else ! 598: { ! 599: i = ((u_int32_t)srb->target << 3) | srb->lun; ! 600: tagIndex = i / 32; ! 601: tagMask = 1 << (i % 32); ! 602: if ( !(tags[tagIndex] & tagMask) ) ! 603: { ! 604: tags[tagIndex] |= tagMask; ! 605: return i; ! 606: } ! 607: /* ! 608: * This per-target semaphore gets unlocked whenever a request completes on a target. Any ! 609: * requests pending for this target will wake-up and try to allocate this pseudo-tag. If they ! 610: * fail they will return here and will be put back to sleep. ! 611: */ ! 612: [targets[srb->target].targetTagSem lock]; ! 613: } ! 614: } ! 615: return -1; ! 616: } ! 617: ! 618: /*-----------------------------------------------------------------------------* ! 619: * This routine frees a previously allocates SCSI tag. It unlocks the appropriate ! 620: * semaphore based on the type of tag returned. ! 621: * ! 622: *-----------------------------------------------------------------------------*/ ! 623: - (void) Sym8xxFreeTag:(SRB *) srb ! 624: { ! 625: u_int32_t i; ! 626: ! 627: i = srb->tag; ! 628: tags[i/32] &= ~(1 << (i % 32)); ! 629: ! 630: if ( i < MIN_SCSI_TAG ) ! 631: { ! 632: [targets[srb->target].targetTagSem unlock]; ! 633: } ! 634: else ! 635: { ! 636: [cmdQTagSem unlock]; ! 637: } ! 638: } ! 639: ! 640: ! 641: /*-----------------------------------------------------------------------------* ! 642: * This routine maintains a list of pages which are divided up into SRB sized ! 643: * allocations. The list of pages is grown or shrunk as needed. ! 644: * ! 645: * The reason we dont use the driverKit IOMalloc function is that it does not ! 646: * guarantee that allocations will not cross page boundaries. The driver does ! 647: * require this since the script accesses memory based on physical rather than ! 648: * virtual addresses. ! 649: * ! 650: *-----------------------------------------------------------------------------*/ ! 651: - (SRB *) Sym8xxAllocSRB ! 652: { ! 653: SRBPool *pSRBPool; ! 654: SRB *pSRB = NULL; ! 655: ! 656: do ! 657: { ! 658: /* ! 659: * We hold the srbPoolLock when we are searching or changing the SRB pool ! 660: * data structures ! 661: */ ! 662: [srbPoolLock lock]; ! 663: ! 664: /* ! 665: * Search the list of pages currently in the SRB pool until we find a page ! 666: * with at least one free SRB to allocate. ! 667: */ ! 668: pSRBPool = (SRBPool *) queue_first( &srbPool ); ! 669: while (!queue_end( &srbPool, &pSRBPool->nextPage ) ) ! 670: { ! 671: if ( !queue_empty( &pSRBPool->freeSRBList ) ) ! 672: { ! 673: pSRBPool->srbInUseCount++; ! 674: queue_remove_first( &pSRBPool->freeSRBList, pSRB, SRB *, srbQ ); ! 675: break; ! 676: } ! 677: pSRBPool = (SRBPool *)queue_next( &pSRBPool->nextPage ); ! 678: } ! 679: ! 680: [srbPoolLock unlock]; ! 681: ! 682: if ( pSRB ) ! 683: { ! 684: bzero( pSRB, sizeof(SRB) ); ! 685: pSRB->srbPhys = (SRB *)(pSRBPool->pagePhysAddr + (uint)pSRB - (uint)pSRBPool); ! 686: pSRB->srbSeqNum = ++srbSeqNum; ! 687: break; ! 688: } ! 689: ! 690: /* ! 691: * If we can find no available SRBs, we unlock a thread to grow the SRB pool and ! 692: * block this request until the pool grow operation completes. When our thread runs ! 693: * again it will retry the SRB allocation. ! 694: */ ! 695: if ( srbPoolGrow == NO ) ! 696: { ! 697: srbPoolGrow = YES; ! 698: [srbPoolGrowLock unlockWith: kSRBGrowPoolRunning]; ! 699: } ! 700: ! 701: [srbPoolGrowLock lockWhen: kSRBGrowPoolIdle]; ! 702: [srbPoolGrowLock unlockWith: kSRBGrowPoolIdle]; ! 703: } ! 704: while ( 1 ); ! 705: ! 706: return pSRB; ! 707: } ! 708: ! 709: /*-----------------------------------------------------------------------------* ! 710: * This routine returns SRBs to the SRB pool. ! 711: * ! 712: * The page in the pool containing the SRB is located and the ! 713: * SRB is added to that page's SRB free list. ! 714: * ! 715: * The pool is then scanned for pages with no SRBs allocated. ! 716: * If more than two pages are found with zero SRBs allocate, the ! 717: * additional idle pages are returned to the kernel. ! 718: * ! 719: *-----------------------------------------------------------------------------*/ ! 720: - (void) Sym8xxFreeSRB: (SRB *) pSRB ! 721: { ! 722: SRB *srbMin, *srbMax; ! 723: SRBPool *pSRBPool, *pSRBPoolNext; ! 724: u_int32_t numSRBs; ! 725: kern_return_t kr; ! 726: u_int32_t idlePageCount = 0; ! 727: ! 728: [srbPoolLock lock]; ! 729: ! 730: numSRBs = (page_size - sizeof(SRBPool)) / sizeof(SRB); ! 731: ! 732: /* ! 733: * Scan the pool for a page containing the returned SRB ! 734: */ ! 735: pSRBPool = (SRBPool *) queue_first( &srbPool ); ! 736: while (!queue_end( &srbPool, &pSRBPool->nextPage ) ) ! 737: { ! 738: srbMin = (SRB *) (pSRBPool+1); ! 739: srbMax = &srbMin[numSRBs-1]; ! 740: ! 741: if ( pSRB >= srbMin && pSRB <= srbMax ) ! 742: { ! 743: pSRBPool->srbInUseCount--; ! 744: queue_enter( &pSRBPool->freeSRBList, pSRB, SRB *, srbQ ); ! 745: break; ! 746: } ! 747: pSRBPool = (SRBPool *)queue_next( &pSRBPool->nextPage ); ! 748: } ! 749: ! 750: /* ! 751: * If we fell off the end of the SRB Pool page list without finding ! 752: * the owning page, we have a bug. ! 753: */ ! 754: if ( queue_end( &srbPool, &pSRBPool->nextPage ) ) ! 755: { ! 756: kprintf("Sym8xxFreeSRB: Bad SRB returned = %08x\n\r", (u_int32_t)pSRB ); ! 757: } ! 758: ! 759: /* ! 760: * We scan the SRBPool page list again looking for pages with no SRBs inuse. ! 761: * If more than idle pool pages are found, we release the remaining pages to ! 762: * the kernel. ! 763: */ ! 764: pSRBPool = (SRBPool *) queue_first( &srbPool ); ! 765: while (!queue_end( &srbPool, &pSRBPool->nextPage ) ) ! 766: { ! 767: pSRBPoolNext = (SRBPool *)queue_next( &pSRBPool->nextPage ); ! 768: ! 769: if ( !pSRBPool->srbInUseCount ) ! 770: { ! 771: if ( ++idlePageCount > kSRBPoolMaxFreePages ) ! 772: { ! 773: queue_remove( &srbPool, pSRBPool, SRBPool *, nextPage ); ! 774: ! 775: // kprintf("SCSI(Symbios8xx): Sym8xxShrinkSRBPool\n\r"); ! 776: ! 777: kr = kmem_free(IOVmTaskSelf(), (vm_offset_t) pSRBPool, page_size ); ! 778: if ( kr != KERN_SUCCESS ) ! 779: { ! 780: IOPanic("SCSI(Symbios8xx): kmem_free failed - Help me\n\r"); ! 781: } ! 782: } ! 783: } ! 784: pSRBPool = pSRBPoolNext; ! 785: } ! 786: ! 787: [srbPoolLock unlock]; ! 788: ! 789: } ! 790: ! 791: /*-----------------------------------------------------------------------------* ! 792: * This routines grows the SRBPool. It runs on its own thread to avoid pager deadlocks. ! 793: * ! 794: * We need this entry thunk since the thread creation routines dont support objC ! 795: * interfaces directly. ! 796: * ! 797: *-----------------------------------------------------------------------------*/ ! 798: IOThreadFunc Sym8xxGrowSRBPool( Sym8xxController *controller ) ! 799: { ! 800: [controller Sym8xxGrowSRBPool]; ! 801: return NULL; ! 802: } ! 803: ! 804: - (void) Sym8xxGrowSRBPool ! 805: { ! 806: SRBPool *pSRBPool; ! 807: SRB *pSRB; ! 808: kern_return_t kr; ! 809: u_int32_t numSRBs; ! 810: u_int32_t i; ! 811: ! 812: while ( 1 ) ! 813: { ! 814: [srbPoolGrowLock lockWhen: kSRBGrowPoolRunning]; ! 815: ! 816: // kprintf("SCSI(Symbios8xx): Sym8xxGrowSRBPool\n\r"); ! 817: ! 818: kr = kmem_alloc_wired(IOVmTaskSelf(), (vm_offset_t *) &pSRBPool, page_size ); ! 819: if ( kr != KERN_SUCCESS ) ! 820: { ! 821: IOPanic("kmem_alloc_wired failed - Help me\n\r"); ! 822: } ! 823: ! 824: IOPhysicalFromVirtual((vm_task_t)IOVmTaskSelf(), (vm_offset_t)pSRBPool, (vm_offset_t *)&pSRBPool->pagePhysAddr ); ! 825: ! 826: pSRBPool->srbInUseCount = 0; ! 827: ! 828: numSRBs = (page_size - sizeof(SRBPool)) / sizeof(SRB); ! 829: pSRB = (SRB *) (pSRBPool+1); ! 830: ! 831: queue_init( &pSRBPool->freeSRBList ); ! 832: for ( i=0; i < numSRBs; i++ ) ! 833: { ! 834: queue_enter( &pSRBPool->freeSRBList, (pSRB+i), SRB *, srbQ ); ! 835: } ! 836: ! 837: [srbPoolLock lock]; ! 838: queue_enter( &srbPool, pSRBPool, SRBPool *, nextPage ); ! 839: [srbPoolLock unlock]; ! 840: ! 841: srbPoolGrow = NO; ! 842: [srbPoolGrowLock unlockWith: kSRBGrowPoolIdle]; ! 843: } ! 844: } ! 845: ! 846: ! 847: /*-----------------------------------------------------------------------------* ! 848: * This routine interfaces between the system timer and our I/O Thread. It ! 849: * sends a message to the IOThread to run the -timeoutOccurred routine which ! 850: * does various timing functions for the driver. See Sym8xxExecuteRequest(timeoutOccurred). ! 851: * ! 852: *-----------------------------------------------------------------------------*/ ! 853: IOThreadFunc Sym8xxTimerReq( Sym8xxController *device ) ! 854: { ! 855: msg_header_t msg = { 0 }; ! 856: ! 857: msg.msg_size = sizeof (msg); ! 858: msg.msg_remote_port = device->interruptPortKern; ! 859: msg.msg_id = IO_TIMEOUT_MSG; ! 860: ! 861: msg_send_from_kernel(&msg, MSG_OPTION_NONE, 0); ! 862: ! 863: return NULL; ! 864: } ! 865: ! 866: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.