|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1992, 1993 NeXT Computer, Inc. ! 3: * ! 4: * Adaptec AHA-1542 SCSI controller driver. ! 5: * ! 6: * HISTORY ! 7: * ! 8: * 13 Apr 1993 Doug Mitchell at NeXT ! 9: * Rewrote using I/O thread which has exclusive access to hardware. ! 10: * ! 11: * 22 Nov 1992 Brian Pinkerton at NeXT ! 12: * Created from non-driverkit driver. ! 13: */ ! 14: ! 15: #import <sys/types.h> ! 16: #import <bsd/sys/param.h> ! 17: #import <objc/Object.h> ! 18: #import <kernserv/queue.h> ! 19: #import <kernserv/prototypes.h> ! 20: #import <driverkit/return.h> ! 21: #import <driverkit/generalFuncs.h> ! 22: #import <driverkit/kernelDriver.h> ! 23: #import <driverkit/i386/kernelDriver.h> ! 24: #import <driverkit/interruptMsg.h> ! 25: #import <driverkit/scsiTypes.h> ! 26: #import <bsd/dev/scsireg.h> ! 27: #import "scsivar.h" ! 28: #import <mach/message.h> ! 29: #import <mach/port.h> ! 30: #import <mach/mach_interface.h> ! 31: #import <machkit/NXLock.h> ! 32: #import <kernserv/ns_timer.h> ! 33: #import <driverkit/i386/ioPorts.h> ! 34: ! 35: #import <driverkit/i386/directDevice.h> ! 36: #import <driverkit/i386/IOEISADeviceDescription.h> ! 37: #import <driverkit/IOSCSIController.h> ! 38: #import "AHAController.h" ! 39: #import "AHATypes.h" ! 40: #import "AHAInline.h" ! 41: #import "AHAThread.h" ! 42: ! 43: extern unsigned ffs(unsigned mask); ! 44: ! 45: /* ! 46: * Template for command message sent to the I/O thread. ! 47: */ ! 48: static msg_header_t AHAMessageTemplate = { ! 49: 0, // msg_unused ! 50: 1, // msg_simple ! 51: sizeof(msg_header_t), // msg_size ! 52: MSG_TYPE_NORMAL, // msg_type ! 53: PORT_NULL, // msg_local_port ! 54: PORT_NULL, // msg_remote_port - TO ! 55: // BE FILLED IN ! 56: IO_COMMAND_MSG // msg_id ! 57: }; ! 58: ! 59: /* ! 60: * Private methods implemented in this file. ! 61: */ ! 62: @interface AHAController(PrivateMethods) ! 63: - (BOOL) probeAtPortBase : (IOEISAPortAddress) portBase; ! 64: - (IOReturn)executeCmdBuf : (AHACommandBuf *)cmdBuf; ! 65: @end ! 66: ! 67: ! 68: @implementation AHAController ! 69: ! 70: /* ! 71: * Probe, configure board, and init new instance. ! 72: */ ! 73: + (BOOL)probe:deviceDescription ! 74: { ! 75: AHAController *aha = [self alloc]; ! 76: IORange ioPort; ! 77: ! 78: ddm_init("AHAController probe\n", 1,2,3,4,5); ! 79: aha->ioThreadRunning = NO; ! 80: ! 81: /* ! 82: * Check that we have some IO Ports assigned, and probe using the ! 83: * first IO Port. ! 84: * -probeAtPortBase returns TRUE if there's an AHA Controller present. ! 85: */ ! 86: if ([deviceDescription numPortRanges] < 1) { ! 87: IOLog("AHAController: can't determine port base!\n"); ! 88: [aha free]; ! 89: return NO; ! 90: } ! 91: ioPort = [deviceDescription portRangeList][0]; ! 92: if (![aha probeAtPortBase:ioPort.start]) { ! 93: IOLog("Adaptec154x Not Found at port 0x%x\n", ioPort.start); ! 94: [aha free]; ! 95: return NO; ! 96: } ! 97: return ([aha initFromDeviceDescription:deviceDescription] ? YES : NO); ! 98: } ! 99: ! 100: - initFromDeviceDescription:deviceDescription ! 101: { ! 102: unsigned Lun; ! 103: kern_return_t krtn; ! 104: ! 105: ddm_init("AHAController initFromDeviceDescription\n", 1,2,3,4,5); ! 106: ! 107: queue_init(&outstandingQ); ! 108: queue_init(&pendingQ); ! 109: queue_init(&commandQ); ! 110: commandLock = [[NXLock alloc] init]; ! 111: outstandingCount = 0; ! 112: dmaLockCount = 0; ! 113: numFreeCcbs = AHA_QUEUE_SIZE; ! 114: ! 115: /* ! 116: * Note the I/O thread provided by IOSCSIController is running ! 117: * upon return from the following method. ! 118: */ ! 119: if ([super initFromDeviceDescription:deviceDescription] == nil) ! 120: return [self free]; ! 121: interruptPortKern = IOConvertPort([self interruptPort], ! 122: IO_KernelIOTask, ! 123: IO_Kernel); ! 124: ioThreadRunning = YES; ! 125: ! 126: /* ! 127: * Check the channel and irq we just found against what's in our ! 128: * device description. If they don't match, print a nasty warning ! 129: * message and fail. ! 130: */ ! 131: if ([deviceDescription numChannels] < 1 || ! 132: [deviceDescription channel] != config.dma_channel) { ! 133: IOLog("AHAController: Actual DMA Channel (%d) doesn't match " ! 134: "configured value (%d)!\n", config.dma_channel, ! 135: ([deviceDescription numChannels] ? ! 136: [deviceDescription channel] : 0)); ! 137: return [self free]; ! 138: } ! 139: ! 140: if ([deviceDescription numInterrupts] < 1 || ! 141: [deviceDescription interrupt] != config.irq) { ! 142: IOLog("AHAController: Actual IRQ (%d) doesn't match " ! 143: "configured value (%d)!\n", config.irq, ! 144: ([deviceDescription numInterrupts] ? ! 145: [deviceDescription interrupt] : 0)); ! 146: return [self free]; ! 147: } ! 148: ! 149: /* ! 150: * Try to set up DMA. Set the appropriate mode on the channel, and ! 151: * try to enable it. ! 152: */ ! 153: if ([self setTransferMode:IO_Cascade forChannel:0] != IO_R_SUCCESS || ! 154: [self enableChannel:0] != IO_R_SUCCESS) { ! 155: IOLog("AHAController: couldn't init DMA!\n"); ! 156: return [super free]; ! 157: } ! 158: ! 159: /* ! 160: * Allocate Mailboxes and CCB's from low 16 M of memory. ! 161: */ ! 162: ahaMbArea = IOMallocLow(sizeof(struct aha_mb_area)); ! 163: ahaCcb = IOMallocLow(sizeof(struct ccb) * AHA_QUEUE_SIZE); ! 164: ! 165: /* ! 166: * Initialize driver data structures: set up the mailbox in/out area, ! 167: * and initialize the CCB queues. ! 168: * ! 169: * Note that if we fail, the call to [super free] will release (and ! 170: * disable) our resources (IRQ, DMA channel, portRanges). ! 171: */ ! 172: if (!aha_setup_mb_area(ioBase, ahaMbArea, ahaCcb)) { ! 173: IOLog("AHAController: couldn't set up mailbox area!\n"); ! 174: return [self free]; ! 175: } ! 176: ! 177: [self resetStats]; ! 178: ! 179: /* ! 180: * Reserve our target, enable interrupts, and go. ! 181: */ ! 182: for(Lun=0; Lun<SCSI_NLUNS; Lun++) { ! 183: [self reserveTarget:config.scsi_id lun:Lun forOwner:self]; ! 184: } ! 185: ! 186: [self enableAllInterrupts]; /* turn on interrupts */ ! 187: ! 188: /* ! 189: * Set the port queue length to the maximum size. ! 190: */ ! 191: krtn = port_set_backlog(task_self(), [self interruptPort], ! 192: PORT_BACKLOG_MAX); ! 193: if(krtn) { ! 194: IOLog("%s: error %d on port_set_backlog()\n", ! 195: [self name], krtn); ! 196: /* Oh well... */ ! 197: } ! 198: [self resetSCSIBus]; ! 199: [self registerDevice]; /* this is the last thing we do! */ ! 200: ! 201: return self; ! 202: } ! 203: ! 204: /* ! 205: * This is slightly incorrect, since we can actually handle more if some of ! 206: * the entries in the scatter/gather list can be more than PAGE_SIZE. In ! 207: * practice, tho, they're never bigger, so we'll make this our max size. ! 208: * 18 Mar 93 dmitch - use (AHA_SG_COUNT - 1) since requests (the first ! 209: * and the last) can cross page boundaries. ! 210: */ ! 211: - (unsigned)maxTransfer ! 212: { ! 213: return (AHA_SG_COUNT - 1) * PAGE_SIZE; ! 214: } ! 215: ! 216: /* ! 217: * kill I/O thread, free up local dynamically allocated resources, ! 218: * then have super release resources. ! 219: */ ! 220: - free ! 221: { ! 222: AHACommandBuf cmdBuf; ! 223: ! 224: if(ioThreadRunning) { ! 225: cmdBuf.op = AO_Abort; ! 226: [self executeCmdBuf:&cmdBuf]; ! 227: } ! 228: if(ahaMbArea) { ! 229: IOFreeLow(ahaMbArea, sizeof(struct aha_mb_area)); ! 230: } ! 231: if(ahaCcb) { ! 232: IOFreeLow(ahaCcb, sizeof(struct ccb) * AHA_QUEUE_SIZE); ! 233: } ! 234: if(commandLock) { ! 235: [commandLock free]; ! 236: } ! 237: return [super free]; ! 238: } ! 239: ! 240: /* ! 241: * Statistics support. ! 242: */ ! 243: - (unsigned int) numQueueSamples ! 244: { ! 245: return totalCommands; ! 246: } ! 247: ! 248: ! 249: - (unsigned int) sumQueueLengths ! 250: { ! 251: return queueLenTotal; ! 252: } ! 253: ! 254: ! 255: - (unsigned int) maxQueueLength ! 256: { ! 257: return maxQueueLen; ! 258: } ! 259: ! 260: ! 261: - (void)resetStats ! 262: { ! 263: totalCommands = 0; ! 264: queueLenTotal = 0; ! 265: maxQueueLen = 0; ! 266: } ! 267: ! 268: /* ! 269: * Do a SCSI command, as specified by an IOSCSIRequest. All the ! 270: * work is done by the I/O thread. ! 271: */ ! 272: - (sc_status_t) executeRequest : (IOSCSIRequest *)scsiReq ! 273: buffer : (void *)buffer ! 274: client : (vm_task_t)client ! 275: { ! 276: AHACommandBuf cmdBuf; ! 277: ! 278: ddm_exp("executeRequest: cmdBuf 0x%x\n", &cmdBuf, 2,3,4,5); ! 279: ! 280: cmdBuf.op = AO_Execute; ! 281: cmdBuf.scsiReq = scsiReq; ! 282: cmdBuf.buffer = buffer; ! 283: cmdBuf.client = client; ! 284: ! 285: [self executeCmdBuf:&cmdBuf]; ! 286: ! 287: ddm_exp("executeRequest: cmdBuf 0x%x complete; result %d\n", ! 288: &cmdBuf, cmdBuf.result, 3,4,5); ! 289: return cmdBuf.result; ! 290: } ! 291: ! 292: ! 293: /* ! 294: * Reset the SCSI bus. All the work is done by the I/O thread. ! 295: */ ! 296: - (sc_status_t)resetSCSIBus ! 297: { ! 298: AHACommandBuf cmdBuf; ! 299: ! 300: ddm_exp("resetSCSIBus: cmdBuf 0x%x\n", &cmdBuf, 2,3,4,5); ! 301: ! 302: cmdBuf.op = AO_Reset; ! 303: [self executeCmdBuf:&cmdBuf]; ! 304: return cmdBuf.result; ! 305: } ! 306: /* ! 307: * The following 6 methods are all called from the I/O thread in ! 308: * IODirectDevice. ! 309: */ ! 310: ! 311: /* ! 312: * Called from the I/O thread when it receives an interrupt message. ! 313: */ ! 314: - (void)interruptOccurred ! 315: { ! 316: struct ccb *ccb; ! 317: aha_intr_reg_t intr; ! 318: aha_mb_t *mb; ! 319: int i; ! 320: ! 321: ddm_thr("interruptOccurred\n", 1,2,3,4,5); ! 322: ! 323: intr = aha_get_intr(ioBase); ! 324: aha_clr_intr(ioBase); ! 325: ! 326: if (!intr.mb_in_full) ! 327: return; ! 328: ! 329: /* ! 330: * Find all ccb's which the controller has marked completed ! 331: * and commandComplete: them. ! 332: */ ! 333: mb = ahaMbArea->mb_in; ! 334: for (i = 0; i < AHA_MB_CNT; i++, mb++) { ! 335: if (mb->mb_stat != AHA_MB_IN_FREE) { ! 336: ! 337: /* ! 338: * FIXME - need IOVirtualFromPhysical(); assume for ! 339: * now that we can access all physical addresses. ! 340: */ ! 341: ccb = (struct ccb *)aha_get_24(mb->ccb_addr); ! 342: mb->mb_stat = AHA_MB_IN_FREE; ! 343: queue_remove(&outstandingQ, ccb, struct ccb *, ccbQ); ! 344: ASSERT(outstandingCount != 0); ! 345: outstandingCount--; ! 346: ! 347: [self commandCompleted:ccb reason:CS_Complete]; ! 348: } ! 349: } ! 350: ! 351: /* ! 352: * Handle possible pending commands (now that we've dequeued at least ! 353: * one CCB). ! 354: */ ! 355: [self runPendingCommands]; ! 356: ! 357: /* ! 358: * One more thing - since we probably just freed up at least one ! 359: * ccb, process possible entries waiting in commandQ. ! 360: */ ! 361: [self commandRequestOccurred]; ! 362: ddm_thr("interruptOccurred: DONE\n", 1,2,3,4,5); ! 363: } ! 364: ! 365: /* ! 366: * These three should not occur; they are here as error traps. All three are ! 367: * called out from the I/O thread upon receipt of messages which it should ! 368: * not be seeing. ! 369: */ ! 370: - (void)interruptOccurredAt:(int)localNum ! 371: { ! 372: IOLog("%s: interruptOccurredAt:%d\n", [self name], localNum); ! 373: } ! 374: ! 375: - (void)otherOccurred:(int)id ! 376: { ! 377: IOLog("%s: otherOccurred:%d\n", [self name], id); ! 378: } ! 379: ! 380: - (void)receiveMsg ! 381: { ! 382: IOLog("%s: receiveMsg\n", [self name]); ! 383: ! 384: /* ! 385: * We have to let IODirectDevice take care of this (i.e., dequeue the ! 386: * bogus message). ! 387: */ ! 388: [super receiveMsg]; ! 389: } ! 390: ! 391: /* ! 392: * Called from the I/O thread when it receives a timeout ! 393: * message. We send these messages ourself from ahaTimeout() in ! 394: * AHAThread.m. ! 395: */ ! 396: - (void)timeoutOccurred ! 397: { ! 398: struct ccb *ccb, *nextCcb; ! 399: ns_time_t now; ! 400: queue_head_t *queue; ! 401: BOOL ccbTimedOut = NO; ! 402: AHACommandBuf *cmdBuf; ! 403: IOSCSIRequest *scsiReq; ! 404: ! 405: ddm_thr("timeoutOccurred\n", 1,2,3,4,5); ! 406: ! 407: IOGetTimestamp(&now); ! 408: ! 409: /* ! 410: * Scan the list of outstanding and pending commands, and time ! 411: * out any ones whose time is past. ! 412: */ ! 413: ! 414: for (queue = &outstandingQ; queue != &pendingQ; queue = &pendingQ) { ! 415: ! 416: ccb = (struct ccb *) queue_first(&outstandingQ); ! 417: while (!queue_end(&outstandingQ, (queue_entry_t) ccb)) { ! 418: ns_time_t expire; ! 419: ! 420: cmdBuf = ccb->cmdBuf; ! 421: scsiReq = cmdBuf->scsiReq; ! 422: expire = ccb->startTime + ! 423: 1000000000ULL * ! 424: (unsigned long long)scsiReq->timeoutLength; ! 425: if (now >= expire) { ! 426: /* ! 427: * Remove ccb from the oustanding queue and ! 428: * complete it. ! 429: */ ! 430: nextCcb = (struct ccb *) queue_next(&ccb->ccbQ); ! 431: queue_remove(&outstandingQ, ccb, struct ccb *, ccbQ); ! 432: if(queue == &outstandingQ) { ! 433: ASSERT(outstandingCount != 0); ! 434: outstandingCount--; ! 435: } ! 436: [self commandCompleted:ccb reason:CS_Timeout]; ! 437: ccb = nextCcb; ! 438: ccbTimedOut = YES; ! 439: } ! 440: else { ! 441: ccb = (struct ccb *) queue_next(&ccb->ccbQ); ! 442: } ! 443: } ! 444: } ! 445: ! 446: /* ! 447: * Reset bus. This also completes all I/Os in outstandingQ with ! 448: * status CS_Reset. ! 449: */ ! 450: if(ccbTimedOut) { ! 451: [self threadResetBus:NULL]; ! 452: } ! 453: ddm_thr("timeoutOccurred: DONE\n", 1,2,3,4,5); ! 454: } ! 455: ! 456: /* ! 457: * Process all commands in commandQ. If we run out of ccb's during this ! 458: * method, we abort, leaving commands enqueued; these will be handled after ! 459: * subqueuent interrupts. ! 460: * ! 461: * This is called either as a result of an IO_COMMAND_MSG message being ! 462: * received by the I/O thread, or upon completion of interrupt handling. In ! 463: * either case, it runs in the context of the I/O thread. ! 464: */ ! 465: - (void)commandRequestOccurred ! 466: { ! 467: AHACommandBuf *cmdBuf; ! 468: ! 469: ddm_thr("commandRequestOccurred: top\n", 1,2,3,4,5); ! 470: [commandLock lock]; ! 471: while(!queue_empty(&commandQ)) { ! 472: cmdBuf = (AHACommandBuf *) queue_first(&commandQ); ! 473: queue_remove(&commandQ, cmdBuf, AHACommandBuf *, link); ! 474: [commandLock unlock]; ! 475: switch(cmdBuf->op) { ! 476: case AO_Reset: ! 477: [self threadResetBus:cmdBuf]; ! 478: break; ! 479: ! 480: case AO_Abort: ! 481: /* ! 482: * First notify caller of completion, then ! 483: * self-terminate. ! 484: */ ! 485: [cmdBuf->cmdLock lock]; ! 486: [cmdBuf->cmdLock unlockWith:CMD_COMPLETE]; ! 487: IOExitThread(); ! 488: /* not reached */ ! 489: ! 490: case AO_Execute: ! 491: if([self threadExecuteRequest:cmdBuf]) { ! 492: /* ! 493: * No more CCBs available. Abort this entire ! 494: * method. Enqueue this request on the head ! 495: * of commandQ for future processing. ! 496: */ ! 497: [commandLock lock]; ! 498: queue_enter_first(&commandQ, cmdBuf, ! 499: AHACommandBuf *, link); ! 500: [commandLock unlock]; ! 501: ddm_thr("processCommandQ: no more ccbs; " ! 502: "cmdBuf 0x%x\n", cmdBuf, 2,3,4,5); ! 503: goto out; ! 504: ! 505: } ! 506: } ! 507: [commandLock lock]; ! 508: } ! 509: [commandLock unlock]; ! 510: out: ! 511: ddm_thr("commandRequestOccurred: DONE\n", 1,2,3,4,5); ! 512: return; ! 513: } ! 514: ! 515: ! 516: @end /* methods declared in AHAController.h */ ! 517: ! 518: @implementation AHAController(PrivateMethods) ! 519: ! 520: - (BOOL) probeAtPortBase:(IOEISAPortAddress) portBase ! 521: { ! 522: aha_inquiry_t inquiry; ! 523: ! 524: ddm_init("AHAController probeAtPortBase\n", 1,2,3,4,5); ! 525: ! 526: ioBase = portBase; ! 527: aha_reset_board(ioBase, ahaBoardId); ! 528: ! 529: /* ! 530: * Do an inquiry to find out the board id and other things that ! 531: * we won't check. ! 532: */ ! 533: if (!aha_probe_cmd(ioBase, AHA_CMD_DO_INQUIRY, 0, 0, ! 534: (unsigned char *)&inquiry, sizeof(inquiry), TRUE)) { ! 535: ddm_init(" ..inquiry command failed\n", 1,2,3,4,5); ! 536: return FALSE; ! 537: } ! 538: ! 539: ahaBoardId = inquiry.board_id; ! 540: ! 541: switch (ahaBoardId) { ! 542: ! 543: case AHA_1540_16HEAD: ! 544: case AHA_154xB: ! 545: case AHA_1540_64HEAD: ! 546: case AHA_1640: ! 547: case AHA_174xA: ! 548: case AHA_154xC: ! 549: break; ! 550: default: ! 551: if (ahaBoardId < AHA_154xC) { ! 552: ddm_init("..bogus board ID (0x%x)\n", ahaBoardId, ! 553: 2,3,4,5); ! 554: return FALSE; ! 555: } ! 556: } ! 557: ! 558: /* ! 559: * Attempt to read the configuration data from the board. ! 560: * If this succeeds, then we have successfully probed. ! 561: */ ! 562: if (!aha_probe_cmd(ioBase, AHA_CMD_GET_CONFIG, 0, 0, ! 563: (unsigned char *)&config, sizeof(config), TRUE)) { ! 564: ddm_init(" ..get config command failed\n", 1,2,3,4,5); ! 565: ! 566: return FALSE; ! 567: } ! 568: ! 569: /* ! 570: * Decode the values in the config struct. ! 571: */ ! 572: config.irq = ffs((unsigned int) config.irq) + 8; ! 573: config.dma_channel = ffs((unsigned int) config.dma_channel) - 1; ! 574: ! 575: IOLog("Adaptec154x at port 0x%x irq %d\n", ! 576: portBase, config.irq); ! 577: return TRUE; ! 578: } ! 579: ! 580: /* ! 581: * Pass one AHACommandBuf to the I/O thread; wait for completion. ! 582: * Normal completion status is in cmdBuf->status; a non-zero return ! 583: * from this function indicates a Mach IPC error. ! 584: * ! 585: * This method allocates and frees cmdBuf->cmdLock. ! 586: */ ! 587: - (IOReturn)executeCmdBuf : (AHACommandBuf *)cmdBuf ! 588: { ! 589: msg_header_t msg = AHAMessageTemplate; ! 590: kern_return_t krtn; ! 591: IOReturn rtn = IO_R_SUCCESS; ! 592: ! 593: cmdBuf->cmdLock = [[NXConditionLock alloc] initWith:CMD_PENDING]; ! 594: [commandLock lock]; ! 595: queue_enter(&commandQ, cmdBuf, AHACommandBuf *, link); ! 596: [commandLock unlock]; ! 597: ! 598: /* ! 599: * Create a Mach message and send it in order to wake up the ! 600: * I/O thread. ! 601: */ ! 602: msg.msg_remote_port = interruptPortKern; ! 603: krtn = msg_send_from_kernel(&msg, MSG_OPTION_NONE, 0); ! 604: if(krtn) { ! 605: IOLog("%s: msg_send_from_kernel() returned %d\n", ! 606: [self name], krtn); ! 607: rtn = IO_R_IPC_FAILURE; ! 608: goto out; ! 609: } ! 610: ! 611: /* ! 612: * Wait for I/O complete. ! 613: */ ! 614: ddm_exp("executeCmdBuf: waiting for completion on cmdBuf 0x%x\n", ! 615: cmdBuf, 2,3,4,5); ! 616: [cmdBuf->cmdLock lockWhen:CMD_COMPLETE]; ! 617: ddm_exp("executeCmdBuf: cmdBuf 0x%x complete\n", ! 618: cmdBuf, 2,3,4,5); ! 619: out: ! 620: [cmdBuf->cmdLock free]; ! 621: return rtn; ! 622: } ! 623: ! 624: @end /* AHAController(PrivateMethods) */ ! 625: ! 626: ! 627:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.