|
|
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: * Copyright (c) 1991, 1992, 1993 NeXT Computer, Inc. ! 26: * ! 27: * Abstract superclass for SCSI controllers. ! 28: * ! 29: * HISTORY ! 30: * ! 31: * 1998-3-11 [email protected] Moved SCSI user to drvSCSIServer project. ! 32: * 12 December 1997 Martin Minow at Apple ! 33: * Added IOSCISUser kernel support ! 34: * 14 June 1995 Doug Mitchell at NeXT ! 35: * Added SCSI-3 support. ! 36: * 25 June 1993 David E. Bohman at NeXT ! 37: * Cleaned up some (made machine independent). Added this header. ! 38: * ?? ??? ???? Doug Mitchell at NeXT ! 39: * Created. ! 40: */ ! 41: ! 42: #import <driverkit/IOSCSIController.h> ! 43: #import <machkit/NXLock.h> ! 44: #import <driverkit/return.h> ! 45: #import <driverkit/generalFuncs.h> ! 46: #import <driverkit/interruptMsg.h> ! 47: #import <driverkit/align.h> ! 48: #import <driverkit/xpr_mi.h> ! 49: #import <driverkit/kernelDriver.h> ! 50: ! 51: static int scUnitNum = 0; ! 52: ! 53: /* ! 54: * An element of _reserveQ. ! 55: */ ! 56: typedef struct { ! 57: unsigned long long target; ! 58: unsigned long long lun; ! 59: id owner; ! 60: id lender; ! 61: queue_chain_t link; ! 62: } reserveElt; ! 63: ! 64: @interface IOSCSIController(private) ! 65: ! 66: - (reserveElt *) searchReserveQ: (unsigned long long) target ! 67: lun: (unsigned long long) lun; ! 68: ! 69: @end ! 70: ! 71: @implementation IOSCSIController ! 72: ! 73: - initFromDeviceDescription:deviceDescription ! 74: { ! 75: unsigned int target, lun; ! 76: char devName[20]; ! 77: IODMAAlignment dmaAlign; ! 78: ! 79: queue_init(&_reserveQ); ! 80: if([[self class] deviceStyle] == IO_DirectDevice) { ! 81: /* ! 82: * Allow a subclass to act like an indirect device by ! 83: * skipping this stuff. ! 84: */ ! 85: if ([super initFromDeviceDescription:deviceDescription] == nil) ! 86: return nil; ! 87: ! 88: if ([self startIOThread] != IO_R_SUCCESS) { ! 89: [self free]; return nil; ! 90: } ! 91: } else ! 92: [self setDeviceDescription:deviceDescription]; ! 93: ! 94: /* ! 95: * We keep track of all scsi controllers in the system by number. ! 96: * Their device names look like 'scXX'. Note that the rest of the ! 97: * system actually LOOKS FOR sc0 instead of something more basic, ! 98: * so we're forced into using this scheme. ! 99: */ ! 100: [self setUnit:scUnitNum]; ! 101: sprintf(devName, "sc%d", scUnitNum++); ! 102: [self setName:devName]; ! 103: [self setDeviceKind:"sc"]; ! 104: ! 105: /* ! 106: * Determine worst case alignment requirement for this controller. ! 107: * The result will be used to do the alignment in ! 108: * allocateBufferOfLength. ! 109: */ ! 110: [self getDMAAlignment:&dmaAlign]; ! 111: _worstCaseAlign = dmaAlign.readStart; ! 112: if(dmaAlign.writeStart > _worstCaseAlign) { ! 113: _worstCaseAlign = dmaAlign.writeStart; ! 114: } ! 115: if(dmaAlign.readLength > _worstCaseAlign) { ! 116: _worstCaseAlign = dmaAlign.readLength; ! 117: } ! 118: if(dmaAlign.writeLength > _worstCaseAlign) { ! 119: _worstCaseAlign = dmaAlign.writeLength; ! 120: } ! 121: if(_worstCaseAlign == 1) { ! 122: _worstCaseAlign = 0; ! 123: } ! 124: return self; ! 125: } ! 126: ! 127: ! 128: - free ! 129: { ! 130: int size; ! 131: reserveElt *rsvElt; ! 132: ! 133: if(_reserveQ.next != NULL) { ! 134: /* ! 135: * Avoid if queue hasn't been init'd.... ! 136: */ ! 137: while(!queue_empty(&_reserveQ)) { ! 138: rsvElt = (reserveElt *)queue_first(&_reserveQ); ! 139: queue_remove(&_reserveQ, rsvElt, reserveElt *, link); ! 140: IOFree(rsvElt, sizeof(*rsvElt)); ! 141: } ! 142: } ! 143: ! 144: /* ! 145: * If this is the last IOSCSIController to have been initialized, ! 146: * update the global unit counter. ! 147: */ ! 148: if([self unit] == (scUnitNum - 1)) { ! 149: scUnitNum--; ! 150: } ! 151: ! 152: /* XXX what about the SCSIController thread?? */ ! 153: return [super free]; ! 154: } ! 155: ! 156: /* ! 157: * Release a reserved element for the owner ! 158: * _reserveLock held ! 159: */ ! 160: ! 161: - (void) releaseReservationElement:(reserveElt *) rsvElt ! 162: { ! 163: id lender; ! 164: ! 165: lender = rsvElt->lender; ! 166: if( lender ! 167: && [lender respondsTo:@selector(reacquireTarget)] ! 168: && (IO_R_SUCCESS == [lender reacquireTarget]) ) { ! 169: ! 170: rsvElt->lender = nil; ! 171: rsvElt->owner = lender; ! 172: ! 173: } else { ! 174: queue_remove(&_reserveQ, rsvElt, reserveElt *, link); ! 175: IOFree(rsvElt, sizeof(*rsvElt)); ! 176: _reserveCount--; ! 177: } ! 178: } ! 179: ! 180: /* ! 181: * Attempt to reserve specified target and lun for calling device. Returns ! 182: * non-zero if device already reserved. ! 183: * ! 184: * This is called by a client (e.g., SCSIDisk) in an attempt to mark a ! 185: * particular target/lun as being "in use" by that client; it's normally ! 186: * only used during SCSI bus configuration. ! 187: */ ! 188: ! 189: - releaseAllUnitsForOwner: owner; ! 190: { ! 191: reserveElt *rsvElt, *nxtElt; ! 192: ! 193: [_reserveLock lock]; ! 194: ! 195: rsvElt = (reserveElt *) queue_first(&_reserveQ); ! 196: while ( !queue_end(&_reserveQ, (queue_t) rsvElt) ) { ! 197: nxtElt = (reserveElt *) rsvElt->link.next; ! 198: if (rsvElt->owner == owner) { ! 199: [self releaseReservationElement:rsvElt]; ! 200: } ! 201: rsvElt = nxtElt; ! 202: } ! 203: ! 204: [_reserveLock unlock]; ! 205: } ! 206: ! 207: - (int) reserveTarget:(u_char)target lun:(u_char)lun forOwner:(id)owner ! 208: { ! 209: return [self reserveSCSI3Target:(unsigned long long)target ! 210: lun:(unsigned long long)lun ! 211: forOwner:owner]; ! 212: } ! 213: ! 214: ! 215: - (void)releaseTarget:(u_char)target lun:(u_char)lun forOwner:(id)owner ! 216: { ! 217: [self releaseSCSI3Target:(unsigned long long)target ! 218: lun:(unsigned long long)lun ! 219: forOwner:owner]; ! 220: } ! 221: ! 222: - (int)reserveSCSI3Target : (unsigned long long)target ! 223: lun : (unsigned long long)lun ! 224: forOwner : owner ! 225: { ! 226: int rtn = 0; ! 227: int targInt = target; ! 228: reserveElt *rsvElt; ! 229: id currentOwner; ! 230: ! 231: xpr_sdev("reserveSCSI3Target: t %x:%x l %x:%x\n", ! 232: (unsigned)(target >> 32), ! 233: (unsigned)target, ! 234: (unsigned)(lun >> 32), ! 235: (unsigned)lun, 5); ! 236: [_reserveLock lock]; ! 237: ! 238: if(targInt >= [self numberOfTargets]) { ! 239: rtn = 1; ! 240: goto out; ! 241: } ! 242: rsvElt = [self searchReserveQ:target lun:lun]; ! 243: if(rsvElt) { ! 244: xpr_sdev("...already reserved\n", 1,2,3,4,5); ! 245: ! 246: currentOwner = rsvElt->owner; ! 247: if( (rsvElt->lender == nil) // only 1 deep stack ! 248: && [currentOwner respondsTo:@selector(requestReleaseTarget)] ! 249: && (IO_R_SUCCESS == [currentOwner requestReleaseTarget]) ) { ! 250: ! 251: rsvElt->lender = currentOwner; ! 252: rsvElt->owner = owner; ! 253: ! 254: } else ! 255: rtn = 1; ! 256: ! 257: goto out; ! 258: } ! 259: ! 260: /* ! 261: * Cons up a new element for _reserveQ. ! 262: */ ! 263: rsvElt = IOMalloc(sizeof(*rsvElt)); ! 264: rsvElt->target = target; ! 265: rsvElt->lun = lun; ! 266: rsvElt->owner = owner; ! 267: rsvElt->lender = nil; ! 268: queue_enter(&_reserveQ, rsvElt, reserveElt *, link); ! 269: _reserveCount++; ! 270: out: ! 271: [_reserveLock unlock]; ! 272: return(rtn); ! 273: } ! 274: ! 275: - (void)releaseSCSI3Target : (unsigned long long)target ! 276: lun : (unsigned long long)lun ! 277: forOwner : owner ! 278: { ! 279: int targInt = target; ! 280: reserveElt *rsvElt; ! 281: ! 282: xpr_sdev("releaseSCSI3Target: t %x:%x l %x:%x\n", ! 283: (unsigned)(target >> 32), ! 284: (unsigned)target, ! 285: (unsigned)(lun >> 32), ! 286: (unsigned)lun, 5); ! 287: [_reserveLock lock]; ! 288: if(targInt >= [self numberOfTargets]) { ! 289: IOLog("IOSCSIController releaseTarget: INVALID TARGET\n"); ! 290: goto out; ! 291: } ! 292: rsvElt = [self searchReserveQ:target lun:lun]; ! 293: if(rsvElt == NULL) { ! 294: IOLog("IOSCSIController releaseTarget: NOT RESERVED\n"); ! 295: goto out; ! 296: } ! 297: if(rsvElt->owner != owner) { ! 298: IOLog("IOSCSIController releaseTarget: INVALID OWNER\n"); ! 299: goto out; ! 300: } ! 301: ! 302: [self releaseReservationElement:rsvElt]; ! 303: ! 304: out: ! 305: [_reserveLock unlock]; ! 306: } ! 307: ! 308: ! 309: /* ! 310: * Returns the number of SCSI targets supported. Default is 8, returned here. ! 311: * Subclasses may override if they support more than 8 targets. ! 312: */ ! 313: - (int)numberOfTargets ! 314: { ! 315: return SCSI_NTARGETS; ! 316: } ! 317: ! 318: - (unsigned int) numReserved ! 319: { ! 320: return _reserveCount; ! 321: } ! 322: ! 323: - (sc_status_t) executeRequest : (IOSCSIRequest *)scsiReq ! 324: buffer : (void *)buffer /* data destination */ ! 325: client : (vm_task_t)client ! 326: { ! 327: /* ! 328: * Subclass must implement this; we should never get here. ! 329: */ ! 330: return SR_IOST_INVALID; ! 331: } ! 332: ! 333: /* ! 334: * executeRequest (with an IOMemoryDescriptor) requires buffers aligned to ! 335: * IO_SCSI_DMA_ALIGNMENT. The client identification is contained in the ! 336: * IOMemoryDescriptor object ! 337: */ ! 338: - (sc_status_t) executeRequest : (IOSCSIRequest *) scsiReq ! 339: ioMemoryDescriptor : (IOMemoryDescriptor *) ioMemoryDescriptor ! 340: { ! 341: /* ! 342: * Subclass must implement this; we should never get here. ! 343: */ ! 344: return SR_IOST_INVALID; ! 345: } ! 346: ! 347: - (sc_status_t) executeSCSI3Request : (IOSCSI3Request *)scsiReq ! 348: buffer : (void *)buffer /* data destination */ ! 349: client : (vm_task_t)client ! 350: { ! 351: /* ! 352: * Optional. ! 353: */ ! 354: return SR_IOST_CMDREJ; ! 355: } ! 356: ! 357: /* ! 358: * executeRequest (with an IOMemoryDescriptor) requires buffers aligned to ! 359: * IO_SCSI_DMA_ALIGNMENT. The client identification is contained in the ! 360: * IOMemoryDescriptor object ! 361: */ ! 362: - (sc_status_t) executeSCSI3Request : (IOSCSI3Request *) scsiReq ! 363: ioMemoryDescriptor : (IOMemoryDescriptor *) ioMemoryDescriptor ! 364: { ! 365: /* ! 366: * Optional. ! 367: */ ! 368: return SR_IOST_CMDREJ; ! 369: } ! 370: ! 371: - (sc_status_t) resetSCSIBus ! 372: { ! 373: return SR_IOST_INVALID; ! 374: } ! 375: ! 376: /* ! 377: * Convert an sc_status_t to an IOReturn. ! 378: */ ! 379: - (IOReturn)returnFromScStatus:(sc_status_t)sc_status ! 380: { ! 381: switch(sc_status) { ! 382: case SR_IOST_GOOD: ! 383: return(IO_R_SUCCESS); ! 384: case SR_IOST_ALIGN: ! 385: return(IO_R_ALIGN); ! 386: case SR_IOST_CMDREJ: ! 387: return(IO_R_INVALID_ARG); ! 388: case SR_IOST_IPCFAIL: ! 389: return(IO_R_IPC_FAILURE); ! 390: case SR_IOST_INT: ! 391: return(IO_R_INTERNAL); ! 392: case SR_IOST_MEMF: ! 393: return(IO_R_VM_FAILURE); ! 394: case SR_IOST_MEMALL: ! 395: return(IO_R_RESOURCE); ! 396: case SR_IOST_DMA: ! 397: return(IO_R_DMA); ! 398: case SR_IOST_WP: ! 399: return(IO_R_NOT_WRITABLE); ! 400: default: ! 401: return(IO_R_IO); ! 402: } ! 403: } ! 404: ! 405: ! 406: /* ! 407: * Determine maximum DMA which can be peformed in a single call to ! 408: * executeRequest:buffer:client:. ! 409: */ ! 410: - (unsigned)maxTransfer ! 411: { ! 412: return 16 * 1024 * 1024; /* XXX */ ! 413: } ! 414: ! 415: ! 416: /* ! 417: * Return required DMA alignment for current architecture. ! 418: * May be overridden by subclass. ! 419: */ ! 420: - (void)getDMAAlignment:(IODMAAlignment *)alignment ! 421: { ! 422: alignment->readStart = 1; /* XXX */ ! 423: alignment->writeStart = 1; /* XXX */ ! 424: alignment->readLength = 1; /* XXX */ ! 425: alignment->writeLength = 1; /* XXX */ ! 426: } ! 427: ! 428: ! 429: ! 430: - (unsigned int) numQueueSamples ! 431: { ! 432: return 0; ! 433: } ! 434: ! 435: ! 436: - (unsigned int) sumQueueLengths ! 437: { ! 438: return 0; ! 439: } ! 440: ! 441: ! 442: - (unsigned int) maxQueueLength ! 443: { ! 444: return 0; ! 445: } ! 446: ! 447: ! 448: - (void)resetStats ! 449: { ! 450: } ! 451: ! 452: ! 453: - (IOReturn)getIntValues:(unsigned *)parameterArray ! 454: forParameter:(IOParameterName)parameterName ! 455: count:(unsigned int *)count ! 456: { ! 457: unsigned maxCount = *count; ! 458: unsigned *returnedCount = count; ! 459: ! 460: unsigned int params[IO_SCSI_CONTROLLER_STAT_ARRAY_SIZE]; ! 461: int i; ! 462: if(maxCount == 0) { ! 463: maxCount = IO_MAX_PARAMETER_ARRAY_LENGTH; ! 464: } ! 465: if(strcmp(parameterName, IO_SCSI_CONTROLLER_STATS) == 0) { ! 466: params[IO_SCSI_CONTROLLER_MAX_QUEUE_LENGTH] = ! 467: [self maxQueueLength]; ! 468: params[IO_SCSI_CONTROLLER_QUEUE_SAMPLES] = ! 469: [self numQueueSamples]; ! 470: params[IO_SCSI_CONTROLLER_QUEUE_TOTAL] = ! 471: [self sumQueueLengths]; ! 472: ! 473: *returnedCount = 0; ! 474: for(i=0; i<IO_SCSI_CONTROLLER_STAT_ARRAY_SIZE; i++) { ! 475: if(*returnedCount == maxCount) ! 476: break; ! 477: parameterArray[i] = params[i]; ! 478: (*returnedCount)++; ! 479: } ! 480: return IO_R_SUCCESS; ! 481: } ! 482: else if(strcmp(parameterName, IO_IS_A_SCSI_CONTROLLER) == 0) { ! 483: /* ! 484: * No data; just let caller know we're a disk. ! 485: */ ! 486: *returnedCount = 0; ! 487: return IO_R_SUCCESS; ! 488: } ! 489: else { ! 490: return [super getIntValues:parameterArray ! 491: forParameter:parameterName ! 492: count:count]; ! 493: ! 494: } ! 495: } ! 496: ! 497: ! 498: - (IOReturn)setIntValues:(unsigned *)parameterArray ! 499: forParameter:(IOParameterName)parameterName ! 500: count:(unsigned int)count ! 501: { ! 502: if(strcmp(parameterName, IO_SCSI_CONTROLLER_STATS) == 0) { ! 503: [self resetStats]; ! 504: return IO_R_SUCCESS; ! 505: } ! 506: else ! 507: return [super setIntValues:parameterArray ! 508: forParameter:parameterName ! 509: count : count]; ! 510: } ! 511: ! 512: /* ! 513: * Allocate some well-aligned memory. *actualStart is what has to be ! 514: * IOFree()'d eventually; *actualLength is the size arg to IOFree. The ! 515: * return value is the well-aligned pointer. There is room at the end of ! 516: * the returned pointer's buffer to do a DMA_DO_ALIGN(...,size, ! 517: * _worstCaseAlign). ! 518: */ ! 519: ! 520: - (void *)allocateBufferOfLength: (unsigned)length ! 521: actualStart : (void **)actualStart ! 522: actualLength : (unsigned *)actualLength ! 523: { ! 524: char *cp; ! 525: int cnt = length + 2 * _worstCaseAlign; ! 526: ! 527: cp = IOMalloc(cnt); ! 528: *actualStart = cp; ! 529: *actualLength = cnt; ! 530: if(_worstCaseAlign > 1) { ! 531: return(IOAlign(void *, cp, _worstCaseAlign)); ! 532: } ! 533: else { ! 534: return cp; ! 535: } ! 536: } ! 537: ! 538: - property_IODeviceClass:(char *)classes length:(unsigned int *)maxLen ! 539: { ! 540: strcpy( classes, IOClassSCSIController); ! 541: return( self); ! 542: } ! 543: ! 544: - property_IODeviceType:(char *)classes length:(unsigned int *)maxLen ! 545: { ! 546: if( [[self class] deviceStyle] == IO_DirectDevice) ! 547: strcat( classes, " "IOTypeSCSI); ! 548: return( self); ! 549: } ! 550: ! 551: @end ! 552: ! 553: @implementation IOSCSIController(private) ! 554: ! 555: /* ! 556: * Locate reserveElt for specified target and LUN. _reserveLock must be ! 557: * held on entry. ! 558: */ ! 559: - (reserveElt *)searchReserveQ : (unsigned long long)target ! 560: lun : (unsigned long long)lun ! 561: { ! 562: reserveElt *rsvElt; ! 563: ! 564: rsvElt = (reserveElt *)queue_first(&_reserveQ); ! 565: while(!queue_end(&_reserveQ, (queue_t)rsvElt)) { ! 566: if((rsvElt->target == target) && (rsvElt->lun == lun)) { ! 567: return rsvElt; ! 568: } ! 569: rsvElt = (reserveElt *)rsvElt->link.next; ! 570: } ! 571: return NULL; ! 572: } ! 573: ! 574: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.