|
|
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: /** ! 26: * Copyright (c) 1994-1996 NeXT Software, Inc. All rights reserved. ! 27: * Copyright 1993-1995 by Apple Computer, Inc., all rights reserved. ! 28: * Copyright 1997-1998 Apple Computer Inc. All Rights Reserved. ! 29: * @author Martin Minow mailto:[email protected] ! 30: * @revision 1997.02.13 Initial conversion from Copland sources. ! 31: * ! 32: * Set tabs every 4 characters. ! 33: * ! 34: * Edit History ! 35: * 1997.02.25 MM Initial conversion from Copland sources. ! 36: */ ! 37: ! 38: #import <sys/systm.h> ! 39: #import <driverkit/generalFuncs.h> ! 40: #import <driverkit/kernelDriver.h> ! 41: #import <driverkit/align.h> ! 42: #import <driverkit/interruptMsg.h> ! 43: #import <driverkit/scsiTypes.h> ! 44: #import <driverkit/debugging.h> ! 45: #import <driverkit/IODirectDevice.h> ! 46: #import <driverkit/IOMemoryDescriptor.h> ! 47: #import <driverkit/IOSimpleMemoryDescriptor.h> ! 48: #import <driverkit/IOSCSIController.h> ! 49: #import <driverkit/IOPower.h> ! 50: #import <driverkit/return.h> ! 51: #import <bsd/dev/scsireg.h> ! 52: #import <mach/kern_return.h> ! 53: #import <mach/mach_interface.h> ! 54: #import <mach/message.h> ! 55: #import <machkit/NXLock.h> ! 56: #import <machdep/ppc/proc_reg.h> ! 57: #import <machdep/ppc/powermac.h> ! 58: #import <machdep/ppc/interrupts.h> ! 59: #import <machdep/ppc/dbdma.h> ! 60: #import <kernserv/prototypes.h> ! 61: #import <objc/objc.h> ! 62: ! 63: extern void flush_cache_v( vm_offset_t pa, unsigned length ); /* Should be available from kernel headers! */ ! 64: // extern void invalidate_cache_v( vm_offset_t pa, unsigned length ); ! 65: extern void kprintf( const char *, ... ); ! 66: extern kern_return_t msg_send_from_kernel( msg_header_t*, int, int ); ! 67: ! 68: ! 69: #import "MESH_DBDMA.h" ! 70: ! 71: #undef ASSERT ! 72: #define ASSERT(x) ! 73: ! 74: #if CustomMiniMon ! 75: extern globals g; /**** Use custom MiniMon's globals ****/ ! 76: extern UInt32 gMESH_DBDMA, gMESH_DBDMA_Phys; ! 77: #else ! 78: globals g; /**** Instantiate the globals ****/ ! 79: #endif /* CustomMiniMon */ ! 80: ! 81: ! 82: ! 83: /* Channel Program. Note that this script must match the offsets */ ! 84: /* specified in AppleMeshDefinitions.h. This script is copied into */ ! 85: /* the channel command area (with appropriate entries byte-swapped */ ! 86: /* so it ends up with the correct endian-ness). */ ! 87: /* Lines beginning with "slash, star, star, slash" are modified */ ! 88: /* by the driver before it starts the Channel Program. */ ! 89: ! 90: const DBDMADescriptor gDescriptorList[] = ! 91: { ! 92: /* 0x00 kcclProblem - Branch here for anomalies */ ! 93: ! 94: { MESH_REG( kMeshInterruptMask, kMeshIntrMask ) }, // Enable MESH interrupt ! 95: { STOP( kcclStageCCLx ) }, // anomaly ! 96: ! 97: /* 0x20 through 0x60 - Data for information phases: */ ! 98: ! 99: { RESERVE }, // kcclCMDOdata - CDB ( 6,10,12,16 bytes) ! 100: { RESERVE }, // kcclMSGOdata - MSGO data (last byte @3F) ! 101: { RESERVE }, // kcclMSGIdata - MSGI data & STATUS ! 102: { RESERVE }, // kcclSenseCDB - CDB for (auto) Sense ! 103: { RESERVE }, // kcclBatchSize, kcclStageLabel ! 104: ! 105: /* 0x70 - kcclSense - AutoSense input: */ ! 106: ! 107: { MESH_REG( kMeshTransferCount1, 0x00 ) }, // set MESH xfer count to 255 ! 108: { MESH_REG( kMeshTransferCount0, kMaxAutosenseByteCount & 0xFF )}, ! 109: { MESH_REG( kMeshSequence, kMeshDataInCmd | kMeshSeqDMA )},// Data-In to Seq register ! 110: { SENSE( kMaxAutosenseByteCount ) }, // Sense INPUT ! 111: { BRANCH( kcclGetStatus ) }, // do finish sequence ! 112: ! 113: /* 0xC0 - kcclPrototype - Prototype MESH 4-command Transfer sequence: */ ! 114: ! 115: { MOVE_4( kcclBatchSize, 0, kRelAddressCP ) }, // MESH batch size ! 116: { MESH_REG( kMeshTransferCount1, 0 ) }, // Set high order Transfer Count ! 117: { MESH_REG( kMeshTransferCount0, 0 ) }, // Set low order Transfer Count ! 118: { MESH_REG( kMeshSequence, kMeshDataInCmd | kMeshSeqDMA )}, // Assume Data-In ! 119: ! 120: { RESERVE }, // spare ! 121: { RESERVE }, // spare ! 122: ! 123: /* 0x120 kcclStart - Arbitrate (START CHANNEL PROGRAM HERE): */ ! 124: /* 0x140 kcclBrProblem */ ! 125: ! 126: { STAGE( kcclStageArb ) }, ! 127: { MESH_REG( kMeshSequence, kMeshArbitrateCmd ) }, // issue Arbitrate ! 128: { BR_IF_PROBLEM }, // branch if exception or error ! 129: ! 130: /* 0x150 - Select with Attention: */ ! 131: ! 132: { STAGE( kcclStageSelA ) }, ! 133: { CLEAR_CMD_DONE }, ! 134: { MESH_REG( kMeshSequence, kMeshSelectCmd | kMeshSeqAtn ) }, // select with attention ! 135: { BR_IF_PROBLEM }, // branch if failed ! 136: ! 137: /* 0x190 kcclMsgoStage- Message-Out: */ ! 138: ! 139: { STAGE( kcclStageMsgO ) }, ! 140: { CLEAR_CMD_DONE }, ! 141: ! 142: /* 0x1B0 kcclMsgoBranch - modify this BRANCH to fall through for multibyte messages: */ ! 143: ! 144: /**/{ BRANCH( kcclLastMsgo ) }, // kcclMsgoBranch - go do only byte of Msg ! 145: ! 146: /* 0x1C0 - do all but last byte of multibyte message: */ ! 147: ! 148: { MESH_REG( kMeshTransferCount1, 0x00 ) }, // count does include last byte ! 149: /**/{ MESH_REG( kMeshTransferCount0, 0xFF ) }, // kcclMsgoMTC - modify MESH xfer count here ! 150: { MESH_REG( kMeshSequence, kMeshMessageOutCmd | kMeshSeqAtn | kMeshSeqDMA ) }, // DMA MsgO with ATN ! 151: /**/{ MSGO( kcclMSGOdata, 255 ) }, // kcclMsgoDTC - output all but last byte ! 152: { CLEAR_CMD_DONE }, ! 153: ! 154: /* 0x210 kcclLastMsgo - wait for REQ signal before dropping ATN: */ ! 155: ! 156: { MESH_REG( kMeshInterruptMask, 0 ) }, // inhibit MESH interrupt ! 157: { MESH_REG_WAIT( kMeshSequence, kMeshStatusCmd | kMeshSeqAtn ) }, // gen PhaseMM ! 158: { CLEAR_INT_REG }, // clear PhaseMM & CmdDone ! 159: { MESH_REG( kMeshInterruptMask, kMeshIntrException | kMeshIntrError ) }, // re-enable ERR/EXC Ints ! 160: ! 161: /* 0x250 - put out the last or only byte of Message-Out phase: */ ! 162: ! 163: { MESH_REG( kMeshTransferCount1, 0x00 ) }, ! 164: { MESH_REG( kMeshTransferCount0, 0x01 ) }, ! 165: { MESH_REG( kMeshSequence, kMeshMessageOutCmd | kMeshSeqDMA ) },// no more ATN ! 166: { MSGO( kcclMSGOLast, 1 ) }, ! 167: ! 168: /* 0x290 kcclCmdoStage - Command Out: */ ! 169: ! 170: { STAGE( kcclStageCmdO ) }, ! 171: { CLEAR_CMD_DONE }, ! 172: { MESH_REG( kMeshTransferCount1, 0x00 ) }, ! 173: /**/{ MESH_REG( kMeshTransferCount0, 0x06 ) }, // kcclCmdoMTC - Set MESH xfer count to 6 ! 174: { MESH_REG( kMeshSequence, kMeshCommandCmd | kMeshSeqDMA )}, // Command phase with DMA on ! 175: /**/{ CMDO( 6 ) }, // kcclCmdoDTC - output the CDB ! 176: ! 177: /* 0x2F0 - DATA XFER - branch to the built CCL @ 0x05D0: */ ! 178: /* also, kcclReselect - reselect code enters here: */ ! 179: ! 180: { CLEAR_CMD_DONE }, ! 181: { STAGE( kcclStageXfer ) }, ! 182: { BRANCH( kcclDataXfer ) }, // go do Xfer CCL ! 183: ! 184: /* 0x320 kcclOverrun - dump excess data in the bit bucket: */ ! 185: /* Exc and Err are still disabled. */ ! 186: ! 187: { STAGE( kcclStageBucket ) }, ! 188: { MESH_REG( kMeshTransferCount1, 0x00 ) }, // set MESH Transfer Count to max ! 189: { MESH_REG( kMeshTransferCount0, 0x00 ) }, ! 190: { CLR_PHASEMM }, ! 191: { MESH_REG( kMeshInterruptMask, kMeshIntrException | kMeshIntrError ) }, // re-enable ERR/EXC Ints ! 192: /**/{ MESH_REG( kMeshSequence, kMeshDataInCmd | kMeshSeqDMA ) }, // set Seq Reg ! 193: /**/{ BUCKET }, // OUT/INPUT_LAST the bits ! 194: { BR_NO_PROBLEM( kcclOverrunDBDMA ) }, // loop til PhaseMismatch ! 195: { BR_IF_PROBLEM }, // take the interrupt now ! 196: ! 197: /* 0x3B0 kcclSyncCleanUp - clean up after Sync xfer: */ ! 198: { CLEAR_INT_REG }, // clear PhaseMM & CmdDone (& Err?) ! 199: { MESH_REG( kMeshInterruptMask, kMeshIntrException | kMeshIntrError ) }, // re-enable ERR/EXC Ints ! 200: ! 201: /* 0x3D0 kcclGetStatus - setup CCL for status, command complete and bus free: */ ! 202: ! 203: { STAGE( kcclStageStat ) }, ! 204: { MESH_REG( kMeshTransferCount1, 0x00 ) }, ! 205: { MESH_REG( kMeshTransferCount0, 0x01 ) }, // set MESH xfer count to 1 ! 206: { MESH_REG( kMeshSequence, kMeshStatusCmd | kMeshSeqDMA )},// Status-in phase with DMA on ! 207: { STATUS_IN }, // input the status byte ! 208: ! 209: /* 0x420 - Message In: */ ! 210: ! 211: { STAGE( kcclStageMsgI ) }, ! 212: { CLEAR_CMD_DONE }, ! 213: { MESH_REG( kMeshTransferCount1, 0x00 ) }, ! 214: { MESH_REG( kMeshTransferCount0, 0x01 ) }, // set MESH xfer count to 1 ! 215: { MESH_REG( kMeshSequence, kMeshMessageInCmd | kMeshSeqDMA )}, // Status-in phase with DMA on ! 216: { MSGI( 1 ) }, // get the Message-In byte ! 217: ! 218: /* 0x480 - Bus Free: */ ! 219: ! 220: { STAGE( kcclStageFree ) }, ! 221: { CLEAR_CMD_DONE }, ! 222: { MESH_REG( kMeshSequence, kMeshEnableReselect ) }, // Enable Reselect ! 223: { MESH_REG( kMeshSequence, kMeshBusFreeCmd ) }, // Bus Free phase ! 224: { BR_IF_PROBLEM }, // branch if failed ! 225: ! 226: /* 0x4D0 kcclMESHintr - Good completion: */ ! 227: ! 228: { STAGE( kcclStageGood ) }, ! 229: { MESH_REG( kMeshInterruptMask, kMeshIntrMask ) }, // latch MESH interrupt ! 230: { STOP( kcclStageStop ) }, // Stop ! 231: ! 232: /* The rest of the Channel Program area is used for autosense */ ! 233: /* and data transfer channel commands: */ ! 234: /* kcclSenseBuffer Autosense area */ ! 235: /* kcclDataXfer Start of data transfer channel commands */ ! 236: /* kcclSenseResult Autosense result stored here */ ! 237: ! 238: }; /* end gDescriptorList structure */ ! 239: ! 240: const UInt32 gDescriptorListSize = sizeof( gDescriptorList ); ! 241: ! 242: enum /* values for g.intLevel: */ ! 243: { ! 244: kLevelISR = 0x80, /* In Interrupt Service Routine */ ! 245: kLevelLocked = 0x40, /* MESH interrupts locked out */ ! 246: kLevelSIH = 0x20, /* In Secondary Interrupt Handler */ ! 247: kLevelLatched = 0x10 /* Interrupt latched */ ! 248: }; ! 249: ! 250: ! 251: // IONamedValue scsiChipRegisterStrings[] = { { 0, NULL, } }; ! 252: ! 253: static int getConfigParam( id configTable, const char *paramName ); ! 254: static unsigned int GetSCSICommandLength( const cdb_t *cdbPtr, unsigned int defaultLength ); ! 255: ! 256: ! 257: /* MAX_DMA_XFER is set so that we don't have to worry about the */ ! 258: /* ambiguous "zero" value in the MESH and DBDMA transfer registers */ ! 259: /* that can mean either 65536 bytes or zero bytes. */ ! 260: ! 261: #define MAX_DMA_XFER 0x0000F000 ! 262: ! 263: #define ONE_SECOND 1 /* for IOScheduleFunc and serviceTimeoutInterrupt */ ! 264: ! 265: /* Template for command message sent to the IO thread: */ ! 266: ! 267: static const msg_header_t cmdMessageTemplate = ! 268: { ! 269: 0, /* msg_unused */ ! 270: 1, /* msg_simple */ ! 271: sizeof( msg_header_t ), /* msg_size */ ! 272: MSG_TYPE_NORMAL, /* msg_type */ ! 273: PORT_NULL, /* msg_local_port */ ! 274: PORT_NULL, /* msg_remote_port - filled in */ ! 275: IO_COMMAND_MSG /* msg_id */ ! 276: }; ! 277: ! 278: /* Template for timeout message. */ ! 279: ! 280: static const msg_header_t gTimeoutMsgTemplate = ! 281: { ! 282: 0, /* msg_unused */ ! 283: 1, /* msg_simple */ ! 284: sizeof( msg_header_t ), /* msg_size */ ! 285: MSG_TYPE_NORMAL, /* msg_type */ ! 286: PORT_NULL, /* msg_local_port */ ! 287: PORT_NULL, /* msg_remote_port - filled in */ ! 288: IO_TIMEOUT_MSG /* msg_id */ ! 289: }; ! 290: ! 291: static port_t gKernelInterruptPort; /* for int/timeout msgs */ ! 292: ! 293: static void serviceTimeoutInterrupt( void *arg ); ! 294: static AppleMesh_SCSI *gInstance; ! 295: ! 296: ! 297: #if USE_ELG && !CustomMiniMon ! 298: void AllocateEventLog( UInt32 size ) ! 299: { ! 300: if ( !g.evLogBuf ) g.evLogBuf = (UInt8*)kalloc( size ); ! 301: if ( !g.evLogBuf ) ! 302: kprintf( "probe - MESH evLog allocation failed " ); ! 303: ! 304: g.evLogFlag = 0; /* assume insufficient memory */ ! 305: g.evLogBufp = g.evLogBuf; ! 306: ! 307: if ( g.evLogBuf ) ! 308: { ! 309: g.evLogBufe = g.evLogBufp + kEvLogSize - 0x20; // ??? overran buffer? ! 310: g.evLogFlag = 0xFEEDBEEF; ! 311: // g.evLogFlag = 0x0333; ! 312: } ! 313: return; ! 314: }/* end AllocateEventLog */ ! 315: ! 316: ! 317: void EvLog( UInt32 a, UInt32 b, UInt32 ascii, char* str ) ! 318: { ! 319: register UInt32 *lp; /* Long pointer */ ! 320: ns_time_t time; ! 321: ! 322: if ( g.evLogFlag == 0 ) ! 323: return; ! 324: ! 325: IOGetTimestamp( &time ); ! 326: ! 327: lp = (UInt32*)g.evLogBufp; ! 328: g.evLogBufp += 0x10; ! 329: ! 330: if ( g.evLogBufp >= g.evLogBufe ) /* handle buffer wrap around if any */ ! 331: { g.evLogBufp = g.evLogBuf; ! 332: if ( g.evLogFlag != 0xFEEDBEEF ) // make 0xFEEDBEEF a symbolic ??? ! 333: g.evLogFlag = 0; /* stop tracing if wrap undesired */ ! 334: } ! 335: ! 336: /* compose interrupt level with 3 byte time stamp: */ ! 337: ! 338: *lp++ = (g.intLevel << 24) | ((time >> 10) & 0x003FFFFF); // ~ 1 microsec resolution ! 339: *lp++ = a; ! 340: *lp++ = b; ! 341: *lp = ascii; ! 342: ! 343: if( g.evLogFlag == 'step' ) ! 344: kprintf( str ); ! 345: ! 346: return; ! 347: }/* end EvLog */ ! 348: ! 349: ! 350: void Pause( UInt32 a, UInt32 b, UInt32 ascii, char* str ) ! 351: { ! 352: char work [256 ]; ! 353: char name[] = "AppleMeshSCSI:"; ! 354: char *bp = work; ! 355: UInt8 x; ! 356: int i; ! 357: ! 358: ! 359: EvLog( a, b, ascii, str ); ! 360: EvLog( '****', '** P', 'ause', "*** Pause" ); ! 361: ! 362: bcopy( name, bp, sizeof( name ) ); ! 363: bp += sizeof( name ) - 1; ! 364: ! 365: *bp++ = '{'; // prepend p1 in hex: ! 366: for ( i = 7; i >= 0; --i ) ! 367: { ! 368: x = a & 0x0F; ! 369: if ( x < 10 ) ! 370: x += '0'; ! 371: else x += 'A' - 10; ! 372: bp[ i ] = x; ! 373: a >>= 4; ! 374: } ! 375: bp += 8; ! 376: ! 377: *bp++ = ' '; // prepend p2 in hex: ! 378: ! 379: for ( i = 7; i >= 0; --i ) ! 380: { ! 381: x = b & 0x0F; ! 382: if ( x < 10 ) ! 383: x += '0'; ! 384: else x += 'A' - 10; ! 385: bp[ i ] = x; ! 386: b >>= 4; ! 387: } ! 388: bp += 8; ! 389: *bp++ = '}'; ! 390: ! 391: *bp++ = ' '; ! 392: ! 393: for ( i = sizeof( work ) - (int)(bp - work); i && (*bp++ = *str++); --i ) ; ! 394: ! 395: kprintf( work ); ! 396: // call_kdp(); // ??? use kdp=3 in boot parameters ! 397: return; ! 398: }/* end Pause */ ! 399: #endif /* not CustomMiniMon */ ! 400: ! 401: ! 402: /* serviceTimeoutInterrupt - Handle timeouts. */ ! 403: /* This function is invoked in kernel context on a DriverKit thread. */ ! 404: /* Just send a timeout message to the IO thread to wake it up. */ ! 405: ! 406: static void serviceTimeoutInterrupt( void *arg ) ! 407: { ! 408: msg_header_t msg = gTimeoutMsgTemplate; ! 409: ! 410: ! 411: ELG( 0, 0, 'Tick', "serviceTimeoutInterrupt\n" ); ! 412: ! 413: /* roll me another one: */ ! 414: IOScheduleFunc( serviceTimeoutInterrupt, (void*)0x333, ONE_SECOND ); ! 415: ! 416: /* Tell the IO thread: */ ! 417: msg.msg_remote_port = gKernelInterruptPort; ! 418: msg_send_from_kernel( &msg, MSG_OPTION_NONE, 0 ); ! 419: return; ! 420: }/* end serviceTimeoutInterrupt */ ! 421: ! 422: ! 423: /* Used in timeoutOccurred to determine if specified cmdBuf has timed out. */ ! 424: /* Returns YES if timeout, else NO. */ ! 425: ! 426: static Boolean isCmdTimedOut( CommandBuffer *cmdBuf ) ! 427: { ! 428: IOSCSIRequest *scsiReq = cmdBuf->scsiReq; ! 429: ns_time_t now, expire; ! 430: Boolean result; ! 431: ! 432: ! 433: IOGetTimestamp( &now ); ! 434: expire = cmdBuf->startTime + ! 435: (1000000000ULL * (unsigned long long)scsiReq->timeoutLength); ! 436: result = (now > expire); ! 437: if ( result ) ELG( cmdBuf, cmdBuf->scsiReq->timeoutLength, 'Tim-', "isCmdTimedOut" ); ! 438: return result; ! 439: }/* end isCmdTimedOut */ ! 440: ! 441: ! 442: /* Implement the public methods for the MESH controller. */ ! 443: ! 444: @implementation AppleMesh_SCSI ! 445: ! 446: /* Create and initialize one instance of AppleMesh_SCSI. */ ! 447: /* The work is done by architecture- and chip-specific modules. */ ! 448: ! 449: + (Boolean) probe : deviceDescription ! 450: { ! 451: Boolean result; ! 452: ! 453: ! 454: gInstance = [ self alloc ]; /* Instantiate yourself */ ! 455: g.intLevel = 0; ! 456: ! 457: MakeTimestampRecord( 512 ); /* conditionally compiled */ ! 458: ! 459: #if USE_ELG ! 460: AllocateEventLog( kEvLogSize ); ! 461: ELG( g.evLogBufp, &g.evLogFlag, 'Prob', "probe - event logging set up.\n" ); ! 462: #endif /* USE_ELG */ ! 463: ! 464: /* Perform device-specific initialization. */ ! 465: /* Free the instance on failure. */ ! 466: ! 467: if ( [ gInstance InitializeHardware : deviceDescription ] == nil ) ! 468: result = NO; ! 469: else result = YES; ! 470: ! 471: return result; ! 472: }/* end probe */ ! 473: ! 474: ! 475: /* The driver is shutting down. Kill everything worth killing. */ ! 476: ! 477: - free ! 478: { ! 479: CommandBuffer cmdBuf; ! 480: ! 481: ! 482: /* First kill the IO thread if running. */ ! 483: ! 484: if ( gFlagIOThreadRunning ) ! 485: { ! 486: cmdBuf.op = kCommandAbortRequest; ! 487: cmdBuf.scsiReq = NULL; ! 488: [ self executeCmdBuf : &cmdBuf ]; ! 489: } ! 490: ! 491: if ( incomingCmdLock ) ! 492: [ incomingCmdLock free ]; ! 493: ! 494: dbdma_stop( DBDMA_MESH_SCSI ); ! 495: ! 496: if ( cclLogAddr ) ! 497: { ! 498: IOFree( cclLogAddr, cclLogAddrSize ); ! 499: cclLogAddr = NULL; ! 500: } ! 501: /* ??? Unmap physical address mapping to registers. */ ! 502: ! 503: return [ super free ]; ! 504: }/* end free */ ! 505: ! 506: ! 507: /* Return required DMA alignment for current architecture. */ ! 508: /* We specify 8-byte alignment to avoid a bug in the Grand Central chip:*/ ! 509: /* if (Reading */ ! 510: /* && (kdbdmaSetFlush || kdbdmaClrRun) */ ! 511: /* && no bytes transferred yet */ ! 512: /* && buffer not 8-byte aligned) */ ! 513: /* { */ ! 514: /* THEN memory in front of buffer will be trashed. */ ! 515: /* } */ ! 516: ! 517: - (void) getDMAAlignment : (IODMAAlignment*)alignment ! 518: { ! 519: alignment->readStart = DBDMA_ReadStartAlignment; ! 520: alignment->writeStart = DBDMA_WriteStartAlignment; ! 521: alignment->readLength = 0; ! 522: alignment->writeLength = 0; ! 523: return; ! 524: }/* end getDMAAlignment */ ! 525: ! 526: ! 527: /* Statistics support. */ ! 528: ! 529: - (unsigned int) numQueueSamples ! 530: { ! 531: return gTotalCommands; ! 532: }/* end numQueueSamples */ ! 533: ! 534: ! 535: - (unsigned int) sumQueueLengths ! 536: { ! 537: return gQueueLenTotal; ! 538: }/* end sumQueueLengths */ ! 539: ! 540: ! 541: - (unsigned int) maxQueueLength ! 542: { ! 543: return gMaxQueueLen; ! 544: }/* end maxQueueLength */ ! 545: ! 546: ! 547: - (void) resetStatistics ! 548: { ! 549: gMaxQueueLen = 0; ! 550: gQueueLenTotal = 0; ! 551: gMaxQueueLen = 0; ! 552: return; ! 553: }/* resetStatistics */ ! 554: ! 555: ! 556: /* Do a SCSI command, as specified by an IOSCSIRequest. */ ! 557: /* All the work is done by the IO thread. */ ! 558: /* @param scsiReq The request to execute */ ! 559: /* @param buffer The data buffer to transfer to/from, if any */ ! 560: /* @param client The data buffer owner task (for VM munging) */ ! 561: /* */ ! 562: /* This method is called from IOSCSIDevice */ ! 563: - (sc_status_t) executeRequest : (IOSCSIRequest*)scsiReq ! 564: buffer : (void*)buffer ! 565: client : (vm_task_t)client ! 566: { ! 567: IOMemoryDescriptor *mem = NULL; ! 568: sc_status_t scsiStatus = SR_IOST_GOOD; /* Fool compiler */ ! 569: ! 570: ! 571: ELG( scsiReq->lun<<16 | scsiReq->target, scsiReq, 'sReq', "executeRequest (buffer)" ); ! 572: ELG( buffer, scsiReq->maxTransfer, 'Buff', "executeRequest" ); ! 573: ! 574: /* Create a simple IO memory descriptor for this client, */ ! 575: /* then toss it to the common method. */ ! 576: ! 577: if ( buffer ) ! 578: { ! 579: if ( scsiReq->read && ((UInt32)buffer & (DBDMA_ReadStartAlignment - 1)) ) ! 580: { ! 581: ELG( scsiReq->maxTransfer, buffer, 'Aln-', "executeRequest/simple buffer - unaligned read buffer." ); ! 582: return SR_IOST_ALIGN; ! 583: } ! 584: mem = [ [ IOSimpleMemoryDescriptor alloc ] ! 585: initWithAddress : (void*)buffer ! 586: length : scsiReq->maxTransfer ]; ! 587: [ mem setClient : client ]; ! 588: } ! 589: ! 590: scsiStatus = [ self executeRequest : scsiReq ioMemoryDescriptor : mem ]; ! 591: ! 592: if ( mem ) ! 593: [ mem release ]; ! 594: ! 595: return scsiStatus; ! 596: }/* end executeRequest buffer */ ! 597: ! 598: ! 599: /* Execute a SCSI request using an IOMemoryDescriptor. */ ! 600: /* This allows callers to provide (kernel-resident) logical */ ! 601: /* scatter-gather lists. For compatibility with existing */ ! 602: /* implementations, the low-level SCSI device driver must */ ! 603: /* first ensure that executeRequestWithIOMemoryDescriptor */ ! 604: /* is supported by executing: */ ! 605: /* [ controller respondsToSelector : executeRequestWithIOMemoryDescriptor ] */ ! 606: ! 607: - (sc_status_t) executeRequest : (IOSCSIRequest*)scsiReq ! 608: ioMemoryDescriptor : (IOMemoryDescriptor*)ioMemoryDescriptor ! 609: { ! 610: CommandBuffer commandBuffer; ! 611: ! 612: ! 613: ELG( scsiReq->lun<<16 | scsiReq->target, scsiReq, 'dReq', "executeRequest (IOMemoryDescriptor)" ); ! 614: ELG( 0, ioMemoryDescriptor, 'iomd', "executeRequest" ); ! 615: ! 616: scsiReq->driverStatus = SR_IOST_INVALID; /* "In progress" */ ! 617: if ( ioMemoryDescriptor ) ! 618: { ! 619: [ ioMemoryDescriptor setMaxSegmentCount : MAX_DMA_XFER ]; ! 620: [ ioMemoryDescriptor state : &commandBuffer.savedDataState ]; ! 621: } ! 622: bzero( &commandBuffer, sizeof( CommandBuffer ) ); ! 623: commandBuffer.op = kCommandExecute; ! 624: commandBuffer.scsiReq = scsiReq; ! 625: commandBuffer.mem = ioMemoryDescriptor; ! 626: ! 627: [ self executeCmdBuf : &commandBuffer ]; ! 628: ! 629: #if TIMESTAMP_AT_IOCOMPLETE ! 630: [ self logTimestamp : "IO complete" ]; /* After RESULT macro */ ! 631: #endif ! 632: ! 633: return commandBuffer.scsiReq->driverStatus; ! 634: }/* end executeRequest IOMemoryDescriptor */ ! 635: ! 636: ! 637: /* Reset the SCSI bus. All the work is done by the IO thread. */ ! 638: ! 639: - (sc_status_t) resetSCSIBus ! 640: { ! 641: CommandBuffer commandBuffer; ! 642: ! 643: ! 644: commandBuffer.op = kCommandResetBus; ! 645: commandBuffer.scsiReq = NULL; ! 646: ! 647: [ self executeCmdBuf : &commandBuffer ]; ! 648: return SR_IOST_GOOD; /* can not fail */ ! 649: }/* end resetSCSIBus */ ! 650: ! 651: ! 652: /* The following 6 methods, */ ! 653: /* interruptOccurred, interruptOccurredAt, otherOccurred, */ ! 654: /* receiveMsg, timeoutOccurred, commandRequestOccurred, */ ! 655: /* are all called from the IO thread in IODirectDevice. */ ! 656: ! 657: /* Called from the IO thread when it receives an interrupt message. */ ! 658: /* Currently all work is done by chip-specific module; maybe we should */ ! 659: /* put this method there.... */ ! 660: ! 661: - (void) interruptOccurred ! 662: { ! 663: g.intLevel |= kLevelISR; /* set ISR flag */ ! 664: g.intLevel &= ~kLevelLatched; /* clear latched */ ! 665: ELG( dbdmaAddr->d_status, dbdmaAddr->d_cmdptrlo, 'Int+', "interruptOccurred." ); ! 666: // ELG( *(UInt32*)0xF3000024, *(UInt32*)0xF300002C, 'Int ', "interruptOccurred." ); ! 667: ! 668: [ self DoHardwareInterrupt ]; /**** HANDLE THE INTERRUPT ****/ ! 669: ! 670: // ELG( gActiveCommand, *(UInt32*)0xF300002C, 'Intx', "interruptOccurred." ); ! 671: ! 672: g.intLevel &= ~kLevelISR; /* clear ISR flag */ ! 673: return; ! 674: }/* end interruptOccurred */ ! 675: ! 676: ! 677: /* These three should not occur; they are here as error traps. */ ! 678: /* All three are called out from the IO thread upon receipt of */ ! 679: /* messages which it should not be seeing. */ ! 680: ! 681: - (void) interruptOccurredAt : (int)localNum ! 682: { ! 683: PAUSE( 0, localNum, 'int@', "interruptOccurredAt.\n" ); ! 684: return; ! 685: }/* end interruptOccurredAt */ ! 686: ! 687: ! 688: - (void) otherOccurred : (int)id ! 689: { ! 690: PAUSE( 0, id, 'Othr', "otherOccurred.\n" ); ! 691: return; ! 692: }/* end otherOccurred */ ! 693: ! 694: ! 695: - (void) receiveMsg ! 696: { ! 697: PAUSE( 0, 0, 'RcvM', "receiveMsg.\n" ); ! 698: ! 699: /* We have to let IODirectDevice take care of this (i.e., */ ! 700: /* dequeue the bogus message). */ ! 701: ! 702: [ super receiveMsg ]; ! 703: return; ! 704: }/* end receiveMsg */ ! 705: ! 706: ! 707: /* This method is invoked by DriverKit when it receives a message */ ! 708: /* generated by the function serviceTimeoutInterrupt() which was called */ ! 709: /* by the kernel on some DriverKit thread. */ ! 710: ! 711: - (void) timeoutOccurred ! 712: { ! 713: CommandBuffer *cmdBuf = gActiveCommand; ! 714: CommandBuffer *nextCmdBuf; ! 715: ! 716: ! 717: if ( g.intLevel & kLevelLatched ) ! 718: { ! 719: ELG( cmdBuf, 0, 'TocL', "timeoutOccurred - interrupt already latched; do nothing" ); ! 720: return; ! 721: } ! 722: ! 723: g.intLevel |= kLevelISR; /* set IOthread-running-flag */ ! 724: ELG( CCLWord( kcclStageLabel ), dbdmaAddr->d_cmdptrlo, 'Tock', "timeoutOccurred - tick.\n" ); ! 725: [ self GetHBARegsAndClear : FALSE ]; /* get the MESH registers */ ! 726: ! 727: /* If gActiveCommand timed out: */ ! 728: ! 729: if ( cmdBuf ) ! 730: { if ( isCmdTimedOut( cmdBuf ) ! 731: && ((CCLWord( kcclStageLabel ) != kcclStageFree) ! 732: || (CCLWord( kcclStageLabel ) != kcclStageGood) ) ) ! 733: { ! 734: dbdma_flush( DBDMA_MESH_SCSI ); /* DBDMA may be hung in */ ! 735: dbdma_stop( DBDMA_MESH_SCSI ); /* middle of transfer. */ ! 736: // invalidate_cache_v( (vm_offset_t)cclLogAddr, cclLogAddrSize ); ! 737: ! 738: cmdBuf->scsiReq->driverStatus = SR_IOST_IOTO; ! 739: [ self ioComplete : cmdBuf ]; ! 740: ! 741: [ self AbortActiveCommand ]; ! 742: ! 743: g.intLevel &= ~kLevelISR; /* clear IOthread-running-flag */ ! 744: return; ! 745: } ! 746: } ! 747: else /* Move any/all timed-out disconnected commands to abortCmdQ: */ ! 748: { ! 749: cmdBuf = (CommandBuffer*)queue_first( &disconnectedCmdQ ); ! 750: while ( !queue_end( &disconnectedCmdQ, (queue_entry_t)cmdBuf ) ) ! 751: { ! 752: nextCmdBuf = (CommandBuffer*)queue_next( &cmdBuf->link ); ! 753: if ( isCmdTimedOut( cmdBuf ) ) ! 754: { /* Move cmdBuf from disconnectQ to abortQ: */ ! 755: queue_remove( &disconnectedCmdQ, cmdBuf, CommandBuffer*, link ); ! 756: queue_enter( &abortCmdQ, cmdBuf, CommandBuffer*, link ); ! 757: cmdBuf->scsiReq->driverStatus = SR_IOST_IOTO; ! 758: } ! 759: cmdBuf = nextCmdBuf; ! 760: }/* end WHILE scanning commands in the disconnected queue */ ! 761: [ self AbortDisconnectedCommand ]; ! 762: } ! 763: g.intLevel &= ~kLevelISR; /* clear IOthread-running-flag */ ! 764: return; ! 765: }/* end timeoutOccurred */ ! 766: ! 767: ! 768: /* Process all commands in incomingCmdQ. At most one of these */ ! 769: /* will become gActiveCommand. The remainder of kCommandExecute commands*/ ! 770: /* go to pendingCmdQ. Other types of commands (such as bus reset) */ ! 771: /* are executed immediately. */ ! 772: /* This method is called from IODirectDevice. */ ! 773: /* */ ! 774: /* Note that we don't have a concept of frozen queue. */ ! 775: ! 776: - (void) commandRequestOccurred ! 777: { ! 778: CommandBuffer *cmdBuf, *pendCmd; ! 779: ! 780: ! 781: [ incomingCmdLock lock ]; ! 782: ! 783: while ( !queue_empty( &incomingCmdQ ) ) ! 784: { ! 785: cmdBuf = (CommandBuffer*)queue_first( &incomingCmdQ ); ! 786: queue_remove( &incomingCmdQ, cmdBuf, CommandBuffer*, link ); ! 787: [ incomingCmdLock unlock ]; ! 788: ELG( gActiveCommand, cmdBuf, 'CRO+', "commandRequestOccurred" ); ! 789: ! 790: switch ( cmdBuf->op ) ! 791: { ! 792: case kCommandResetBus: ! 793: /* Note all active and disconnected commands will be terminated.*/ ! 794: [ self threadResetBus : "Reset Command Received" ]; ! 795: [ self ioComplete : cmdBuf ]; ! 796: break; ! 797: ! 798: case kCommandAbortRequest: ! 799: /* 1. Abort all active, pending, and disconnected commands. */ ! 800: /* 2. Notify caller of completion. */ ! 801: /* 3. Self-terminate. */ ! 802: ! 803: [ self abortAllCommands : SR_IOST_INT ]; ! 804: pendCmd = (CommandBuffer*)queue_first( &pendingCmdQ ); ! 805: ! 806: while ( !queue_end( &pendingCmdQ, (queue_entry_t)pendCmd ) ) ! 807: { ! 808: pendCmd->scsiReq->driverStatus = SR_IOST_INT; ! 809: [ self ioComplete : pendCmd ]; ! 810: pendCmd = (CommandBuffer*)queue_next( &pendCmd->link ); ! 811: } ! 812: ! 813: [ cmdBuf->cmdLock lock ]; ! 814: [ cmdBuf->cmdLock unlockWith : CMD_COMPLETE ]; ! 815: IOExitThread(); ! 816: /***** not reached *****/ ! 817: ! 818: case kCommandExecute: ! 819: [ self threadExecuteRequest : cmdBuf ]; ! 820: break; ! 821: }/* end SWITCH */ ! 822: ! 823: [ incomingCmdLock lock ]; ! 824: }/* end WHILE queue not empty */ ! 825: ! 826: [ incomingCmdLock unlock ]; ! 827: return; ! 828: }/* end commandRequestOccurred */ ! 829: ! 830: ! 831: /* Power management methods. All we care about is power off, when */ ! 832: /* we must reset the SCSI bus due to the Compaq BIOS's lack of a */ ! 833: /* SCSI reset, which causes a hang if we have set up targets for */ ! 834: /* sync data transfer mode. */ ! 835: ! 836: - (IOReturn) getPowerState : (PMPowerState*)state_p ! 837: { ! 838: return IO_R_UNSUPPORTED; ! 839: }/* end getPowerState */ ! 840: ! 841: ! 842: - (IOReturn) setPowerState : (PMPowerState) state ! 843: { ! 844: ELG( 0, state, 'sPwr', "setPowerState.\n" ); ! 845: ! 846: if ( state == PM_OFF ) ! 847: { ! 848: // [ self scsiReset ]; ! 849: // ** ** ** TBS: [ self powerDown ]; ! 850: return IO_R_SUCCESS; ! 851: } ! 852: return IO_R_UNSUPPORTED; ! 853: }/* end setPowerState */ ! 854: ! 855: ! 856: - (IOReturn) getPowerManagement : (PMPowerManagementState*)state_p ! 857: { ! 858: return IO_R_UNSUPPORTED; ! 859: }/* end getPowerManagement */ ! 860: ! 861: ! 862: - (IOReturn) setPowerManagement : (PMPowerManagementState)state ! 863: { ! 864: return IO_R_UNSUPPORTED; ! 865: }/* end setPowerManagement */ ! 866: ! 867: ! 868: #if APPLE_MESH_ENABLE_GET_SET ! 869: ! 870: - (IOReturn) setIntValues : (unsigned*) parameterArray ! 871: forParameter : (IOParameterName) parameterName ! 872: count : (unsigned int) count ! 873: { ! 874: int target; ! 875: PerTargetData *perTargetPtr; ! 876: IOReturn ioReturn = IO_R_INVALID_ARG; ! 877: ! 878: ! 879: if ( strcmp( parameterName, APPLE_MESH_AUTOSENSE ) == 0 ) ! 880: { ! 881: if ( count == 1 ) ! 882: { ! 883: autoSenseEnable = parameterArray[0] ? 1 : 0; ! 884: ELG( 0, autoSenseEnable, 'sVas', "setIntValues - autoSense\n" ); ! 885: ioReturn = IO_R_SUCCESS; ! 886: } ! 887: } ! 888: else if ( strcmp( parameterName, APPLE_MESH_CMD_QUEUE ) == 0 ) ! 889: { ! 890: if ( count == 1 ) ! 891: { ! 892: cmdQueueEnable = parameterArray[0] ? 1 : 0; ! 893: ELG( 0, cmdQueueEnable, 'sVqe', "setIntValues - cmdQueueEnable\n" ); ! 894: ioReturn = IO_R_SUCCESS; ! 895: } ! 896: } ! 897: else if ( strcmp( parameterName, APPLE_MESH_SYNC ) == 0 ) ! 898: { ! 899: if ( count == 1 ) ! 900: { ! 901: syncModeEnable = parameterArray[0] ? 1 : 0; ! 902: ELG( 0, syncModeEnable, 'sVse', "setIntValues - syncModeEnable\n" ); ! 903: ioReturn = IO_R_SUCCESS; ! 904: } ! 905: } ! 906: else if ( strcmp( parameterName, APPLE_MESH_FAST_SCSI ) == 0 ) ! 907: { ! 908: if ( count == 1 ) ! 909: { ! 910: fastModeEnable = parameterArray[0] ? 1 : 0; ! 911: ELG( 0, fastModeEnable, 'sVfe', "setIntValues - fastModeEnable\n" ); ! 912: ioReturn = IO_R_SUCCESS; ! 913: } ! 914: } ! 915: else if ( strcmp( parameterName, APPLE_MESH_RESET_TARGETS ) == 0 ) ! 916: { ! 917: if ( count == 0 ) ! 918: { ! 919: /* Re-enable sync and command queuing. */ ! 920: /* The disable bits persist after a reset. */ ! 921: for ( target = 0; target < SCSI_NTARGETS; target++ ) ! 922: { ! 923: perTargetPtr = &gPerTargetData[ target ]; ! 924: perTargetPtr->syncDisable = FALSE; ! 925: perTargetPtr->maxQueue = 0; ! 926: perTargetPtr->inquiry_7 = 0; ! 927: } ! 928: ELG( 0, 0, 'sVrt', "setIntValues - reset targets\n" ); ! 929: ioReturn = IO_R_SUCCESS; ! 930: } ! 931: } ! 932: else if ( strcmp( parameterName, APPLE_MESH_RESET_TIMESTAMP ) == 0 ) ! 933: { ! 934: ResetTimestampIndex(); ! 935: ioReturn = IO_R_SUCCESS; ! 936: } ! 937: else if ( strcmp( parameterName, APPLE_MESH_ENABLE_TIMESTAMP ) == 0 ) ! 938: { ! 939: EnableTimestamp( TRUE ); ! 940: ioReturn = IO_R_SUCCESS; ! 941: } ! 942: else if ( strcmp( parameterName, APPLE_MESH_DISABLE_TIMESTAMP ) == 0 ) ! 943: { ! 944: EnableTimestamp( FALSE ); ! 945: ioReturn = IO_R_SUCCESS; ! 946: } ! 947: else if ( strcmp( parameterName, APPLE_MESH_PRESERVE_FIRST_TIMESTAMP ) == 0 ) ! 948: { ! 949: PreserveTimestamp( TRUE ); ! 950: ioReturn = IO_R_SUCCESS; ! 951: } ! 952: else if ( strcmp( parameterName, APPLE_MESH_PRESERVE_LAST_TIMESTAMP ) == 0 ) ! 953: { ! 954: PreserveTimestamp( FALSE ); ! 955: ioReturn = IO_R_SUCCESS; ! 956: } ! 957: else ! 958: { ! 959: ioReturn [ super setIntValues : parameterArray ! 960: forParameter : parameterName ! 961: count : count ]; ! 962: } ! 963: return ioReturn; ! 964: }/* end setIntValues */ ! 965: ! 966: ! 967: - (IOReturn) getIntValues : (unsigned*) parameterArray ! 968: forParameter : (IOParameterName) parameterName ! 969: count : (unsigned*) count /* in/out */ ! 970: { ! 971: IOReturn ioReturn = IO_R_INVALID_ARG; ! 972: ! 973: ! 974: if ( strcmp( parameterName, APPLE_MESH_AUTOSENSE) == 0 ) ! 975: { ! 976: if ( *count == 1 ) ! 977: { ! 978: parameterArray[0] = autoSenseEnable; ! 979: ioReturn = IO_R_SUCCESS; ! 980: } ! 981: } ! 982: else if ( strcmp( parameterName, APPLE_MESH_CMD_QUEUE ) == 0 ) ! 983: { ! 984: if ( *count == 1 ) ! 985: { ! 986: parameterArray[0] = cmdQueueEnable; ! 987: ioReturn = IO_R_SUCCESS; ! 988: } ! 989: } ! 990: else if ( strcmp( parameterName, APPLE_MESH_SYNC ) == 0 ) ! 991: { ! 992: if ( *count == 1 ) ! 993: { ! 994: parameterArray[0] = syncModeEnable; ! 995: ioReturn = IO_R_SUCCESS; ! 996: } ! 997: } ! 998: else if ( strcmp( parameterName, APPLE_MESH_FAST_SCSI ) == 0 ) ! 999: { ! 1000: if ( *count == 1 ) ! 1001: { ! 1002: parameterArray[0] = fastModeEnable; ! 1003: ioReturn = IO_R_SUCCESS; ! 1004: } ! 1005: } ! 1006: else if ( strcmp( parameterName, APPLE_MESH_RESET_TIMESTAMP ) == 0 ) ! 1007: { ! 1008: ResetTimestampIndex(); ! 1009: ioReturn = IO_R_SUCCESS; ! 1010: } ! 1011: else if ( strcmp( parameterName, APPLE_MESH_ENABLE_TIMESTAMP ) == 0 ) ! 1012: { ! 1013: EnableTimestamp( TRUE ); ! 1014: ioReturn = IO_R_SUCCESS; ! 1015: } ! 1016: else if ( strcmp( parameterName, APPLE_MESH_DISABLE_TIMESTAMP ) == 0 ) ! 1017: { ! 1018: EnableTimestamp( FALSE ); ! 1019: ioReturn = IO_R_SUCCESS; ! 1020: } ! 1021: else if ( strcmp( parameterName, APPLE_MESH_PRESERVE_FIRST_TIMESTAMP ) == 0 ) ! 1022: { ! 1023: PreserveTimestamp( TRUE ); ! 1024: ioReturn = IO_R_SUCCESS; ! 1025: } ! 1026: else if ( strcmp( parameterName, APPLE_MESH_PRESERVE_LAST_TIMESTAMP ) == 0 ) ! 1027: { ! 1028: PreserveTimestamp( FALSE ); ! 1029: ioReturn = IO_R_SUCCESS; ! 1030: } ! 1031: else ! 1032: { ! 1033: ioReturn = [ super getIntValues : parameterArray ! 1034: forParameter : parameterName ! 1035: count : count ]; ! 1036: } ! 1037: return ioReturn; ! 1038: }/* end getIntValues */ ! 1039: ! 1040: #endif APPLE_MESH_ENABLE_GET_SET ! 1041: ! 1042: @end /* AppleMesh_SCSI */ ! 1043: ! 1044: ! 1045: ! 1046: @implementation AppleMesh_SCSI( Hardware ) ! 1047: ! 1048: /* Perform MESH-specific initialization. */ ! 1049: /* Fetch the device's bus address and interrupt port number. */ ! 1050: /* Also, allocate one page of memory for the channel program. */ ! 1051: ! 1052: - InitializeHardware : deviceDescription ! 1053: { ! 1054: IOReturn ioReturn = IO_R_SUCCESS; ! 1055: id result = self; ! 1056: kern_return_t kernelReturn; ! 1057: UInt8 target, lun; ! 1058: id configTable; ! 1059: const char *configValue; ! 1060: UInt8 deviceNumber; ! 1061: UInt8 functionNumber; ! 1062: UInt8 busNumber; ! 1063: ! 1064: ! 1065: configTable = [ deviceDescription configTable ]; ! 1066: ASSERT( configTable ); ! 1067: configValue = [ configTable valueForStringKey: "Bus Type" ]; ! 1068: ! 1069: if ( configValue == NULL || strcmp( configValue, "PPC" ) ) ! 1070: { ! 1071: PAUSE( 0, 'init', 'Hdw-', "InitializeHardware - bus type NG.\n" ); ! 1072: ioReturn = IO_R_NO_DEVICE; ! 1073: } ! 1074: ! 1075: if ( ioReturn == IO_R_SUCCESS ) ! 1076: { ! 1077: #if 0 // ** ** ** Need correct definition ** ** ** ! 1078: ioReturn = [ deviceDescription getPCIDevice ! 1079: : &deviceNumber ! 1080: function : &functionNumber ! 1081: bus : &busNumber ]; ! 1082: #else ! 1083: deviceNumber = 0; ! 1084: functionNumber = 0; ! 1085: busNumber = 0; ! 1086: kernelReturn = 0; ! 1087: #endif ! 1088: if ( ioReturn != IO_R_SUCCESS ) ! 1089: PAUSE( 0, ioReturn, 'iHd-', "InitializeHardware - Can't get PCI device information.\n" ); ! 1090: } ! 1091: ! 1092: if ( configValue ) ! 1093: { ! 1094: [ configTable freeString : configValue ]; ! 1095: configValue = NULL; ! 1096: } ! 1097: ! 1098: if ( ioReturn == IO_R_SUCCESS ) ! 1099: ioReturn = [ self AllocHdwAndChanMem : deviceDescription ]; ! 1100: ! 1101: if ( ioReturn == IO_R_SUCCESS ) ! 1102: { ! 1103: for ( target = 0; target < SCSI_NTARGETS; target++ ) ! 1104: { ! 1105: gPerTargetData[ target ].syncParms = kSyncParmsAsync; ! 1106: gPerTargetData[ target ].negotiateSDTR = kSyncParmsFast; // negotiate Fast ! 1107: gPerTargetData[ target ].inquiry_7 = 0; ! 1108: } ! 1109: ! 1110: /* All of the addresses are established. */ ! 1111: /* Check that the hardware is present and working. */ ! 1112: ioReturn = [ self DoHBASelfTest ]; ! 1113: } ! 1114: ! 1115: if ( ioReturn == IO_R_SUCCESS ) ! 1116: { ! 1117: /* Tell the superclass to initialize our IO thread. */ ! 1118: /* After this, we should be able to execute SCSI requests. */ ! 1119: ! 1120: if ( [ super initFromDeviceDescription : deviceDescription ] == NULL ) ! 1121: { ! 1122: PAUSE( 0, 0, 'i h-', "InitializeHardware - Host Adaptor was not initialized. Fatal.\n" ); ! 1123: ioReturn = IO_R_NO_DEVICE; ! 1124: } ! 1125: } ! 1126: ! 1127: if ( ioReturn == IO_R_SUCCESS ) ! 1128: { ! 1129: gFlagIOThreadRunning = 1; ! 1130: ! 1131: /* Initialize local variables. Note that activeArray and */ ! 1132: /* perTarget arrays are zeroed by objc runtime. */ ! 1133: ! 1134: queue_init( &disconnectedCmdQ ); ! 1135: queue_init( &incomingCmdQ ); ! 1136: queue_init( &pendingCmdQ ); ! 1137: queue_init( &abortCmdQ ); ! 1138: incomingCmdLock = [ [ NXLock alloc ] init ]; ! 1139: gActiveCommand = NULL; ! 1140: [ self resetStatistics ]; ! 1141: gNextQueueTag = QUEUE_TAG_NONTAGGED + 1; ! 1142: gInitiatorID = kInitiatorIDDefault; ! 1143: gInitiatorIDMask = 1 << gInitiatorID; /* BusID bitmask for selection. */ ! 1144: gFlagReselecting = FALSE; ! 1145: ! 1146: /* Reserve the initiator ID for all LUNs: */ ! 1147: ! 1148: for ( lun = 0; lun < SCSI_NLUNS; lun++ ) ! 1149: [ self reserveTarget : gInitiatorID lun : lun forOwner : self ]; ! 1150: ! 1151: /* Get tagged command queueing, sync mode, */ ! 1152: /* fast mode enables from configTable. */ ! 1153: ! 1154: gOptionCmdQueueEnable = getConfigParam( configTable, CMD_QUEUE_ENABLE ); ! 1155: gOptionSyncModeEnable = getConfigParam( configTable, SYNC_ENABLE ); ! 1156: gOptionFastModeEnable = getConfigParam( configTable, FAST_ENABLE ); ! 1157: gOptionExtendTiming = getConfigParam( configTable, EXTENDED_TIMING ); ! 1158: gOptionAutoSenseEnable = AUTO_SENSE_ENABLE; // from bringup.h ! 1159: ! 1160: gOptionCmdQueueEnable = 1; /* Temp for testing??? */ ! 1161: ! 1162: /* Get internal version of interruptPort; */ ! 1163: /* set the port queue length to the maximum size. */ ! 1164: /* It is not clear if we want to do this. */ ! 1165: ! 1166: gKernelInterruptPort = IOConvertPort( [ self interruptPort ], ! 1167: IO_KernelIOTask, ! 1168: IO_Kernel ); ! 1169: #if 0 /***** Need correct header file *****/ ! 1170: kernelReturn = port_set_backlog( task_self(), ! 1171: [ self interruptPort ], ! 1172: PORT_BACKLOG_MAX ); ! 1173: if ( kernelReturn != KERN_SUCCESS ) ! 1174: PAUSE( 0, kernelReturn, 'i H-', "InitializeHardware - warning, port_set_backlog error.\n" ); ! 1175: #endif ! 1176: ! 1177: /* Initialize the chip and reset the bus: */ ! 1178: ! 1179: ioReturn = [ self ResetHardware : TRUE ]; ! 1180: meshAddr->sourceID = gInitiatorID; // mlj ??? fix this ! 1181: } ! 1182: ! 1183: if ( ioReturn == IO_R_SUCCESS ) ! 1184: { ! 1185: /* OK, we're ready to roll. */ ! 1186: ! 1187: [ self enableInterrupt : 0 ]; ! 1188: [ self registerDevice ]; ! 1189: ! 1190: IOScheduleFunc( serviceTimeoutInterrupt, (void*)0x333, ONE_SECOND ); ! 1191: } ! 1192: else ! 1193: { /* Do we need to free the locks and similar? */ ! 1194: [ self free ]; ! 1195: result = NULL; ! 1196: } ! 1197: ! 1198: return result; ! 1199: }/* end InitializeHardware */ ! 1200: ! 1201: ! 1202: /* This includes a SCSI reset. */ ! 1203: /* Handling of ioComplete of active and disconnected commands */ ! 1204: /* must be done elsewhere. Returns IO_R_SUCCESS if successful. */ ! 1205: /* This is called from a Task thread. It will disable and */ ! 1206: /* re-enable interrupts. Reason is for error logging. */ ! 1207: ! 1208: - (IOReturn) ResetHardware : (Boolean)resetSCSIBus ! 1209: { ! 1210: ELG( 0, resetSCSIBus, 'RstH', "ResetHardware - Bus Reset.\n" ); ! 1211: ! 1212: [ self abortAllCommands : SR_IOST_RESET ]; ! 1213: [ self ResetMESH : resetSCSIBus ]; ! 1214: ! 1215: return IO_R_SUCCESS; ! 1216: }/* end ResetHardware */ ! 1217: ! 1218: ! 1219: /* Start a SCSI transaction for the specified command. */ ! 1220: /* ActiveCmd must be NULL. A return of kHardwareStartRejected */ ! 1221: /* indicates that caller may try again with another command; */ ! 1222: /* kHardwareStartBusy indicates a condition other than */ ! 1223: /* (activeCmd != NULL) which prevents the processing of the command. */ ! 1224: ! 1225: - (HardwareStartResult) hardwareStart : (CommandBuffer*) cmdBuf ! 1226: { ! 1227: IOSCSIRequest *scsiReq; ! 1228: HardwareStartResult result = kHardwareStartOK; ! 1229: cdb_t *cdbp; ! 1230: Boolean okToDisconnect = TRUE; ! 1231: Boolean okToQueue = gOptionCmdQueueEnable; ! 1232: UInt8 msgByte; ! 1233: ! 1234: ! 1235: ASSERT( cmdBuf && cmdBuf->scsiReq ); ! 1236: ! 1237: scsiReq = cmdBuf->scsiReq; ! 1238: gCurrentTarget = scsiReq->target; ! 1239: gCurrentLUN = scsiReq->lun; ! 1240: cdbp = &scsiReq->cdb; ! 1241: gMsgOutFlag = 0; ! 1242: ! 1243: cmdBuf->cdbLength = GetSCSICommandLength( cdbp, scsiReq->cdbLength ); ! 1244: if ( cmdBuf->cdbLength == 0 ) ! 1245: { ! 1246: /* Failure: we can't determine the length of this command. */ ! 1247: ! 1248: scsiReq->driverStatus = SR_IOST_CMDREJ; ! 1249: [ self ioComplete : cmdBuf ]; ! 1250: result = kHardwareStartRejected; ! 1251: } ! 1252: { UInt8 *bp = (UInt8*)cdbp; ! 1253: ELG( ( bp[0]<<24) | (bp[1]<<16) | (bp[2]<<8) | bp[3], ! 1254: ( bp[4]<<24) | (bp[5]<<16) | (bp[6]<<8) | bp[7], ! 1255: '=CDB', "hardwareStart - CDB" ); ! 1256: } ! 1257: ! 1258: if ( result == kHardwareStartOK ) ! 1259: { ! 1260: /* Peek at the control byte (the last byte in the command). */ ! 1261: ! 1262: msgByte = ((UInt8*)cdbp)[ cmdBuf->cdbLength - 1 ]; ! 1263: if ( (msgByte & CTRL_LINKFLAG) != CTRL_NOLINK ) ! 1264: { ! 1265: /* Failure: we don't support linked commands. */ ! 1266: ! 1267: scsiReq->driverStatus = SR_IOST_CMDREJ; ! 1268: [ self ioComplete : cmdBuf ]; ! 1269: result = kHardwareStartRejected; ! 1270: } ! 1271: } ! 1272: ! 1273: if ( result == kHardwareStartOK ) ! 1274: { ! 1275: /* Autosense always renegotiates synchronous transfer mode. */ ! 1276: /* This is necessary as the target might have been reset */ ! 1277: /* or hit with a power-cycle. Autosense is never issued */ ! 1278: /* with a queue tag. */ ! 1279: ! 1280: cmdBuf->queueTag = QUEUE_TAG_NONTAGGED; /* No tag just yet */ ! 1281: if ( cmdBuf->flagIsAutosense ) ! 1282: { ! 1283: okToDisconnect = FALSE; ! 1284: gPerTargetData[ gCurrentTarget ].negotiateSDTR = gPerTargetData[ gCurrentTarget ].syncParms; ! 1285: } ! 1286: else ! 1287: { ! 1288: /* This is a real command. Setup the user data pointers */ ! 1289: /* and counters and build a SCSI request CCL. */ ! 1290: /* First, peek at the command for some special cases. */ ! 1291: ! 1292: switch ( cdbp->cdb_opcode ) ! 1293: { ! 1294: case kScsiCmdInquiry: ! 1295: ! 1296: /* The first command SCSIDisk sends us is an Inquiry. */ ! 1297: /* This never gets retried, so avoid a possible */ ! 1298: /* reject of a command queue tag. Avoid this hack if */ ! 1299: /* there are any other commands outstanding for this */ ! 1300: /* Target/LUN. */ ! 1301: ! 1302: if ( gActiveArray[ scsiReq->target ][ scsiReq->lun ] == 0 ) ! 1303: scsiReq->cmdQueueDisable = TRUE; ! 1304: ! 1305: okToDisconnect = FALSE; /* no disconnect, no queuing */ ! 1306: break; ! 1307: ! 1308: case kScsiCmdRequestSense: ! 1309: /* Always force sync renegotiation on any Request Sense */ ! 1310: /* to catch independent target power cycles. */ ! 1311: /* (Sync renegotiation needed should be set after all */ ! 1312: /* target-detected errors -- fix needed in MessageIn). */ ! 1313: /* Sense is always issued with disconnect disabled to */ ! 1314: /* maintain T/L/Q nexus. */ ! 1315: /* Watch it: request sense from a client is incompatible*/ ! 1316: /* with tagged queuing. */ ! 1317: ! 1318: gPerTargetData[ gCurrentTarget ].negotiateSDTR ! 1319: = gPerTargetData[ gCurrentTarget ].syncParms; ! 1320: okToDisconnect = FALSE; ! 1321: break; ! 1322: ! 1323: case kScsiCmdTestUnitReady: ! 1324: case kScsiCmdReadCapacity: ! 1325: okToDisconnect = FALSE; ! 1326: break; ! 1327: }/* end SWITCH on opcode */ ! 1328: }/* end ELSE not auto sense */ ! 1329: }/* end IF kHardwareStartOK */ ! 1330: ! 1331: okToDisconnect &= scsiReq->disconnect; ! 1332: ! 1333: okToQueue &= okToDisconnect ! 1334: && (scsiReq->cmdQueueDisable == FALSE) ! 1335: && (gPerTargetData[ scsiReq->target ].inquiry_7 & 0x02); ! 1336: ! 1337: cmdBuf->flagActive = 0; /* Initialize flags for this command. */ ! 1338: ! 1339: /* Make sure that the HBA is stable before we */ ! 1340: /* try to start a request. */ ! 1341: ! 1342: if ( result == kHardwareStartOK ) ! 1343: { ! 1344: if ( gActiveCommand ) ! 1345: { ! 1346: /* This should never happen. It ensures that there are */ ! 1347: /* no race conditions that reselect us between the time */ ! 1348: /* threadExecuteRequest looked at gActiveCommand and */ ! 1349: /* the time we disabled interrupts. */ ! 1350: ! 1351: queue_enter( &pendingCmdQ, cmdBuf, CommandBuffer*, link ); ! 1352: result = kHardwareStartBusy; ! 1353: } ! 1354: } ! 1355: ! 1356: if ( result == kHardwareStartOK ) ! 1357: { ! 1358: /* Activate this command - if we fail later, we'll de-activate it. */ ! 1359: ! 1360: ASSERT( gActiveCommand == NULL ); ! 1361: [ self activateCommand : cmdBuf ]; ! 1362: ASSERT( scsiReq->target == gCurrentTarget && scsiReq->lun == gCurrentLUN ); ! 1363: [ self ClearCPResults ]; ! 1364: ! 1365: /* Reset the message-out buffer pointer for the */ ! 1366: /* Identify, SDTR, and queue tag messages. */ ! 1367: ! 1368: msgOutPtr = (UInt8*)CCLAddress( kcclMSGOdata ); ! 1369: msgByte = kScsiMsgIdentify | scsiReq->lun; ! 1370: ! 1371: if ( okToDisconnect ) ! 1372: msgByte |= kScsiMsgEnableDisconnectMask; ! 1373: ! 1374: *msgOutPtr++ = msgByte; ! 1375: ! 1376: /* According to the SCSI Spec, the tag command */ ! 1377: /* immediately follows the selection. */ ! 1378: /* Note that autosense is never tagged.. */ ! 1379: /* The command was initialized with QUEUE_TAG_NONTAGGED. */ ! 1380: /***** Driver Kit only supports simple queue tags. *****/ ! 1381: ! 1382: if ( okToQueue ) ! 1383: { ! 1384: /* Avoid using tag QUEUE_TAG_NONTAGGED (zero). */ ! 1385: ! 1386: cmdBuf->queueTag = gNextQueueTag; ! 1387: if ( ++gNextQueueTag == QUEUE_TAG_NONTAGGED ) ! 1388: gNextQueueTag++; ! 1389: *msgOutPtr++ = kScsiMsgSimpleQueueTag; ! 1390: *msgOutPtr++ = cmdBuf->queueTag; ! 1391: gMsgOutFlag |= kFlagMsgOut_Queuing; ! 1392: } ! 1393: ! 1394: /* Do we need to negotiate SDTR for this target? */ ! 1395: ! 1396: msgByte = gPerTargetData[ scsiReq->target ].negotiateSDTR; ! 1397: ELG( scsiReq->target << 16 | gPerTargetData[ scsiReq->target ].inquiry_7, ! 1398: gPerTargetData[ scsiReq->target ].syncParms << 16 | msgByte, ! 1399: 'SYN?', "Sync" ); ! 1400: if ( !(gPerTargetData[ scsiReq->target ].inquiry_7 & 0x10) ) ! 1401: { ! 1402: msgByte = 0; /* if Inquiry data doesn't permit Synchronous */ ! 1403: } ! 1404: if ( msgByte ) ! 1405: { ! 1406: // gPerTargetData[ scsiReq->target ].negotiateSDTR = 0; ! 1407: *msgOutPtr++ = kScsiMsgExtended; /* Extended Message */ ! 1408: *msgOutPtr++ = 0x03; /* Message Length */ ! 1409: *msgOutPtr++ = kScsiMsgSyncXferReq; ! 1410: if ( msgByte == kSyncParmsAsync ) ! 1411: { ! 1412: *msgOutPtr++ = 200 >> 4; /* Period? used? */ ! 1413: *msgOutPtr++ = 0; /* Offset (async) */ ! 1414: } ! 1415: else ! 1416: { ! 1417: *msgOutPtr++ = 100 >> 2; /* 100 nSec period */ ! 1418: *msgOutPtr++ = msgByte >> 4; /* FIFO size */ ! 1419: } ! 1420: gMsgOutFlag |= kFlagMsgOut_SDTR; ! 1421: }/* end IF need to negotiate (a)sync */ ! 1422: ! 1423: if ( cmdBuf->flagIsAutosense ) ! 1424: { ! 1425: [ self InitAutosenseCCL ]; ! 1426: } ! 1427: else ! 1428: { ! 1429: cmdBuf->currentDataIndex = 0; ! 1430: cmdBuf->savedDataIndex = 0; ! 1431: if ( cmdBuf->mem ) ! 1432: { ! 1433: [ cmdBuf->mem setPosition : 0 ]; ! 1434: [ cmdBuf->mem state : &cmdBuf->savedDataState ]; ! 1435: } ! 1436: scsiReq->driverStatus = SR_IOST_INVALID; ! 1437: scsiReq->totalTime = 0; ! 1438: scsiReq->latentTime = 0; ! 1439: [ self UpdateCP : FALSE ]; /* Update the DBDMA Channel Program */ ! 1440: } ! 1441: ! 1442: /***** Can a caller override the default timeout? *****/ ! 1443: ! 1444: meshAddr->selectionTimeOut = gSelectionTimeout; ! 1445: meshAddr->destinationID = scsiReq->target; ! 1446: meshAddr->syncParms = gPerTargetData[ scsiReq->target ].syncParms; ! 1447: SynchronizeIO(); ! 1448: [ self RunDBDMA : kcclStart stageLabel : kcclStageInit ]; ! 1449: IOGetTimestamp( &cmdBuf->startTime ); ! 1450: } ! 1451: return result; ! 1452: }/* end hardwareStart */ ! 1453: ! 1454: ! 1455: @end /* AppleMesh_SCSI( Hardware ) */ ! 1456: ! 1457: ! 1458: /* Obtain a YES/NO type parameter from the config table. */ ! 1459: /* @param configTable The table to examine. */ ! 1460: /* @param paramName The parameter to look for. */ ! 1461: /* @result Zero if missing from the table or the table */ ! 1462: /* value is not YES. One if present in the table and the */ ! 1463: /* table value is YES. */ ! 1464: ! 1465: static int getConfigParam( id configTable, const char *paramName ) ! 1466: { ! 1467: const char *value; ! 1468: int rtn = 0; // default if not present in table ! 1469: ! 1470: ! 1471: value = [ configTable valueForStringKey : paramName ]; ! 1472: if ( value ) ! 1473: { ! 1474: if ( strcmp( value, "YES" ) == 0 ) ! 1475: rtn = 1; ! 1476: [ configTable freeString : value ]; ! 1477: } ! 1478: return rtn; ! 1479: }/* end getConfigParam */ ! 1480: ! 1481: ! 1482: static unsigned int GetSCSICommandLength( const cdb_t *cdbPtr, unsigned int defaultLength ) ! 1483: { ! 1484: unsigned int result; ! 1485: ! 1486: /* Warning: don't use sizeof here - the compiler rounds */ ! 1487: /* the value up to the next word boundary. */ ! 1488: ! 1489: switch ( ((UInt8*)cdbPtr)[0] & 0xE0 ) ! 1490: { ! 1491: case (0 << 5): result = 6; break; ! 1492: case (1 << 5): ! 1493: case (2 << 5): result = 10; break; ! 1494: case (5 << 5): result = 12; break; ! 1495: case (6 << 5): result = (defaultLength != 0) ? defaultLength : 6; break; ! 1496: case (7 << 5): result = (defaultLength != 0) ? defaultLength : 10; break; ! 1497: default: result = 0; break; ! 1498: } ! 1499: return result; ! 1500: }/* end GetSCSICommandLength */ ! 1501: ! 1502: ! 1503: /* These are the hardware-specific methods that are not */ ! 1504: /* explicitly tied to Mesh and DBDMA. */ ! 1505: ! 1506: @implementation AppleMesh_SCSI( HardwarePrivate ) ! 1507: ! 1508: /* Fetch the device's bus address and allocate one page of memory */ ! 1509: /* for the channel command. (Strictly speaking, we don't need an */ ! 1510: /* entire page, but we can use the rest of the page for a permanent */ ! 1511: /* status log). */ ! 1512: /* @param deviceDescription Specify the device to initialize. */ ! 1513: /* @return IO_R_SUCCESS if successful, else an error status. */ ! 1514: ! 1515: - (IOReturn) AllocHdwAndChanMem : deviceDescription ! 1516: { ! 1517: IOReturn ioReturn = IO_R_SUCCESS; ! 1518: enum ! 1519: { kMESHRegisterBase = 0, ! 1520: kDBDMARegisterBase = 1, ! 1521: kNumberRegisters = 2 ! 1522: }; ! 1523: ! 1524: ! 1525: meshAddr = (MeshRegister*)gMESHPhysAddr = 0; ! 1526: dbdmaAddr = (dbdma_regmap_t*)dbdmaAddrPhys = 0; ! 1527: ! 1528: /* Set the default selection timeout to the MESH value (10 msec units). */ ! 1529: ! 1530: gSelectionTimeout = 250 / 10; // ??? symbolic ! 1531: ! 1532: /* Allocate a page of wired-down memory in the kernel. Although */ ! 1533: /* Driver Kit provides a memory allocator, IOMalloc, it does */ ! 1534: /* not guarantee page alignment. Thus, we call the Mach kernel */ ! 1535: /* routine. According to the description of kalloc(), 8192 is */ ! 1536: /* the smallest amount of memory we can allocate. The channel */ ! 1537: /* command area will fit into the start of this area. */ ! 1538: ! 1539: cclLogAddrSize = page_size; ! 1540: cclLogAddr = (DBDMADescriptor*)kalloc( cclLogAddrSize ); ! 1541: if ( !cclLogAddr ) ! 1542: { PAUSE( 0, cclLogAddrSize, 'CCA-', "AllocHdwAndChanMem - can't allocate channel command area.\n" ); ! 1543: ioReturn = IO_R_NO_MEMORY; ! 1544: } ! 1545: ! 1546: if ( ioReturn == IO_R_SUCCESS ) ! 1547: { ! 1548: if ( IOIsAligned( cclLogAddr, page_size ) == 0 ) ! 1549: { ! 1550: PAUSE( 0, cclLogAddr, 'cca-', "AllocHdwAndChanMem - not page-aligned.\n" ); ! 1551: ioReturn = IO_R_NO_MEMORY; ! 1552: } ! 1553: } ! 1554: ! 1555: if ( ioReturn == IO_R_SUCCESS ) ! 1556: { ! 1557: /* Remember the number of DBDMA descriptors that */ ! 1558: /* can be used for data transfer channel commands. */ ! 1559: ! 1560: gDBDMADescriptorMax = (cclLogAddrSize - kcclDataXfer) ! 1561: / sizeof( DBDMADescriptor ); ! 1562: #if 0 ! 1563: /* Fetch the logical and physical addresses */ ! 1564: /* to access the MESH and DBDMA hardware. */ ! 1565: ! 1566: memoryRangeList = [ deviceDescription memoryRangeList ]; ! 1567: numMemoryRanges = [ deviceDescription numMemoryRanges ]; ! 1568: for ( i = 0; i < numMemoryRanges; i++ ) ! 1569: ELG( memoryRangeList[ i ].start, memoryRangeList[ i ].size, 'Rang', "AllocHdwAndChanMem - range start & size.\n" ); ! 1570: if ( numMemoryRanges != kNumberRegisters ) ! 1571: { PAUSE( memoryRangeList[ i ].start, memoryRangeList[ i ].size, 'Rng-', "AllocHdwAndChanMem - numMemoryRanges != kNumberRegisters.\n" ); ! 1572: ioReturn = IO_R_INVALID; /* This "can't happen" */ ! 1573: } ! 1574: #endif ! 1575: } ! 1576: ! 1577: #if 0 ! 1578: if ( ioReturn == IO_R_SUCCESS ) ! 1579: { ! 1580: /* We know that the first range describes the MESH chip, */ ! 1581: /* and the second range describes the DBDMA chip. */ ! 1582: ! 1583: gMESHPhysAddr = (PhysicalAddress)memoryRangeList[ kMESHRegisterBase ].start; ! 1584: dbdmaAddrPhys = (PhysicalAddress)memoryRangeList[ kDBDMARegisterBase ].start; ! 1585: ! 1586: /* Weave together the logical and physical addresses. */ ! 1587: /* First, map the MESH and DBDMA chips into our address space. */ ! 1588: ! 1589: ioReturn = IOMapPhysicalIntoIOTask( (UInt32)gMESHPhysAddr, ! 1590: sizeof( MeshRegister ), ! 1591: (vm_address_t*)&meshAddr ); ! 1592: if ( ioReturn != IO_R_SUCCESS ) ! 1593: PAUSE( 0, ioReturn, 'map-', "AllocHdwAndChanMem - MESH mapping err.\n" ); ! 1594: } ! 1595: ! 1596: if ( ioReturn == IO_R_SUCCESS ) ! 1597: { ! 1598: ioReturn = IOMapPhysicalIntoIOTask( (UInt32)dbdmaAddrPhys, ! 1599: sizeof( dbdma_regmap_t ), ! 1600: (vm_address_t*)&dbdmaAddr ); ! 1601: if ( ioReturn != IO_R_SUCCESS ) ! 1602: PAUSE( 0, ioReturn, 'Map-', "AllocHdwAndChanMem - DBDMA mapping err.\n" ); ! 1603: } ! 1604: #else ! 1605: if ( ioReturn == IO_R_SUCCESS ) ! 1606: { ! 1607: meshAddr = (MeshRegister*)gMESHPhysAddr = (PhysicalAddress)PCI_MESH_BASE_PHYS; ! 1608: ! 1609: // dbdmaAddr = (dbdma_regmap_t*)PCI_MESH_DMA_BASE_PHYS; ! 1610: dbdmaAddr = (dbdma_regmap_t*)DBDMA_REGMAP( DBDMA_MESH_SCSI ); ! 1611: // dbdmaAddrPhys = (PhysicalAddress)KVTOPHYS( (vm_offset_t)dbdmaAddr ); ! 1612: dbdmaAddrPhys = (PhysicalAddress)dbdmaAddr; ! 1613: ! 1614: ELG( dbdmaAddrPhys, dbdmaAddr, 'DBDM', ! 1615: "AllocHdwAndChanMem - DBDMA phys/logical addresses." ); ! 1616: g.meshAddr = (UInt32)meshAddr; // for debugging, miniMon ... ! 1617: #if CustomMiniMon ! 1618: gMESH_DBDMA = (UInt32)dbdmaAddr; ! 1619: gMESH_DBDMA_Phys = (UInt32)dbdmaAddrPhys; ! 1620: #endif /* CustomMiniMon */ ! 1621: } ! 1622: ! 1623: #endif ! 1624: ! 1625: if ( ioReturn == IO_R_SUCCESS ) ! 1626: { ! 1627: /* Ensure that the addresses are valid: */ ! 1628: ! 1629: ASSERT( probe_rb( meshAddr ) == 0 ); ! 1630: ASSERT( probe_rb( dbdmaAddr ) == 0 ); ! 1631: ! 1632: /* Get the physical address corresponding the DBDMA channel area: */ ! 1633: ! 1634: ioReturn = IOPhysicalFromVirtual( IOVmTaskSelf(), ! 1635: (UInt32)cclLogAddr, ! 1636: (vm_offset_t*)&cclPhysAddr ); ! 1637: g.cclPhysAddr = (UInt32)cclPhysAddr; // for debugging ease ! 1638: g.cclLogAddr = (UInt32)cclLogAddr; ! 1639: if ( ioReturn != IO_R_SUCCESS ) ! 1640: PAUSE( 0, ioReturn, 'MAP-', "AllocHdwAndChanMem - DBDMA mapping err.\n" ); ! 1641: } ! 1642: ! 1643: if ( ioReturn == IO_R_SUCCESS) ! 1644: { ! 1645: ELG( cclPhysAddr, cclLogAddr, '=CCL', ! 1646: "AllocHdwAndChanMem - CCL phys/logical addresses." ); ! 1647: [ self InitCP ]; ! 1648: } ! 1649: /* What do we do on failure? Should we try to deallocate */ ! 1650: /* the stuff we created, or will the system do this for us? */ ! 1651: ! 1652: return ioReturn; ! 1653: }/* end AllocHdwAndChanMem */ ! 1654: ! 1655: ! 1656: /* Perform one-time-only channel command program initialization. */ ! 1657: ! 1658: - (void) InitCP ! 1659: { ! 1660: register DBDMADescriptor *dst = cclLogAddr; ! 1661: register const DBDMADescriptor *src = gDescriptorList; ! 1662: UInt32 i; ! 1663: UInt8 *bp; ! 1664: ! 1665: ! 1666: ! 1667: /* Set the interrupt, branch, and wait DBDMA registers. */ ! 1668: /* Caution: the following MESH interrupt register bits are */ ! 1669: /* EndianSwapped, reverse polarity and in a different position. */ ! 1670: /* The pattern is: 0xvv00mm00, where mm is a mask byte */ ! 1671: /* and vv is a value byte to match. (After EndianSwapping). */ ! 1672: /* 0x80 means NO errors (kMeshIntrError) */ ! 1673: /* 0x40 means NO exceptions (kMeshIntrException) */ ! 1674: /* 0x20 means NO command done (kMeshIntrCmdDone) */ ! 1675: /* Branch Select is used with BRANCH_FALSE */ ! 1676: ! 1677: // DBDMASetInterruptSelect( 0x00000000 ); /* Never let DBDMA interrupt */ ! 1678: // DBDMASetWaitSelect( 0x00200020 ); /* Wait until command done */ ! 1679: // DBDMASetBranchSelect( 0x00C000C0 ); /* Branch if exception or error */ ! 1680: ! 1681: *(volatile UInt32*)&dbdmaAddr->d_intselect = 0x00000000; /* Never let DBDMA interrupt */ ! 1682: *(volatile UInt32*)&dbdmaAddr->d_wait = 0x20002000; /* Wait until command done */ ! 1683: *(volatile UInt32*)&dbdmaAddr->d_branch = 0xC000C000; /* Br if Exc or Err */ ! 1684: SynchronizeIO(); ! 1685: ! 1686: /* Relocate and EndianSwap the global channel command list */ ! 1687: /* into the page that is shared with the DBDMA device. */ ! 1688: ! 1689: for ( i = 0; i < gDescriptorListSize; i += sizeof( DBDMADescriptor ) ) ! 1690: { ! 1691: dst->operation = SWAP( src->operation ); /* copy command with count */ ! 1692: ! 1693: switch ( src->result & kRelAddress ) ! 1694: { ! 1695: case kRelAddressMESH: ! 1696: dst->address = SWAP( src->address + (UInt32)gMESHPhysAddr ); ! 1697: break; ! 1698: case kRelAddressCP: ! 1699: dst->address = SWAP( src->address + (UInt32)cclPhysAddr ); ! 1700: break; ! 1701: case kRelAddressPhys: ! 1702: dst->address = SWAP( src->address ); ! 1703: break; ! 1704: default: ! 1705: dst->address = SWAP( src->address ); ! 1706: break; ! 1707: } ! 1708: ! 1709: switch ( src->result & kRelCmdDep ) ! 1710: { ! 1711: case kRelCmdDepCP: ! 1712: dst->cmdDep = SWAP( src->cmdDep + (UInt32)cclPhysAddr ); ! 1713: break; ! 1714: case kRelCmdDepLabel: ! 1715: dst->cmdDep = src->cmdDep; ! 1716: break; ! 1717: default: ! 1718: dst->cmdDep = SWAP( src->cmdDep ); ! 1719: break; ! 1720: } ! 1721: ! 1722: dst->result = 0; ! 1723: src++; ! 1724: dst++; ! 1725: } /* FOR all elements in the descriptor list */ ! 1726: ! 1727: /* Build a SCSI CDB for the autosense Request Sense command. */ ! 1728: ! 1729: bp = (UInt8*)CCLAddress( kcclSenseCDB ); ! 1730: *bp++ = kScsiCmdRequestSense; /* Command */ ! 1731: *bp++ = 0; /* LUN to be filled in */ ! 1732: *bp++ = 0; /* reserved */ ! 1733: *bp++ = 0; /* reserved */ ! 1734: *bp++ = kMaxAutosenseByteCount; /* Allocation length - to be filled in */ ! 1735: *bp++ = 0; /* Control (flag) */ ! 1736: return; ! 1737: }/* end InitCP */ ! 1738: ! 1739: ! 1740: /* Initialize the data transfer channel command list for a normal SCSI */ ! 1741: /* command. The channel command list has a complex structure of */ ! 1742: /* transfer groups and items, where: */ ! 1743: /* transfer group The number of bytes transferred by a single */ ! 1744: /* MESH operation. This will be from 1 to */ ! 1745: /* kMaxDMATransferLength (65536 - 4096). */ ! 1746: /* transfer item The number of bytes transferred by a single */ ! 1747: /* DBDMA operation. These bytes are guaranteed */ ! 1748: /* to be physically-contiguous. */ ! 1749: /* Thus, the data transfer CCL looks like the following: */ ! 1750: /* Prolog 1: Load MESH with the first group count. */ ! 1751: /* Item 1.1: Load DBDMA with the first physical address and */ ! 1752: /* item count. */ ! 1753: /* Item 1.2 etc: Load DBDMA with the next physical address and */ ! 1754: /* item count. */ ! 1755: /* Prolog 2, etc. Load MESH with the next group count. */ ! 1756: /* Item 2.1, etc. Load DBDMA with the next group of physical */ ! 1757: /* addresses. */ ! 1758: /* Stop/Branch If all of the data transfer commands fit in the */ ! 1759: /* channel command list, branch to the Status phase*/ ! 1760: /* channel command. Otherwise, stop transfer */ ! 1761: /* (which stops in Data phase) and re-build the */ ! 1762: /* command list for the next set of data. */ ! 1763: /* Note that the last DBDMA command must be INPUT_LAST or OUTPUT_LAST */ ! 1764: /* to handle synchronous transfer odd-byte disconnect. */ ! 1765: ! 1766: - (void) UpdateCP : (Boolean) reselecting ! 1767: { ! 1768: CommandBuffer *cmdBuf; ! 1769: IOSCSIRequest *scsiReq; ! 1770: DBDMADescriptor *descProto = CCLDescriptor( kcclPrototype ); ! 1771: IOReturn ioReturn = IO_R_SUCCESS; ! 1772: DBDMADescriptor *descriptorPtr; /* current data descriptor */ ! 1773: DBDMADescriptor *descriptorMax; /* beyond the last data descriptor */ ! 1774: DBDMADescriptor *preamblePtr; /* current prolog descriptor */ ! 1775: UInt32 dbdmaOpProto; /* prototype Opcode for DBDMA */ ! 1776: UInt32 dbdmaOp; /* Opcode for DBDMA */ ! 1777: UInt32 meshSeq; /* Opcode for MESH request */ ! 1778: SInt32 transferLength; /* Number of bytes left to transfer */ ! 1779: UInt32 totalXferLen = 0; /* Total length of this transfer */ ! 1780: UInt32 groupLength; /* Number of bytes in this group */ ! 1781: UInt8 syncParms; /* Fast synchronous param value */ ! 1782: ByteCount bc; ! 1783: PhysicalRange range; ! 1784: ItemCount rangeByteCount; ! 1785: DBDMADescriptor *dp; ! 1786: ! 1787: ! 1788: ASSERT( gActiveCommand && gActiveCommand->scsiReq ); ! 1789: cmdBuf = gActiveCommand; ! 1790: scsiReq = cmdBuf->scsiReq; ! 1791: ASSERT( scsiReq->target == gCurrentTarget && scsiReq->lun == gCurrentLUN ); ! 1792: ! 1793: /* How many descriptors can we store (need some slop for the */ ! 1794: /* terminator commands). Get a pointer to the first free */ ! 1795: /* descriptor and the total number of bytes left to transfer in */ ! 1796: /* this IO request. */ ! 1797: ! 1798: descriptorPtr = CCLDescriptor( kcclDataXfer ); ! 1799: descriptorMax = &descriptorPtr[ gDBDMADescriptorMax - 16 ]; ! 1800: transferLength = scsiReq->maxTransfer - cmdBuf->currentDataIndex; ! 1801: ELG( cmdBuf, transferLength, 'UpCP', "UpdateCP" ); ! 1802: ! 1803: if ( reselecting == FALSE ) ! 1804: { ! 1805: [ self SetupMsgO ]; /* Setup for Message Out phase. */ ! 1806: ! 1807: /* Setup for Command phase: */ ! 1808: CCLByte( kcclCmdoMTC ) = cmdBuf->cdbLength; /* MESH transfer count */ ! 1809: CCLByte( kcclCmdoDTC ) = cmdBuf->cdbLength; /* DBDMA count */ ! 1810: bcopy( &scsiReq->cdb, CCLAddress( kcclCMDOdata ), cmdBuf->cdbLength ); ! 1811: } ! 1812: ! 1813: /* Generate MESH "sequence" & DBDMA "operation" for Input or Output: */ ! 1814: ! 1815: if ( scsiReq->read ) ! 1816: { dbdmaOpProto = INPUT_MORE | kBranchIfFalse; ! 1817: meshSeq = kMeshDataInCmd | kMeshSeqDMA; ! 1818: } ! 1819: else ! 1820: { dbdmaOpProto = OUTPUT_MORE | kBranchIfFalse; ! 1821: meshSeq = kMeshDataOutCmd | kMeshSeqDMA; ! 1822: } ! 1823: ! 1824: CCLWord( kcclBatchSize ) = 0; ! 1825: ! 1826: while ( ioReturn == IO_R_SUCCESS ! 1827: && transferLength > 0 ! 1828: && descriptorPtr < descriptorMax ) ! 1829: { ! 1830: /* Do one group, ie, enough CCs to fill a MESH transfer count. */ ! 1831: /* There are more data to be transferred, and CCL space to store*/ ! 1832: /* another group of data. First, leave space for the preamble. */ ! 1833: ! 1834: preamblePtr = descriptorPtr; ! 1835: groupLength = 0; ! 1836: descriptorPtr += 4; /* Preamble takes 4 descriptors */ ! 1837: ! 1838: while ( transferLength > 0 /* more to xfer */ ! 1839: && descriptorPtr < descriptorMax ) /* room in CCL */ ! 1840: { ! 1841: /* Do one physically contiguous segment: */ ! 1842: ! 1843: bc = MAX_DMA_XFER - groupLength; /* calc room left in group */ ! 1844: if ( bc < page_size ) ! 1845: break; ! 1846: rangeByteCount = [ cmdBuf->mem getPhysicalRanges : (ItemCount) 1 ! 1847: maxByteCount : bc ! 1848: newPosition : NULL ! 1849: actualRanges : NULL ! 1850: physicalRanges : &range ]; ! 1851: ! 1852: if ( rangeByteCount == 0 ) ! 1853: break; ! 1854: ! 1855: ASSERT( range.length > 0 ); ! 1856: groupLength += range.length; ! 1857: transferLength -= range.length; ! 1858: dbdmaOp = dbdmaOpProto | range.length; ! 1859: if ( transferLength <= 0 ) ! 1860: dbdmaOp |= (OUTPUT_MORE ^ OUTPUT_LAST); /* add LAST to cmd */ ! 1861: descriptorPtr->operation = SWAP( dbdmaOp ); ! 1862: descriptorPtr->address = SWAP( (UInt32)range.address ); ! 1863: descriptorPtr->cmdDep = SWAP( (UInt32)cclPhysAddr + kcclProblem ); ! 1864: descriptorPtr->result = 0; // for debugging ! 1865: descriptorPtr++; ! 1866: }/* end inner WHILE */ ! 1867: ! 1868: if ( groupLength == 0 ) ! 1869: { ! 1870: /* Nothing was built - we apparently failed to get */ ! 1871: /* a physical address. Note: there is a potential problem with */ ! 1872: /* the following sequence as the *previous* DBDMA command, if */ ! 1873: /* any, should be changed to set xxPUT_LAST. */ ! 1874: ! 1875: ELG( 0, 0, 'Grp-', "UpdateCP - groupLength is 0" ); ! 1876: preamblePtr->operation = SWAP( ! 1877: NOP_CMD | kBranchIfFalse | kWaitIfTrue ); ! 1878: preamblePtr->address = 0; ! 1879: preamblePtr->cmdDep = SWAP( (UInt32)cclPhysAddr + kcclProblem ); ! 1880: preamblePtr->result = 0; ! 1881: descriptorPtr = preamblePtr + 1; ! 1882: ioReturn = IO_R_INVALID; /* Exit the outer loop */ ! 1883: } ! 1884: else ! 1885: { ! 1886: totalXferLen += groupLength; ! 1887: ! 1888: /* This group is complete. Fill in the preamble. */ ! 1889: /* The preamble consists of the following commands: */ ! 1890: /* [0] Move <totalXferLen> to kcclBatchSize */ ! 1891: /* [1] Store group length high-byte in MESH */ ! 1892: /* transfer count 1 register */ ! 1893: /* [2] Store group length low-byte in MESH */ ! 1894: /* transfer count 1 register */ ! 1895: /* [3] Store the input/output command in the MESH */ ! 1896: /* sequence register. */ ! 1897: /* If the command finishes prematurely (perhaps the */ ! 1898: /* device wants to disconnect), the interrupt service */ ! 1899: /* routine will use totalXferLen - the residual byte */ ! 1900: /* count to determine the number of bytes xferred. */ ! 1901: ! 1902: descProto[0].cmdDep = totalXferLen; // update batch size ! 1903: descProto[1].cmdDep = SWAP( groupLength >> 8 ); ! 1904: descProto[2].cmdDep = SWAP( groupLength & 0xFF ); ! 1905: descProto[3].cmdDep = SWAP( meshSeq ); ! 1906: bcopy( descProto, preamblePtr, sizeof( DBDMADescriptor ) * 4 ); ! 1907: ELG( preamblePtr, totalXferLen, '=Tot', "UpdateCP - set preamble" ); ! 1908: ! 1909: /* If there is another group, wait for */ ! 1910: /* cmdDone and clear it: */ ! 1911: if ( transferLength > 0 ) ! 1912: { /* Wait for CmdDone: */ ! 1913: bcopy( CCLDescriptor( kcclBrProblem ), descriptorPtr, sizeof( DBDMADescriptor ) ); ! 1914: ++descriptorPtr; ! 1915: /* Clear CmdDone: */ ! 1916: /* HACK - if we reached the end of the CCL page, */ ! 1917: /* we don't want to clear cmdDone because we will lose */ ! 1918: /* an interrupt. So, this instruction may be deleted */ ! 1919: /* down below. (Radar 2298440) */ ! 1920: descriptorPtr->operation = SWAP( STORE_QUAD | KEY_SYSTEM | 1 ); ! 1921: descriptorPtr->address = SWAP( (UInt32)gMESHPhysAddr + kMeshInterrupt ); ! 1922: descriptorPtr->cmdDep = SWAP( kMeshIntrCmdDone ); ! 1923: descriptorPtr->result = 0; ! 1924: ++descriptorPtr; ! 1925: }/* end IF not last group */ ! 1926: }/* end if/ELSE a group was built */ ! 1927: }/* end outer WHILE */ ! 1928: ! 1929: /* All of the data have been transferred (or we ran off the end */ ! 1930: /* of the CCL). Update the transfer start index to reflect on */ ! 1931: /* what we *think* we will transfer in this DATA operation. If */ ! 1932: /* we completed DATA phase, branch to the Status Phase CCL; */ ! 1933: /* if not, stop the channel command so we can reload the CCL */ ! 1934: /* with the next big chunk. */ ! 1935: /* When the transfer completes, the last prolog will have stored*/ ! 1936: /* the total number of bytes transferred in a known location in */ ! 1937: /* the CCL area. */ ! 1938: /* Now, append the data transfer postamble to handle */ ! 1939: /* synchronous odd-byte disconnect and jump to status phase */ ! 1940: /* (or just stop if there's more DMA) */ ! 1941: ! 1942: #define kMaxPostamble 9 ! 1943: #define kDBDMADescriptorEnd (CCLDescriptor(kcclDataXfer) + gDBDMADescriptorMax) ! 1944: ! 1945: ASSERT( descriptorPtr + kMaxPostamble < kDBDMADescriptorEnd ); ! 1946: ! 1947: /* Do some synchronous data transfer cleanup: */ ! 1948: ! 1949: syncParms = gPerTargetData[ scsiReq->target ].syncParms; ! 1950: meshAddr->syncParms = syncParms; ! 1951: SynchronizeIO(); ! 1952: ELG( gMsgOutFlag, syncParms, 'SynP', "UpdateCP - sync parms" ); ! 1953: ! 1954: if ( ((syncParms & 0xF0) || (gMsgOutFlag & kFlagMsgOut_SDTR)) // Sync? ! 1955: && (totalXferLen > 0) // any data moving? ! 1956: && (transferLength == 0) ) // end of xfer? ! 1957: { ! 1958: gFlagIncompleteDBDMA = FALSE; /* indicate complete xfer */ ! 1959: ! 1960: /* MESH has a problem at the end of Synchronous transfers. */ ! 1961: /* If the target is fast enough, it can move from data phase to */ ! 1962: /* Status phase while MESH still has ACKed bytes in its FIFO and */ ! 1963: /* the DBDMA is still running. MESH raises PhaseMismatch Exception */ ! 1964: /* causing an interrupt in which we must empty the FIFO and move */ ! 1965: /* the bytes to the user's buffer by programmed IO. */ ! 1966: /* If the target is not fast enough, we can save the interrupt and */ ! 1967: /* bypass the mess. */ ! 1968: /* So, we do the following: */ ! 1969: /* 1) Enable only MESH Err interrupts; disable Exc and CmdDone. */ ! 1970: /* 2) Don't Wait; Branch if an interrupt may have already occurred.*/ ! 1971: /* 3) Wait for cmdDone at least for TC = FIFO count = 0 and */ ! 1972: /* maybe including PhaseMismatch. Branch to SyncCleanup if PMM. */ ! 1973: /* 4) Assume an interphase condition as opposed to an */ ! 1974: /* overrun condition and Branch Always to get Status. */ ! 1975: ! 1976: /* If the Channel Program gets this far, the OUTPUT_LAST */ ! 1977: /* has finished writing its data to the FIFO and MESH may still */ ! 1978: /* be putting bytes on the bus OR the INPUT_LAST has read all */ ! 1979: /* its data from the FIFO and MESH has already ACKed them. */ ! 1980: /* There may be or not some time before REQ appears again, */ ! 1981: /* either for data overrun or the next phase. */ ! 1982: ! 1983: /* Disable Exc and CmdDone (leave Err enabled): */ ! 1984: ! 1985: descriptorPtr->operation = SWAP( STORE_QUAD | KEY_SYSTEM | 1 ); ! 1986: descriptorPtr->address = SWAP( (UInt32)gMESHPhysAddr + kMeshInterruptMask ); ! 1987: descriptorPtr->cmdDep = SWAP( kMeshIntrError ); ! 1988: descriptorPtr->result = 0; ! 1989: ++descriptorPtr; ! 1990: ! 1991: /* Take the interrupt if PhaseMismatch not definitely caught. */ ! 1992: /* Branch (don't wait for cmdDone) if Exc may have already occurred: */ ! 1993: ! 1994: descriptorPtr->operation = SWAP( NOP_CMD | kBranchIfFalse ); ! 1995: descriptorPtr->address = 0; ! 1996: descriptorPtr->cmdDep = SWAP( (UInt32)cclPhysAddr + kcclProblem ); ! 1997: descriptorPtr->result = 0; ! 1998: /* Radar 2281306 ( and 2272931 ): */ ! 1999: /* Output may completely fit in the FIFO and not make it out */ ! 2000: /* to the SCSI bus if the target disconnects after the command. */ ! 2001: /* If that's possible, wait here for cmdDone and */ ! 2002: /* take the PhaseMismatch interrupt. This situation occurred on a */ ! 2003: /* Mode Select with an output of 12 bytes. Do this to prevent */ ! 2004: /* the Stage from advancing from kcclStageXfer so that proper */ ! 2005: /* cleanup can take place. */ ! 2006: if ( (totalXferLen < 16) && !scsiReq->read ) ! 2007: descriptorPtr->operation= SWAP( NOP_CMD | kWaitIfTrue | kBranchIfFalse ); ! 2008: ++descriptorPtr; ! 2009: ! 2010: /* Possible PhaseMisMatch caught after FIFO emptied. */ ! 2011: /* Wait for cmdDone. If Exc, branch to SyncCleanUp: */ ! 2012: ! 2013: descriptorPtr->operation = SWAP( NOP_CMD | kWaitIfTrue | kBranchIfFalse ); ! 2014: descriptorPtr->address = 0; ! 2015: descriptorPtr->cmdDep = SWAP( (UInt32)cclPhysAddr + kcclSyncCleanUp ); ! 2016: descriptorPtr->result = 0; ! 2017: descriptorPtr++; ! 2018: ! 2019: /* Interphase condition or possible overrun. */ ! 2020: /* 29sep98 PhaseMismatch occurred even after */ ! 2021: /* CmdDone was set. */ ! 2022: ! 2023: ! 2024: /* Branch Always to assume we will bit bucket some data: */ ! 2025: ! 2026: descriptorPtr->operation = SWAP( NOP_CMD | kBranchAlways ); ! 2027: descriptorPtr->address = 0; ! 2028: descriptorPtr->cmdDep = SWAP( (UInt32)cclPhysAddr + kcclOverrun ); ! 2029: descriptorPtr->result = 0; ! 2030: descriptorPtr++; ! 2031: ! 2032: /* Fix up the DataOverrun code just in case: */ ! 2033: ! 2034: dp = CCLDescriptor( kcclOverrunMESH ); ! 2035: if ( scsiReq->read ) ! 2036: { dp->cmdDep = SWAP( kMeshDataInCmd | kMeshSeqDMA ); ! 2037: dp = CCLDescriptor( kcclOverrunDBDMA ); ! 2038: dp->operation = SWAP( INPUT_LAST | kBranchIfFalse | 8 ); ! 2039: } ! 2040: else ! 2041: { dp->cmdDep = SWAP( kMeshDataOutCmd | kMeshSeqDMA ); ! 2042: dp = CCLDescriptor( kcclOverrunDBDMA ); ! 2043: dp->operation = SWAP( OUTPUT_LAST | kBranchIfFalse | 8 ); ! 2044: } ! 2045: }/* end IF last of Synchronous transfer */ ! 2046: else ! 2047: { ! 2048: /* Async or incomplete Sync. Append Branches to finish this process: */ ! 2049: ! 2050: /* If this is a partial transfer, set 'incomplete' flag. */ ! 2051: ! 2052: if ( transferLength > 0 ) ! 2053: gFlagIncompleteDBDMA = TRUE; /* set incomplete */ ! 2054: else gFlagIncompleteDBDMA = FALSE; /* assume complete xfer */ ! 2055: ! 2056: ! 2057: if ( gFlagIncompleteDBDMA ) ! 2058: { /* Delete the ccl to clear cmdDone: */ ! 2059: --descriptorPtr; /* see HACK note above. */ ! 2060: } ! 2061: else if ( totalXferLen > 0 ) ! 2062: { /* If something moved AND (Radar 2298440) xfer completed, */ ! 2063: /* Wait & Branch if problem: */ ! 2064: /* Radar 2272931 - If entire output fits in FIFO, then */ ! 2065: /* the OUTPUT_LAST completes OK without a PhaseMismatch if */ ! 2066: /* the target disconnects right after the command phase. */ ! 2067: bcopy( CCLDescriptor( kcclBrProblem ), descriptorPtr, sizeof( DBDMADescriptor ) ); ! 2068: descriptorPtr++; ! 2069: } ! 2070: /* Assume all's well - Branch to get status: */ ! 2071: descriptorPtr->operation = SWAP( NOP_CMD | kBranchAlways ); ! 2072: descriptorPtr->address = 0; ! 2073: descriptorPtr->cmdDep = SWAP( (UInt32)cclPhysAddr + kcclGetStatus ); ! 2074: descriptorPtr->result = 0; ! 2075: ! 2076: /* If this is a partial transfer, set 'incomplete' flag and */ ! 2077: /* change the Branch from GetStatus to Good: */ ! 2078: ! 2079: if ( gFlagIncompleteDBDMA ) ! 2080: { /* change last Branch from Status to Good: */ ! 2081: descriptorPtr->cmdDep = SWAP( (UInt32)cclPhysAddr + kcclMESHintr ); ! 2082: ELG( descriptorPtr, transferLength, 'Part', "UpdateCP - built partial CCL." ); ! 2083: } ! 2084: descriptorPtr++; ! 2085: }/* end if/ELSE Async or partial xfer */ ! 2086: ! 2087: ASSERT( descriptorPtr < kDBDMADescriptorEnd ); ! 2088: return; ! 2089: }/* end UpdateCP */ ! 2090: ! 2091: ! 2092: /* StartBucket - Start the channel commands to run the bit bucket. */ ! 2093: ! 2094: - (void) StartBucket ! 2095: { ! 2096: CommandBuffer *cmdBuf; ! 2097: IOSCSIRequest *scsiReq; ! 2098: DBDMADescriptor *dp; /* current data descriptor */ ! 2099: UInt32 dbdmaOp; /* Opcode for DBDMA */ ! 2100: UInt32 meshSeq; /* Opcode for MESH request */ ! 2101: ! 2102: ! 2103: cmdBuf = gActiveCommand; ! 2104: scsiReq = cmdBuf->scsiReq; ! 2105: ELG( cmdBuf, scsiReq, 'Bkt-', "StartBucket" ); ! 2106: ! 2107: /* Generate MESH "sequence" & DBDMA "operation" for Input or Output: */ ! 2108: ! 2109: if ( scsiReq->read ) ! 2110: { dbdmaOp = INPUT_MORE | kBranchIfFalse | 8; ! 2111: meshSeq = kMeshDataInCmd | kMeshSeqDMA; ! 2112: } ! 2113: else ! 2114: { dbdmaOp = OUTPUT_MORE | kBranchIfFalse | 8; ! 2115: meshSeq = kMeshDataOutCmd | kMeshSeqDMA; ! 2116: } ! 2117: ! 2118: dp = CCLDescriptor( kcclOverrunMESH ); dp->cmdDep = meshSeq; ! 2119: dp = CCLDescriptor( kcclOverrunDBDMA ); dp->operation = dbdmaOp; ! 2120: ! 2121: [ self RunDBDMA : kcclDataXfer stageLabel : kcclStageBucket ]; ! 2122: return; ! 2123: }/* end StartBucket */ ! 2124: ! 2125: ! 2126: /* Set up the channel commands for MsgO phase. */ ! 2127: ! 2128: - (void) SetupMsgO ! 2129: { ! 2130: UInt8 msgoSize; ! 2131: ! 2132: ! 2133: msgOutPtr--; /* treat the last or only byte special (drop ATN) */ ! 2134: msgoSize = msgOutPtr - CCLAddress( kcclMSGOdata ); ! 2135: if( msgoSize == 0 ) ! 2136: { /* Identify byte only: */ ! 2137: CCLWord( kcclMsgoBranch ) = SWAP( NOP_CMD | kBranchAlways ); ! 2138: } ! 2139: else /* multibyte message - set counts for all but last byte: */ ! 2140: { CCLByte( kcclMsgoMTC ) = msgoSize; ! 2141: CCLByte( kcclMsgoDTC ) = msgoSize; ! 2142: /* NOP the BRANCH: */ ! 2143: CCLWord( kcclMsgoBranch ) = SWAP( NOP_CMD ); ! 2144: } ! 2145: CCLByte( kcclMSGOLast )= *msgOutPtr; /* position last byte */ ! 2146: return; ! 2147: }/* end SetupMsgO */ ! 2148: ! 2149: ! 2150: /* Initialize the autosense area and build the autosense channel command. */ ! 2151: ! 2152: - (void) InitAutosenseCCL ! 2153: { ! 2154: ELG( 0, 0, 'Auto', "InitAutosenseCCL" ); ! 2155: /* Make sure we've allocated enough space in the CCL area. */ ! 2156: ! 2157: ASSERT( kcclSenseBuffer + kMaxAutosenseByteCount < kcclSenseResult ); ! 2158: bzero( CCLAddress( kcclSenseBuffer ), kMaxAutosenseByteCount ); ! 2159: ! 2160: /* Copy the Sense CDB to the CDB area and */ ! 2161: /* copy the Sense CCL to the Xfer area */ ! 2162: ! 2163: /* Copy the Sense CDB & Sense CCL */ ! 2164: bcopy( CCLAddress( kcclSenseCDB ), CCLAddress( kcclCMDOdata ), 6 ); ! 2165: bcopy( CCLAddress( kcclSense ), CCLAddress( kcclDataXfer ), 5 * sizeof( DBDMADescriptor ) ); ! 2166: ! 2167: /* Set the MESH and DBDMA transfer counts for the command. */ ! 2168: CCLByte( kcclCmdoMTC ) = 6; ! 2169: CCLByte( kcclCmdoDTC ) = 6; ! 2170: ! 2171: /* Set the data transfer count (use a hard-wired value). */ ! 2172: ! 2173: CCLWord( kcclBatchSize ) = kMaxAutosenseByteCount; ! 2174: ! 2175: [ self SetupMsgO ]; ! 2176: ! 2177: return; ! 2178: }/* end InitAutosenseCCL */ ! 2179: ! 2180: ! 2181: - (void) ClearCPResults ! 2182: { ! 2183: register DBDMADescriptor *dp = CCLDescriptor( kcclStart ); ! 2184: register int i; ! 2185: ! 2186: ! 2187: /* Don't clear the reserved areas or prototypes */ ! 2188: ! 2189: for ( i = (gDescriptorListSize - kcclStart) / sizeof ( DBDMADescriptor ); i; --i ) ! 2190: { ! 2191: dp->result = 0; ! 2192: dp++; ! 2193: } ! 2194: ! 2195: return; ! 2196: }/* end ClearCPResults */ ! 2197: ! 2198: ! 2199: @end /* AppleMesh_SCSI(HardwarePrivate) */ ! 2200: ! 2201: ! 2202: ! 2203: ! 2204: @implementation AppleMesh_SCSI( MeshInterrupt ) ! 2205: ! 2206: ! 2207: /* DoHardwareInterrupt - Handle an Interrupt Service message */ ! 2208: ! 2209: - (void) DoHardwareInterrupt /* called from interruptOccurred */ ! 2210: { ! 2211: [ self GetHBARegsAndClear : TRUE ]; /* get the MESH registers */ ! 2212: [ self SetIntMask : 0 ]; /* Disable MESH interrupts */ ! 2213: ! 2214: gFlagReselecting = FALSE; ! 2215: ! 2216: if ( g.shadow.mesh.interrupt == 0 ) ! 2217: { /* Interrupts can occur with no bits set in the */ ! 2218: /* interrupt register one way: */ ! 2219: /* - Eating interrupts in the driver (the ASIC */ ! 2220: /* latches the interrupt even though the */ ! 2221: /* driver or Channel Program clears the MESH */ ! 2222: /* interrupt register). */ ! 2223: PAUSE( dbdmaAddr->d_cmdptrlo, ! 2224: (g.shadow.mesh.busStatus0 << 8) | g.shadow.mesh.busStatus1, ! 2225: 'ISR?', ! 2226: "DoHardwareInterrupt - spurious interrupt" ); ! 2227: ! 2228: if ( !gActiveCommand ) ! 2229: { ! 2230: [ self selectNextRequest ]; ! 2231: if ( !gActiveCommand && queue_empty( &abortCmdQ ) ) ! 2232: /* if neither new request nor aborting: */ ! 2233: [ self SetIntMask : kMeshIntrMask ];/* Enable interrupts */ ! 2234: } ! 2235: return; ! 2236: }/* end IF no bit set in interrupt register */ ! 2237: ! 2238: dbdma_flush( DBDMA_MESH_SCSI ); /* DBDMA may be hung in */ ! 2239: dbdma_stop( DBDMA_MESH_SCSI ); /* middle of transfer. */ ! 2240: // invalidate_cache_v( (vm_offset_t)cclLogAddr, cclLogAddrSize ); ! 2241: ! 2242: /* If the DBDMA was running a channel command, handle this */ ! 2243: /* (this could be done at a lower priority level). */ ! 2244: ! 2245: if ( CCLWord( kcclStageLabel ) ) ! 2246: { ! 2247: [ self ProcessInterrupt ]; ! 2248: return; ! 2249: } ! 2250: ! 2251: /* This was not a DBDMA completion. */ ! 2252: /* See if the last MESH operation completed */ ! 2253: /* without errors or exceptions. */ ! 2254: ! 2255: if ( g.shadow.mesh.interrupt == kMeshIntrCmdDone ) ! 2256: { ! 2257: /* This was presumably a Programmed IO completion. */ ! 2258: ! 2259: if ( gActiveCommand ) ! 2260: { /* The command has not completed yet. */ ! 2261: /* We need to wait for a phase stabilizing interrupt. */ ! 2262: ! 2263: PAUSE( 0, 0, 'dhi-', "DoHardwareInterrupt - MESH interrupt problem: need phase stabilizing wait.\n" ); ! 2264: return; ! 2265: } ! 2266: else ! 2267: { /* There is no active command. */ ! 2268: /* This is presumably a bus-free completion. */ ! 2269: ! 2270: [ self selectNextRequest ]; /* Try to start another request.*/ ! 2271: ! 2272: if ( !gActiveCommand && queue_empty( &abortCmdQ ) ) ! 2273: /* If still nothing to do: */ ! 2274: [ self SetIntMask : kMeshIntrMask ];/* Re-enable ints */ ! 2275: return; ! 2276: } ! 2277: }/* end IF CmdDone without Err or Exc */ ! 2278: ! 2279: /* None of the above "completion" states occurred. */ ! 2280: /* Either a command completed unsuccessfully, or we */ ! 2281: /* were reselected. First, check for phase mismatch. */ ! 2282: if ( g.shadow.mesh.interrupt == (kMeshIntrCmdDone | kMeshIntrException) ! 2283: && g.shadow.mesh.exception == kMeshExcPhaseMM ) ! 2284: { ! 2285: PAUSE( 0, 0, 'DHI-', "DoHardwareInterrupt - MESH interrupt problem: phase mismatch interrupt.\n" ); ! 2286: } ! 2287: else ! 2288: { /* Handle reselection and all other problems separately. */ ! 2289: /* (This can be done at a lower priority.) */ ! 2290: [ self ProcessInterrupt ]; ! 2291: } ! 2292: return; ! 2293: }/* end DoHardwareInterrupt */ ! 2294: ! 2295: ! 2296: /* Respond to a DBDMA channel command completion interrupt */ ! 2297: /* or some error or exception condition. */ ! 2298: ! 2299: - (void) ProcessInterrupt ! 2300: { ! 2301: register CommandBuffer *cmdBuf; ! 2302: register IOSCSIRequest *scsiReq; ! 2303: UInt32 stage; /* Stage in the Channel Program */ ! 2304: UInt32 cclIndex; /* Index of CCL descriptor */ ! 2305: UInt32 count; /* transfer count */ ! 2306: UInt8 phase; /* Current bus phase */ ! 2307: IOReturn rc; ! 2308: ! 2309: ! 2310: if ( gActiveCommand == NULL ) ! 2311: { ! 2312: if ( g.shadow.mesh.exception & kMeshExcResel ) ! 2313: { ! 2314: [ self HandleReselectionInterrupt ]; ! 2315: } ! 2316: else ! 2317: { /* There is no active request and we are not reselecting. */ ! 2318: /* Can get here if Reject/Abort occurs or after a BusFree */ ! 2319: /* command is put in the Sequence register and we exit the */ ! 2320: /* interrupt. */ ! 2321: if ( !queue_empty( &abortCmdQ ) ) ! 2322: { ! 2323: [ self SetSeqReg : kMeshFlushFIFO ]; /* flush the FIFO */ ! 2324: queue_remove_first( &abortCmdQ, cmdBuf, CommandBuffer*, link ); ! 2325: [ self ioComplete : cmdBuf ]; ! 2326: }/* end IF Aborting disconnected commands */ ! 2327: else ! 2328: { /* This should be a Bus Free interrupt: */ ! 2329: ELG( 0, 0, 'Int0', "Process interrupt with no active request\n" ); ! 2330: } ! 2331: ! 2332: [ self selectNextRequest ]; ! 2333: if ( !gActiveCommand && queue_empty( &abortCmdQ ) ) ! 2334: /* Re-enable ints for reselect */ ! 2335: [ self SetIntMask : (kMeshIntrException | kMeshIntrError) ]; ! 2336: } ! 2337: return; ! 2338: }/* end IF had no active command */ ! 2339: ! 2340: /* There is an active request: */ ! 2341: /* get the stage of the CCL and Switch on it. */ ! 2342: stage = CCLWord( kcclStageLabel ); ! 2343: cmdBuf = gActiveCommand; ! 2344: scsiReq = cmdBuf->scsiReq; ! 2345: cclIndex = SWAP( dbdmaAddr->d_cmdptrlo ) ! 2346: - (UInt32) cclPhysAddr; ! 2347: CCLWord( kcclStageLabel ) = 0; ! 2348: ASSERT( scsiReq->target == gCurrentTarget && scsiReq->lun == gCurrentLUN ); ! 2349: ! 2350: /* Analyse where the DBDMA ended up: */ ! 2351: ! 2352: switch ( stage ) ! 2353: { ! 2354: case kcclStageGood: /* Normal completion */ ! 2355: [ self DoInterruptStageGood ]; ! 2356: break; ! 2357: ! 2358: case kcclStageInit: /* Value before DBDMA runs */ ! 2359: case kcclStageArb: /* Arbitration anomaly */ ! 2360: [ self DoInterruptStageArb ]; ! 2361: break; ! 2362: ! 2363: case kcclStageSelA: /* Selection anomaly */ ! 2364: [ self DoInterruptStageSelA ]; ! 2365: break; ! 2366: ! 2367: case kcclStageMsgO: /* Message Out */ ! 2368: [ self DoInterruptStageMsgO ]; ! 2369: break; ! 2370: ! 2371: case kcclStageCmdO: /* Command stage anomaly */ ! 2372: [ self DoInterruptStageCmdO ]; ! 2373: break; ! 2374: ! 2375: case kcclStageXfer: ! 2376: if ( cmdBuf->flagIsAutosense ) ! 2377: [ self DoInterruptStageXferAutosense ]; ! 2378: else [ self DoInterruptStageXfer ]; /* DMA transfer complete */ ! 2379: break; ! 2380: ! 2381: case kcclStageStat: /* Synchronous, odd transfer, data-out */ ! 2382: /* OR no data, disconnect */ ! 2383: ! 2384: /* Don't use UpdateCurrentIndex here because */ ! 2385: /* kcclStageStat destroys TC with a 1. */ ! 2386: count = CCLWord( kcclBatchSize ); /* Our transfer count */ ! 2387: cmdBuf->currentDataIndex += count; /* Increment data index */ ! 2388: if ( cmdBuf->mem ) ! 2389: [ cmdBuf->mem setPosition : cmdBuf->currentDataIndex ]; ! 2390: ! 2391: ELG( count, cmdBuf->currentDataIndex, 'Uidx', "ProcessInterrupt" ); ! 2392: CCLWord( kcclBatchSize ) = 0; /* Clear our count */ ! 2393: ! 2394: /* Analyze the current bus signals: */ ! 2395: ! 2396: if ( !(g.shadow.mesh.busStatus0 & kMeshReq) ) ! 2397: { /* Get here if Sync Read or Write is too short as */ ! 2398: /* in reading 512 bytes from a 2K block of CD-ROM. */ ! 2399: [ self StartBucket ]; ! 2400: return; ! 2401: }/* end IF no REQ signal */ ! 2402: ! 2403: phase = g.shadow.mesh.busStatus0 & kMeshPhaseMask; ! 2404: switch ( phase ) ! 2405: { ! 2406: case kBusPhaseMSGI: ! 2407: rc = [ self DoMessageInPhase ]; ! 2408: // if ( rc == IO_R_SUCCESS && gActiveCommand ) ! 2409: // break; /* msg processed ok & not disconnect */ ! 2410: /* Enable Exc (for Reselect) and Err interrupts (not CmdDone)*/ ! 2411: break; ! 2412: ! 2413: case kBusPhaseDATO: ! 2414: case kBusPhaseDATI: ! 2415: /* Get here if Async Read or Write is too short as */ ! 2416: /* in reading 512 bytes from a 2K block of CD-ROM */ ! 2417: [ self StartBucket ]; ! 2418: break; ! 2419: ! 2420: default: ! 2421: PAUSE( scsiReq->target, phase, 'pmm-', ! 2422: "ProcessInterrupt - expected Status phase.\n" ); ! 2423: break; ! 2424: }/* end SWITCH on phase */ ! 2425: break; ! 2426: ! 2427: case kcclStageBucket: ! 2428: count = CCLWord( kcclBatchSize ); /* Our transfer count */ ! 2429: cmdBuf->currentDataIndex += count; /* Increment data index */ ! 2430: if ( cmdBuf->mem ) ! 2431: [ cmdBuf->mem setPosition : cmdBuf->currentDataIndex ]; ! 2432: CCLWord( kcclBatchSize ) = 0; /* Clear our count */ ! 2433: ! 2434: ELG( count, cmdBuf->currentDataIndex, 'Buck', "ProcessInterrupt - bit bucket done.\n" ); ! 2435: ! 2436: // scsiReq->driverStatus = SR_IOST_DMAOR; /* set DMA OverRun error */ ! 2437: ! 2438: [ self SetSeqReg : kMeshFlushFIFO ]; /* flush the FIFO */ ! 2439: [ self RunDBDMA : kcclGetStatus stageLabel : kcclStageStat ]; ! 2440: break; ! 2441: ! 2442: case kcclStageMsgI: /* Message-in: */ ! 2443: case kcclStageFree: /* Bus free: */ ! 2444: default: /* Can't happen? */ ! 2445: PAUSE( cclIndex, stage, 'P i-', "ProcessInterrupt - strange or unknown interrupt for device.\n" ); ! 2446: break; ! 2447: }/* end SWITCH on Channel Program stage */ ! 2448: return; ! 2449: }/* end ProcessInterrupt */ ! 2450: ! 2451: ! 2452: /* The channel command (and, hence, the IO request) ran to */ ! 2453: /* completion without problems. Complete this IO request */ ! 2454: /* and try to start another. */ ! 2455: ! 2456: - (void) DoInterruptStageGood ! 2457: { ! 2458: register CommandBuffer *cmdBuf; ! 2459: register IOSCSIRequest *scsiReq; ! 2460: UInt32 totalXferLen; ! 2461: IOMemoryDescriptorState state; ! 2462: UInt8 byte; ! 2463: ! 2464: ! 2465: ASSERT( gActiveCmd && gActiveCmd->scsiReq ); ! 2466: ! 2467: // [ self SetSeqReg : kMeshEnableReselect ]; // done by CCL ! 2468: ! 2469: cmdBuf = gActiveCommand; ! 2470: scsiReq = cmdBuf->scsiReq; ! 2471: ! 2472: /* Retrieve the total number of bytes transferred */ ! 2473: /* in the last data phase. */ ! 2474: ! 2475: cmdBuf->flagRequestSelectOK = FALSE; ! 2476: totalXferLen = CCLWord( kcclBatchSize ); ! 2477: CCLWord( kcclBatchSize ) = 0; ! 2478: ! 2479: ELG( scsiReq, totalXferLen, 'Good', "DoInterruptStageGood" ); ! 2480: ! 2481: if ( cmdBuf->flagIsAutosense ) ! 2482: { ! 2483: /* We are completing an autosense command. */ ! 2484: /* Copy the status byte (which had better be */ ! 2485: /* "good" ) from the CCL to the autosense status*/ ! 2486: /* and complete the IO request. The autosense */ ! 2487: /* data itself was copied into the user buffer */ ! 2488: /* by a previous 'Xfer' interrupt. */ ! 2489: ! 2490: cmdBuf->autosenseStatus = CCLByte( kcclStatusData ); ! 2491: [ self deactivateCmd : cmdBuf ]; ! 2492: [ self ioComplete : cmdBuf ]; ! 2493: } ! 2494: else ! 2495: { /* We are completing a normal command. */ ! 2496: /* Update the transfer count and current data pointer. */ ! 2497: ! 2498: cmdBuf->currentDataIndex += totalXferLen; ! 2499: if ( cmdBuf->mem ) ! 2500: [ cmdBuf->mem setPosition : cmdBuf->currentDataIndex ]; ! 2501: ! 2502: if ( gFlagIncompleteDBDMA == FALSE ) ! 2503: { ! 2504: /* Yes, the IO is really complete: */ ! 2505: ! 2506: scsiReq->scsiStatus = CCLByte( kcclStatusData ); ! 2507: scsiReq->driverStatus = SR_IOST_GOOD; ! 2508: ! 2509: /* If this was an Inquiry, peek at the data */ ! 2510: /* for Synchronous and Queuing support: */ ! 2511: ! 2512: if ( (*(UInt8*)&scsiReq->cdb == kScsiCmdInquiry) ! 2513: && (cmdBuf->currentDataIndex > 7) ) ! 2514: { ! 2515: [ cmdBuf->mem state : &state ]; /* save context */ ! 2516: [ cmdBuf->mem setPosition : 7 ]; ! 2517: if ( [ cmdBuf->mem readFromClient : &byte count : 1 ] == 1 ) ! 2518: { ! 2519: gPerTargetData[ scsiReq->target ].inquiry_7 = byte; ! 2520: ELG( scsiReq->target, byte, 'Inq+', ! 2521: "DoInterruptStageGood - peek at Inquiry data" ); ! 2522: } ! 2523: [ cmdBuf->mem setState : &state ]; /* restore context */ ! 2524: } ! 2525: [ self deactivateCmd : cmdBuf ]; ! 2526: [ self ioComplete : cmdBuf ]; ! 2527: } ! 2528: else ! 2529: { /* The CCL ended, but the caller expected more data. */ ! 2530: /* Restart the CCL. */ ! 2531: /* Don't regenerate arbitration or command stuff. */ ! 2532: ! 2533: [ self UpdateCP : TRUE ]; ! 2534: [ self RunDBDMA : kcclDataXfer stageLabel : kcclStageXfer ]; ! 2535: return; ! 2536: }/* end ELSE need to continue Channel Program */ ! 2537: }/* end ELSE not AutoSense */ ! 2538: ! 2539: /* Since IO completed (otherwise, we would have exited in the */ ! 2540: /* "return" above), check whether a reselection attempt */ ! 2541: /* is piggy-backed on top of the good DBDMA completion. */ ! 2542: ! 2543: if ( g.shadow.mesh.exception & kMeshExcResel ) ! 2544: [ self HandleReselectionInterrupt ]; ! 2545: else /* Nothing happening. Try to start another request. */ ! 2546: { ! 2547: [ self selectNextRequest ]; ! 2548: if ( !gActiveCommand && queue_empty( &abortCmdQ ) ) ! 2549: /* Re-enable ints for reselect */ ! 2550: [ self SetIntMask : (kMeshIntrException | kMeshIntrError) ]; ! 2551: } ! 2552: ! 2553: return; ! 2554: }/* end DoInterruptStageGood */ ! 2555: ! 2556: ! 2557: /* Process the autosense data transfer phase. IO is not complete. */ ! 2558: /* There are several reasons why we might get here: */ ! 2559: /* -- autosense completion (which could be a separate stage) */ ! 2560: /* -- DMA completion with more DMA to do */ ! 2561: /* -- Bus phase mismatch (short transfer or disconnect) */ ! 2562: ! 2563: - (void) DoInterruptStageXferAutosense ! 2564: { ! 2565: register CommandBuffer *cmdBuf = gActiveCommand; ! 2566: IOSCSIRequest *scsiReq = cmdBuf->scsiReq; ! 2567: UInt32 residual; ! 2568: UInt32 count; ! 2569: ! 2570: ! 2571: cmdBuf->flagRequestSelectOK = FALSE; ! 2572: ! 2573: /* An autosense Data In transfer is complete. Copy the */ ! 2574: /* autosense data from our private buffer to the */ ! 2575: /* caller's sense_data area and restart IO to get the */ ! 2576: /* status and command-complete message byte. */ ! 2577: ! 2578: residual = SWAP( CCLWord( kcclSenseResult ) ) & 0xFF; ! 2579: count = kMaxAutosenseByteCount - residual; ! 2580: ASSERT( count <= sizeof( esense_reply_t ) ); ! 2581: bcopy( CCLAddress( kcclSenseBuffer ), &scsiReq->senseData, count ); ! 2582: ! 2583: /* Driver Kit does not return "sense valid" or */ ! 2584: /* the actual sense transfer count. */ ! 2585: /* Restart the channel command to fetch the */ ! 2586: /* status byte and Command Completion byte. */ ! 2587: ! 2588: [ self RunDBDMA : kcclGetStatus stageLabel : kcclStageStat ]; ! 2589: return; ! 2590: }/* end DoInterruptStageXferAutosense */ ! 2591: ! 2592: ! 2593: /* Process a normal data phase interrupt. IO is not complete. */ ! 2594: /* There are several reasons why we might get here: */ ! 2595: /* -- autosense completion (which could be a separate stage) */ ! 2596: /* -- DMA completion with more DMA to do */ ! 2597: /* -- Bus phase mismatch (short transfer or disconnect, MsgIn) */ ! 2598: /* Note that we know that we are not in autosense. */ ! 2599: ! 2600: - (void) DoInterruptStageXfer ! 2601: { ! 2602: register CommandBuffer *cmdBuf = gActiveCommand; ! 2603: UInt32 count; /* DMA transfer count */ ! 2604: UInt8 phase; /* Current bus phase */ ! 2605: IOReturn rc; ! 2606: int goAround; ! 2607: ! 2608: ! 2609: cmdBuf->flagRequestSelectOK = FALSE; ! 2610: count = cmdBuf->currentDataIndex; ! 2611: ! 2612: [ self UpdateCurrentIndex ]; ! 2613: ! 2614: do ! 2615: { goAround = FALSE; /* assume loop not repeated */ ! 2616: [ self SetSeqReg : kMeshFlushFIFO ]; ! 2617: ! 2618: /* We've cleaned up the mess from the previous data transfer. */ ! 2619: /* Look at the current bus phase. The channel command waited */ ! 2620: /* for REQ to be set before interrupting the processor. */ ! 2621: ! 2622: phase = g.shadow.mesh.busStatus0 & kMeshPhaseMask; ! 2623: ASSERT( g.shadow.mesh.busStatus0 & kMeshReq ); /* REQ is set, right? */ ! 2624: ! 2625: switch ( phase ) ! 2626: { ! 2627: case kBusPhaseSTS: ! 2628: gFlagIncompleteDBDMA = FALSE; /* indicate no-more-data */ ! 2629: [ self RunDBDMA : kcclGetStatus stageLabel : kcclStageStat ]; ! 2630: break; ! 2631: ! 2632: case kBusPhaseMSGI: ! 2633: rc = [ self DoMessageInPhase ]; ! 2634: if ( rc == IO_R_SUCCESS && gActiveCommand ) ! 2635: goAround = TRUE; /* msg ok & not disconnect */ ! 2636: break; ! 2637: ! 2638: case kBusPhaseDATO: ! 2639: case kBusPhaseDATI: ! 2640: if ( count != cmdBuf->currentDataIndex ) ! 2641: { /* Data phase had already started: */ ! 2642: PAUSE( 0, phase, 'dat-', "DoInterruptStageXfer - unexpected Data phase.\n" ); ! 2643: } ! 2644: else ! 2645: { /* try starting data phase again */ ! 2646: [ self RunDBDMA : kcclDataXfer stageLabel : kcclStageXfer ]; ! 2647: } ! 2648: break; ! 2649: ! 2650: default: ! 2651: PAUSE( cmdBuf->scsiReq->target, phase, 'Phs-', "DoInterruptStageXfer - bogus phase.\n" ); ! 2652: break; ! 2653: }/* end SWITCH on phase */ ! 2654: } while ( goAround ); ! 2655: return; ! 2656: }/* end DoInterruptStageXfer */ ! 2657: ! 2658: ! 2659: /* DoInterruptStageArb - Process an anomaly during arbitration. */ ! 2660: ! 2661: - (void) DoInterruptStageArb ! 2662: { ! 2663: ASSERT( gActiveCommand ); ! 2664: ELG( 0, 0, 'Arb-', "DoInterruptStageArb - Lost arbitration.\n" ); ! 2665: ! 2666: [ self pushbackCurrentRequest : gActiveCommand ]; ! 2667: ASSERT( gActiveCommand == NULL ); ! 2668: ! 2669: if ( g.shadow.mesh.exception & kMeshExcResel ) ! 2670: { if ( g.shadow.mesh.error & kMeshErrDisconnected ) ! 2671: { ! 2672: /* 18sep98 - Sometimes MESH gets real confused when its */ ! 2673: /* arbitration loses to a target's reselect arbitration. */ ! 2674: /* The registers show Exc:ArbLost, Resel and Err:UnExpDisc. */ ! 2675: /* The FIFO count is 1 (should be SCSI ID bits) while the */ ! 2676: /* BusStatus0,1 registers show IO and Sel both of which are */ ! 2677: /* set by the reselecting Target. */ ! 2678: /* The SCSI bus anaylzer shows the following events occcurring */ ! 2679: /* within a few microseconds of BSY being set by the target: */ ! 2680: /* bus free for at least hundreds of microseconds */ ! 2681: /* Target raises BSY along with its ID bit */ ! 2682: /* Target raises SEL */ ! 2683: /* Target raises IO to indicate reselection */ ! 2684: /* Target adds MESH's ID bit */ ! 2685: /* Target drops BSY */ ! 2686: /* MESH raises BSY to accept reselection */ ! 2687: /* **** MESH drops BSY **** here is where MESH is confused */ ! 2688: /* Target stays on bus for 250 milliseconds. */ ! 2689: /* To solve this problem, whack MESH with a RstMESH. */ ! 2690: ! 2691: ELG( ' Rst', 'MESH', 'UEP-', "DoInterruptStageArb - Resel/Unexpected Disconnect.\n" ); ! 2692: [ self SetSeqReg : kMeshResetMESH ]; /* completes quickly */ ! 2693: [ self GetHBARegsAndClear : TRUE ]; /* clear cmdDone */ ! 2694: [ self SetSeqReg : kMeshEnableReselect ]; ! 2695: [ self SetIntMask : kMeshIntrMask ]; /* Enable Interrupts */ ! 2696: return; /* now wait for another reselect interrupt */ ! 2697: } ! 2698: [ self HandleReselectionInterrupt ]; ! 2699: } ! 2700: else ! 2701: { /* 22sep97 - lost arbitration without reselection. */ ! 2702: /* Probably lost the reselect condition processing an */ ! 2703: /* error or something. */ ! 2704: ELG( 0, 0, 'ARB-', "DoInterruptStageArb - Lost arbitration without reselect.\n" ); ! 2705: } ! 2706: return; ! 2707: }/* end DoInterruptStageArb */ ! 2708: ! 2709: ! 2710: /* Process an anomaly during target selection. */ ! 2711: ! 2712: - (void) DoInterruptStageSelA ! 2713: { ! 2714: ASSERT( gActiveCommand ); ! 2715: [ self SetSeqReg : kMeshEnableReselect ]; ! 2716: [ self SetSeqReg : kMeshBusFreeCmd ]; /* clear ATN signal MESH left on */ ! 2717: [ self killCurrentRequest ]; ! 2718: [ self GetHBARegsAndClear : FALSE ]; /* check MESH registers */ ! 2719: [ self SetIntMask : kMeshIntrMask ]; /* Enable Interrupts */ ! 2720: return; ! 2721: }/* end DoInterruptStageSelA */ ! 2722: ! 2723: ! 2724: /* Process an anomaly during Message-Out phase. */ ! 2725: /* Target probably doing Message Reject (0x07). */ ! 2726: ! 2727: - (void) DoInterruptStageMsgO ! 2728: { ! 2729: UInt8 phase; ! 2730: IOReturn rc; ! 2731: ! 2732: ! 2733: ASSERT( gActiveCommand ); ! 2734: ! 2735: phase = g.shadow.mesh.busStatus0 & kMeshPhaseMask; /* phase me */ ! 2736: PAUSE( gActiveCommand, phase, 'Mgo-', ! 2737: "DoInterruptStageMsgO - error during msg-out phase.\n" ); ! 2738: ! 2739: switch ( phase ) ! 2740: { ! 2741: case kBusPhaseMSGI: ! 2742: rc = [ self DoMessageInPhase ]; ! 2743: if ( rc != IO_R_SUCCESS ) ! 2744: { ! 2745: PAUSE( 0, rc, ' MI-', ! 2746: "DoInterruptStageMsgO - MsgIn during MsgOut phase.\n" ); ! 2747: // ??? need to get to bus-free from here ! 2748: // ??? need to blow off the IO ! 2749: } ! 2750: else ! 2751: { ELG( 0, gMsgInFlag, 'rej?', "DoInterruptStageMsgO - got MsgIn.\n" ); ! 2752: if ( gMsgInFlag & kFlagMsgIn_Reject ) ! 2753: [ self AbortActiveCommand ]; ! 2754: } ! 2755: break; ! 2756: ! 2757: default: ! 2758: PAUSE( gMsgInFlag, phase, 'mgo-', ! 2759: "DoInterruptStageMsgO - unknown phase during MsgOut phase.\n" ); ! 2760: break; ! 2761: } ! 2762: return; ! 2763: }/* end DoInterruptStageMsgO */ ! 2764: ! 2765: ! 2766: /* DoInterruptStageCmdO - Process an anomaly during command stage. */ ! 2767: ! 2768: - (void) DoInterruptStageCmdO ! 2769: { ! 2770: register CommandBuffer *cmdBuf; ! 2771: UInt8 phase; ! 2772: IOReturn rc; ! 2773: ! 2774: ! 2775: /* See if this is part of the normal AbortTag/BusDeviceReset process: */ ! 2776: ! 2777: ! 2778: if ( !queue_empty( &abortCmdQ ) ) ! 2779: { ! 2780: [ self SetSeqReg : kMeshFlushFIFO ]; /* flush the FIFO */ ! 2781: cmdBuf = (CommandBuffer*)queue_first( &abortCmdQ ); ! 2782: ELG( cmdBuf, 0, 'Abo-', "DoInterruptStageCmdO - Aborting." ); ! 2783: queue_remove( &abortCmdQ, cmdBuf, CommandBuffer*, link ); ! 2784: [ self ioComplete : cmdBuf ]; ! 2785: [ self AbortDisconnectedCommand ]; /* do the next, if any */ ! 2786: return; ! 2787: } ! 2788: ! 2789: /* Not aborting - something bad happened: */ ! 2790: ! 2791: ASSERT( gActiveCommand ); ! 2792: cmdBuf = gActiveCommand; ! 2793: cmdBuf->flagRequestSelectOK = FALSE; ! 2794: phase = g.shadow.mesh.busStatus0 & kMeshPhaseMask; /* phase me */ ! 2795: ELG( cmdBuf, phase, 'CMD?', "DoInterruptStageCmdO - anomaly during Cmd phase.\n" ); ! 2796: ! 2797: if ( phase == kBusPhaseMSGI ) ! 2798: { /* We are probably negotiating SDTR or */ ! 2799: /* getting rejected on a nonzero LUN. */ ! 2800: rc = [ self DoMessageInPhase ]; ! 2801: if ( rc != IO_R_SUCCESS ) ! 2802: { ! 2803: PAUSE( 0, rc, ' mi-', ! 2804: "DoInterruptStageCmdO - MsgIn during Cmd phase.\n" ); ! 2805: } ! 2806: else ! 2807: { /* Message processed - where do we go from here? */ ! 2808: ! 2809: if ( !gActiveCommand ) /* if Rejected, */ ! 2810: return; /* return */ ! 2811: ! 2812: phase = g.shadow.mesh.busStatus0 & kMeshPhaseMask; ! 2813: switch ( phase ) ! 2814: { ! 2815: case kBusPhaseSTS: ! 2816: [ self RunDBDMA : kcclCmdoStage stageLabel : kcclStageInit ]; ! 2817: break; ! 2818: ! 2819: case kBusPhaseMSGO: ! 2820: msgOutPtr = (UInt8*)CCLAddress( kcclMSGOdata ); ! 2821: [ self SetupMsgO ]; ! 2822: [ self RunDBDMA : kcclMsgoStage stageLabel : kcclStageInit ]; ! 2823: break; ! 2824: ! 2825: case kBusPhaseCMD: ! 2826: [ self RunDBDMA : kcclCmdoStage stageLabel : kcclStageInit ]; ! 2827: break; ! 2828: } ! 2829: } ! 2830: } ! 2831: else if ( phase == kBusPhaseSTS ) /* Probably Check Condition */ ! 2832: { /* Perhaps block # invalid */ ! 2833: gFlagIncompleteDBDMA = FALSE; /* indicate no-more-data */ ! 2834: [ self RunDBDMA : kcclGetStatus stageLabel : kcclStageStat ]; ! 2835: } ! 2836: else ! 2837: { ! 2838: PAUSE( 0, phase, 'Phs?', "DoInterruptStageCmdO - error during Command phase.\n" ); ! 2839: } ! 2840: return; ! 2841: }/* end DoInterruptStageCmdO */ ! 2842: ! 2843: ! 2844: /* We are in MSGI phase. Read the bytes. Return TRUE if an entire */ ! 2845: /* message was read (we may still be in MSGI phase). Note that this */ ! 2846: /* is done by programmed IO, which will fail (logging the error) if */ ! 2847: /* the target sets MSGI but does not send us a message quickly enough. */ ! 2848: /* This method is called from the normal data transfer interrupt when */ ! 2849: /* the target enters message in phase, and from the reselection */ ! 2850: /* interrupt handler when we read a valid reselection target ID. */ ! 2851: /* Note that MESH interrupts are disabled on exit. */ ! 2852: ! 2853: - (IOReturn) DoMessageInPhase ! 2854: { ! 2855: register UInt8 messageByte; ! 2856: UInt32 index = 0; ! 2857: IOReturn ioReturn = IO_R_SUCCESS; ! 2858: ! 2859: ! 2860: /* We do not necessarily have a valid command in this method. */ ! 2861: /* While we're processing Message-In bytes, we don't want any */ ! 2862: /* MESH hardware interrupts. */ ! 2863: ! 2864: [ self SetIntMask : 0 ]; /* no MESH interrupt latching */ ! 2865: [ self SetSeqReg : kMeshFlushFIFO ]; /* Flush the FIFO */ ! 2866: ! 2867: gMsgInCount = 0; ! 2868: gMsgInState = kMsgInInit; ! 2869: ! 2870: while ( gMsgInState != kMsgInReady /* Disconnect makes gActiveCommand */ ! 2871: && ioReturn == IO_R_SUCCESS ) /* go away */ ! 2872: { ! 2873: meshAddr->transferCount1 = 0; ! 2874: meshAddr->transferCount0 = 1; /* get single byte */ ! 2875: [ self SetSeqReg : kMeshMessageInCmd ]; /* issue MsgIn */ ! 2876: ! 2877: ioReturn = [ self WaitForMesh : TRUE ]; /* wait for cmdDone */ ! 2878: if ( ioReturn != IO_R_SUCCESS ) ! 2879: { ! 2880: PAUSE( gCurrentTarget, ioReturn, 'Mgi-', "DoMessageInPhase - Target hung: message in timeout.\n" ); ! 2881: break; /* Bus reset here? */ ! 2882: } ! 2883: ! 2884: if ( (g.shadow.mesh.exception & kMeshExcPhaseMM) ! 2885: || (g.shadow.mesh.busStatus0 & kMeshPhaseMask) != kBusPhaseMSGI ) ! 2886: { ! 2887: break; /* exit loop if no longer in Msg-In phase */ ! 2888: } ! 2889: ! 2890: if ( g.shadow.mesh.FIFOCount == 0 ) ! 2891: { ! 2892: PAUSE( gCurrentTarget, 0, 'mgi-', "DoMessageInPhase - no message byte.\n" ); ! 2893: break; ! 2894: } ! 2895: ! 2896: messageByte = meshAddr->xFIFO; ! 2897: ! 2898: ASSERT( index < 256 ); ! 2899: gMsgInBuffer[ index++ ] = messageByte; ! 2900: ! 2901: switch ( gMsgInState ) ! 2902: { ! 2903: case kMsgInInit: ! 2904: /* This is the first message byte. Check for 1-byte codes. */ ! 2905: if ( messageByte == kScsiMsgCmdComplete ! 2906: || (messageByte >= kScsiMsgOneByteMin && messageByte <= kScsiMsgOneByteMax) ! 2907: || messageByte >= kScsiMsgIdentify ) ! 2908: { ! 2909: gMsgInState = kMsgInReady; ! 2910: } ! 2911: else if ( messageByte >= kScsiMsgTwoByteMin ! 2912: && messageByte <= kScsiMsgTwoByteMax ) ! 2913: { ! 2914: /* This is a two-byte message. */ ! 2915: /* Set the count and read the next byte. */ ! 2916: ! 2917: gMsgInState = kMsgInReading; /* Need one more */ ! 2918: gMsgInCount = 1; ! 2919: } ! 2920: else ! 2921: { /* This is an extended message. */ ! 2922: /* The next byte has the count. */ ! 2923: gMsgInState = kMsgInCounting; ! 2924: } ! 2925: break; ! 2926: ! 2927: case kMsgInCounting: /* Count byte of multi-byte message: */ ! 2928: gMsgInCount = messageByte; ! 2929: gMsgInState = kMsgInReading; ! 2930: break; ! 2931: ! 2932: case kMsgInReading: /* Body of multi-byte message: */ ! 2933: if ( --gMsgInCount <= 0 ) ! 2934: gMsgInState = kMsgInReady; ! 2935: break; ! 2936: ! 2937: default: ! 2938: ASSERT( gMsgInState ); /* Bogus state */ ! 2939: PAUSE( 0, 0, 'Msg-', "DoMessageInPhase - Bogus MSGI state!\n" ); ! 2940: gMsgInState = kMsgInReady; ! 2941: break; ! 2942: }/* end SWITCH on MSGI state */ ! 2943: ! 2944: if ( gMsgInState == kMsgInReady ) ! 2945: { ! 2946: [ self ProcessMSGI ]; ! 2947: gMsgInState = kMsgInInit; ! 2948: index = 0; ! 2949: if ( gMsgInBuffer[0] == kScsiMsgDisconnect ) ! 2950: ioReturn = IO_R_IO; /* break out of WHILE loop */ ! 2951: ! 2952: if ( gMsgInFlag & kFlagMsgIn_Reject ) ! 2953: { ! 2954: [ self AbortActiveCommand ]; ! 2955: break; ! 2956: } ! 2957: ! 2958: if ( gFlagReselecting ) ! 2959: break; /* Take Identify only - leave +ACK */ ! 2960: }/* end IF have a complete message-in to process */ ! 2961: }/* end WHILE there are more message bytes */ ! 2962: ! 2963: /***** If the target switches out of MSGI phase without *****/ ! 2964: /***** sending a complete message, we should do some *****/ ! 2965: /***** sort of error recovery. *****/ ! 2966: ! 2967: if ( gMsgInState != kMsgInInit ) ! 2968: { ! 2969: PAUSE( gCurrentTarget, gMsgInState, 'MGI-', "DoMessageInPhase - incomplete message.\n" ); ! 2970: if ( ioReturn == IO_R_SUCCESS ) ! 2971: ioReturn = IO_R_IO; /* General IO error */ ! 2972: } ! 2973: ! 2974: return ioReturn; ! 2975: }/* end DoMessageInPhase */ ! 2976: ! 2977: ! 2978: /* ProcessMSGI - DoMessageInPhase has read a complete message. */ ! 2979: /* Process it (this will probably change our internal state). */ ! 2980: ! 2981: - (void) ProcessMSGI ! 2982: { ! 2983: /* Note that, during reselection, we may not have */ ! 2984: /* a current target or LUN, nor possibly a valid command */ ! 2985: ! 2986: register CommandBuffer *cmdBuf; ! 2987: register IOSCSIRequest *scsiReq; ! 2988: ! 2989: UInt8 sdtr; ! 2990: UInt8 currentTarget, currentLUN; ! 2991: UInt8 period, offset; ! 2992: UInt8 targetResponse; /* responding or requesting? */ ! 2993: ! 2994: ! 2995: cmdBuf = gActiveCommand; /* May be NULL */ ! 2996: scsiReq = (cmdBuf == NULL) ? NULL : cmdBuf->scsiReq; ! 2997: if ( scsiReq ) ! 2998: { currentTarget = scsiReq->target; ! 2999: currentLUN = scsiReq->lun; ! 3000: ASSERT( currentTarget == gCurrentTarget && currentLUN == gCurrentLUN ); ! 3001: } ! 3002: else ! 3003: { currentTarget = gCurrentTarget; ! 3004: currentLUN = gCurrentLUN; ! 3005: } ! 3006: ! 3007: ELG( 0, *(UInt32*)gMsgInBuffer, '<Msg', "ProcessMSGI" ); ! 3008: ! 3009: switch ( gMsgInBuffer[0] ) ! 3010: { ! 3011: case kScsiMsgCmdComplete: ! 3012: if ( cmdBuf ) ! 3013: { ! 3014: /* This command is complete. Clear interrupts and */ ! 3015: /* allow subsequent MESH interrupts. Then tell the */ ! 3016: /* MESH to wait for the target to release the bus. */ ! 3017: ! 3018: [ self SetSeqReg : kMeshEnableReselect ]; ! 3019: [ self SetSeqReg : kMeshBusFreeCmd ]; /* cause Int */ ! 3020: ! 3021: if ( cmdBuf->flagIsAutosense == FALSE ) ! 3022: { ! 3023: if ( scsiReq ) ! 3024: scsiReq->scsiStatus = CCLByte( kcclStatusData ); ! 3025: ! 3026: /* Driver Kit does not return the command-complete byte. */ ! 3027: ! 3028: } ! 3029: [ self ioComplete : cmdBuf ]; ! 3030: } ! 3031: goto exit; /* Don't exit through the SWITCH end */ ! 3032: ! 3033: case kScsiMsgLinkedCmdComplete: ! 3034: case kScsiMsgLinkedCmdCompleteFlag: ! 3035: PAUSE( gCurrentTarget, 0, 'pmi-', "ProcessMSGI - linked command complete not supported.\n" ); ! 3036: [ self AbortActiveCommand ]; ! 3037: break; ! 3038: ! 3039: case kScsiMsgNop: ! 3040: break; ! 3041: ! 3042: case kScsiMsgRestorePointers: ! 3043: if ( cmdBuf ) ! 3044: { ! 3045: cmdBuf->currentDataIndex = cmdBuf->savedDataIndex; ! 3046: if ( cmdBuf->mem ) ! 3047: [ cmdBuf->mem setState : &cmdBuf->savedDataState ]; ! 3048: } ! 3049: break; ! 3050: ! 3051: case kScsiMsgSaveDataPointers: ! 3052: if ( cmdBuf ) ! 3053: { ! 3054: cmdBuf->savedDataIndex = cmdBuf->currentDataIndex; ! 3055: if ( cmdBuf->mem ) ! 3056: [ cmdBuf->mem state : &cmdBuf->savedDataState ]; ! 3057: } ! 3058: break; ! 3059: ! 3060: case kScsiMsgDisconnect: ! 3061: /* Driver Kit does not support automatic Save Data Pointers on */ ! 3062: /* Disconnect. */ ! 3063: /* Move this request to the disconnect queue, enable reselection, */ ! 3064: /* re-enable MESH interrupts, and wait (here) for bus free, but */ ! 3065: /* don't eat the interrupt. */ ! 3066: ! 3067: gMsgInFlag |= kFlagMsgIn_Disconnect; ! 3068: [ self disconnect ]; /* requeue active */ ! 3069: [ self SetSeqReg : kMeshEnableReselect ]; /* enable reselect */ ! 3070: [ self SetIntMask : kMeshIntrMask ]; /* Enable Ints */ ! 3071: [ self SetSeqReg : kMeshBusFreeCmd ]; /* issue BusFree */ ! 3072: ! 3073: /* wait for Bus Free command to complete: */ ! 3074: ! 3075: [ self WaitForMesh : FALSE ]; /* don't clear possible reselect */ ! 3076: ! 3077: /* Interrupt for bus-free now latched. Prevent a double interrupt, */ ! 3078: /* 1 from bus-free + 1 from reselect from occurring. */ ! 3079: /* This fixes the following BADNESS: */ ! 3080: /* Issue bus-free for disconnect. */ ! 3081: /* Interrupt occurs in microseconds - even before exiting */ ! 3082: /* "interruptOccurred" routine. */ ! 3083: /* Mach queues message to driverKit. */ ! 3084: /* Exit "interruptOccurred" routine. */ ! 3085: /* DriverKit dequeues and starts handling 1st Mach message. */ ! 3086: /* Interrupt occurs for reselect while driverKit running. */ ! 3087: /* Mach queues 2nd message to driverKit. */ ! 3088: /* DriverKit invokes MESH driver for 1st msg. */ ! 3089: /* MESH driver sees cmdDone fm bus-free AND reselect exception.*/ ! 3090: /* MESH driver handles reselect by setting up and running */ ! 3091: /* DBDMA. MESH driver exits. */ ! 3092: /* DriverKit invokes MESH driver with 2nd Mach message. */ ! 3093: /* MESH driver handles this as a DBDMA completion and royally */ ! 3094: /* screws up. */ ! 3095: ! 3096: g.intLevel |= kLevelLatched; /* set latched-interrupt flag */ ! 3097: [ self SetIntMask : 0 ]; /* prevent multiple MESH ints */ ! 3098: break; ! 3099: ! 3100: case kScsiMsgRejectMsg: ! 3101: ELG( currentTarget, gMsgOutFlag, 'Rej-', "ProcessMSGI - Reject." ); ! 3102: gMsgInFlag |= kFlagMsgIn_Reject; ! 3103: break; ! 3104: ! 3105: case kScsiMsgSimpleQueueTag: ! 3106: msgInTagType = gMsgInBuffer[0]; ! 3107: msgInTag = gMsgInBuffer[1]; ! 3108: ELG( 0, msgInTag, '=Tag', "Simple Queue Tag" ); ! 3109: break; ! 3110: ! 3111: case kScsiMsgExtended: ! 3112: ! 3113: /* Multi-byte message, presumably Synchronous Negotiation: */ ! 3114: ! 3115: switch ( gMsgInBuffer[ 2 ] ) /* switch on the msg code byte */ ! 3116: { ! 3117: case kScsiMsgSyncXferReq: /* handle sync negotiation: */ ! 3118: if ( scsiReq == NULL ) // ??? can this happen? ! 3119: { ! 3120: PAUSE( currentTarget, 0, 'pMI-', "ProcessMSGI - attempted to negotiate SDTR without a nexus.\n" ); ! 3121: [ self AbortActiveCommand ]; ! 3122: } ! 3123: else ! 3124: { /* Get period in nanoseconds */ ! 3125: period = gMsgInBuffer[ 3 ] * 4; /* SCSI uses 4ns granularity */ ! 3126: ! 3127: /* determine target responding or initiating? */ ! 3128: targetResponse = gPerTargetData[ scsiReq->target ].negotiateSDTR; ! 3129: gPerTargetData[ scsiReq->target ].negotiateSDTR = 0; ! 3130: if ( targetResponse ) ! 3131: { ! 3132: if ( gMsgInBuffer[ 4 ] == 0 ) /* check offset */ ! 3133: { ! 3134: sdtr = kSyncParmsAsync; /* Offset == 0 implies async */ ! 3135: } ! 3136: else /* synchronous: */ ! 3137: { ! 3138: if ( period == 100 ) /* special-case 100=FAST */ ! 3139: { ! 3140: sdtr = kSyncParmsFast & 0x0F; ! 3141: } ! 3142: else /* Older CD-ROMs get here. */ ! 3143: { /* The MESH manual says: */ ! 3144: /* period = 4 * clk + 2 * clk * P */ ! 3145: /* where: */ ! 3146: /* period is the target nanoseconds */ ! 3147: /* clk is the MESH clock rate which is */ ! 3148: /* 20 nanoseconds for a 50 MHz clock */ ! 3149: /* P is the 1-nibble period code we stuff */ ! 3150: /* in the syncParms register */ ! 3151: /* So: */ ! 3152: /* period = 4 * 20 + 2 * 20 * P */ ! 3153: /* period = 80 + 40 * P */ ! 3154: /* P = (period - 80) / 40 */ ! 3155: /* Since P must round up for safety: */ ! 3156: /* P = ((period - 80) + 39) / 40 */ ! 3157: /* P = (period - 41) / 40 */ ! 3158: /* A value of P == 3 results in 5 MB/s */ ! 3159: sdtr = (UInt8)((period - 41) / 40); ! 3160: } ! 3161: #ifdef CRAP ! 3162: /* If period is longer than 200 ns resulting */ ! 3163: /* in less than 5 MB/s, renegotiate async later.*/ ! 3164: if ( period >= 200 ) ! 3165: gPerTargetData[ scsiReq->target ].negotiateSDTR = kSyncParmsAsync; ! 3166: #endif /* CRAP */ ! 3167: }/* end ELSE have offset ergo Synchronous */ ! 3168: ! 3169: /* OR in the offset. */ ! 3170: sdtr |= (gMsgInBuffer[ 4 ] << 4); ! 3171: }/* end IF Target is responding to negotiation */ ! 3172: ! 3173: else /* target is initiating negotiation: */ ! 3174: { ! 3175: msgOutPtr = (UInt8*)CCLAddress( kcclMSGOdata ); ! 3176: *msgOutPtr++ = kScsiMsgExtended; /* 0x01 Ext Msg */ ! 3177: *msgOutPtr++ = 0x03; /* 0x03 Message Len */ ! 3178: *msgOutPtr++ = kScsiMsgSyncXferReq; /* 0x01 SDTR code */ ! 3179: offset = gMsgInBuffer[ 4 ]; ! 3180: if ( offset == 0 ) /* Offset == 0 means async: */ ! 3181: { ! 3182: *msgOutPtr++ = 0; /* clear period byte */ ! 3183: *msgOutPtr++ = 0; /* offset byte = 0 for async*/ ! 3184: sdtr = kSyncParmsAsync; /* set value for MESH reg */ ! 3185: } ! 3186: else /* have offset ergo sync: */ ! 3187: { ! 3188: if ( offset > 15 ) ! 3189: offset = 15; /* MESH can only handle 15 */ ! 3190: ! 3191: if ( period <= 100 ) /* special-case 100=FAST */ ! 3192: period = 100; ! 3193: else ! 3194: { /* round up to MESH's 40 ns granularity */ ! 3195: period = ((period + 39) / 40) * 40; ! 3196: } ! 3197: *msgOutPtr++ = period / 4; /* SCSI 4ns granularity */ ! 3198: *msgOutPtr++ = offset; ! 3199: sdtr = (offset << 8) | (UInt8)((period - 41) / 40); ! 3200: }/* end target is negotiating Sync */ ! 3201: /* respond to target: */ ! 3202: [ self RunDBDMA : kcclMsgoStage stageLabel : kcclStageInit ]; ! 3203: gPerTargetData[ scsiReq->target ].negotiateSDTR = 0; ! 3204: }/* end ELSE target is initiating negotiation */ ! 3205: ! 3206: meshAddr->syncParms = sdtr; ! 3207: SynchronizeIO(); ! 3208: gPerTargetData[ scsiReq->target ].syncParms = sdtr; ! 3209: ELG( *(UInt32*)&gMsgInBuffer[0], gMsgInBuffer[4]<<24 | sdtr, 'SDTR', "ProcessMSGI - SDTR" ); ! 3210: } /* end ELSE have a nexus */ ! 3211: break; ! 3212: ! 3213: default: ! 3214: PAUSE( currentTarget, gMsgInBuffer[0], 'PMi-', "ProcessMSGI - unsupported extended message.\n" ); ! 3215: [ self AbortActiveCommand ]; ! 3216: break; ! 3217: }/* end SWITCH on extended message code */ ! 3218: break; ! 3219: ! 3220: default: ! 3221: if ( gMsgInBuffer[0] >= kScsiMsgIdentify ) ! 3222: { ! 3223: ASSERT( gCurrentTarget != kInvalidTarget ); ! 3224: ASSERT( gCurrentLUN == kInvalidLUN ); ! 3225: gCurrentLUN = gMsgInBuffer[0] & kScsiMsgIdentifyLUNMask; ! 3226: currentLUN = gCurrentLUN; ! 3227: } ! 3228: else ! 3229: { ! 3230: PAUSE( currentTarget, gMsgInBuffer[0], 'mi -', "ProcessMSGI - unsupported message: rejected.\n" ); ! 3231: [ self AbortActiveCommand ]; ! 3232: } ! 3233: }/* end SWITCH on message selection */ ! 3234: ! 3235: exit: ! 3236: return; ! 3237: }/* end ProcessMSGI */ ! 3238: ! 3239: ! 3240: /* Process a reselection interrupt. */ ! 3241: ! 3242: - (void) HandleReselectionInterrupt ! 3243: { ! 3244: IOReturn ioReturn; ! 3245: ! 3246: ! 3247: ASSERT( gActiveCommand == NULL ); ! 3248: ! 3249: gFlagReselecting = TRUE; ! 3250: ! 3251: /* Sometimes MESH gives a bogus Disconnected error during Reselection. */ ! 3252: /* 31mar98 - Issuing an Abort message, causes "unexpected disconnect". */ ! 3253: /* When Err:UnexpDisc and Exc:Resel are simultaneously set, the */ ! 3254: /* busStatus0,1 registers may not be current. */ ! 3255: if ( g.shadow.mesh.error & kMeshErrDisconnected ) ! 3256: { ! 3257: [ self SetSeqReg : kMeshBusFreeCmd ]; ! 3258: [ self WaitForMesh : TRUE ]; // now maybe busStatus0,1 are live ! 3259: PAUSE( 0, 0, 'Dsc-', ! 3260: "HandleReselectionInterrupt: Caught disconnected glitch\n" ); ! 3261: }/* End IF bus disconnect error */ ! 3262: ! 3263: /* Read the target ID (which should be our initiator ID OR'd with the */ ! 3264: /* Target and the Identify byte with the reselecting LUN. Store this */ ! 3265: /* in gTargetID and gTargetLUN. Note that, during reselection, we will */ ! 3266: /* have a NULL gCurrentCommand and a valid gCurrentTarget and gCurrentLUN. */ ! 3267: /* If we get a valid reselection target, call the message in phase */ ! 3268: /* directly to read the LUN byte. */ ! 3269: /* @return TRUE if successful. */ ! 3270: ! 3271: msgInTag = 0; ! 3272: if ( meshAddr->FIFOCount == 0 ) ! 3273: { ! 3274: PAUSE( 0, 0, 'HRI-', "HandleReselectionInterrupt - Empty FIFO in reselection.\n" ); ! 3275: return; ! 3276: } ! 3277: else /* get the Target ID bit from the bus out of the FIFO */ ! 3278: { /* then, get the msg-in Identify byte for the LUN. */ ! 3279: if ( [ self getReselectionTargetID ] ) ! 3280: { ! 3281: if ( [ self DoMessageInPhase ] != IO_R_SUCCESS ) /* get Identify */ ! 3282: { ! 3283: PAUSE( 0, 0, 'Id -', "HandleReselectionInterrupt - Expected Identify byte after reselection.\n" ); ! 3284: } ! 3285: } ! 3286: else return; ! 3287: } ! 3288: ! 3289: /* Try to find an untagged command for this Target/LUN: */ ! 3290: ! 3291: ioReturn = [ self reselectNexus : gCurrentTarget ! 3292: lun : gCurrentLUN ! 3293: queueTag : 0 ]; ! 3294: ! 3295: if ( ioReturn != IO_R_SUCCESS ) ! 3296: { /* No untagged command, try to get a Tag. Hope that */ ! 3297: /* you're still in Message-In phase at this point. */ ! 3298: if ( [ self DoMessageInPhase ] != IO_R_SUCCESS ) /* get Tag msg */ ! 3299: { ! 3300: PAUSE( 0, 0, 'tag-', "HandleReselectionInterrupt - Expected tag message.\n" ); ! 3301: } ! 3302: ioReturn = [ self reselectNexus : gCurrentTarget ! 3303: lun : gCurrentLUN ! 3304: queueTag : msgInTag ]; ! 3305: } ! 3306: ! 3307: if ( ioReturn == IO_R_SUCCESS ) ! 3308: { ! 3309: ELG( gActiveCommand, ! 3310: (msgInTag<<16) | (gCurrentLUN<<8) | gCurrentTarget, ! 3311: 'Resl', "HandleReselectionInterrupt" ); ! 3312: /* If reselectNexus succeeded, gActiveCommand is set to the command.*/ ! 3313: /* Clear out the channel command results and build the channel */ ! 3314: /* command to continue operation. The TRUE flag prevents */ ! 3315: /* constructing an arbitrate/select/command sequence. */ ! 3316: ! 3317: [ self ClearCPResults ]; ! 3318: [ self UpdateCP : TRUE ]; ! 3319: [ self RunDBDMA : kcclReselect stageLabel : kcclStageInit ]; ! 3320: } ! 3321: else ! 3322: { /* There is no associated command. */ ! 3323: /* Reject the reselection attempt. */ ! 3324: /* This should cycle back to selectNextRequest. */ ! 3325: PAUSE( gCurrentTarget, msgInTag, 'Rsl-', ! 3326: "HandleReselectionInterrupt - No command for reselection attempt.\n" ); ! 3327: [ self AbortActiveCommand ]; ! 3328: } ! 3329: return; ! 3330: }/* end HandleReselectionInterrupt */ ! 3331: ! 3332: ! 3333: /* Validate the target's reselection byte (put on the bus before */ ! 3334: /* reselecting us). Erase the initiator ID and convert the other */ ! 3335: /* bit into an index. The algorithm should be faster than a */ ! 3336: /* sequential search, but it probably doesn't matter much. */ ! 3337: /* @return TRUE if successful (gCurrentTarget is now valid). */ ! 3338: /* This function does not check whether there actually */ ! 3339: /* is a command pending for this target. */ ! 3340: ! 3341: - (Boolean) getReselectionTargetID ! 3342: { ! 3343: Boolean success = FALSE; ! 3344: register UInt8 targetID = 0; ! 3345: register UInt8 bitValue = 0; /* Suppress warning */ ! 3346: register UInt8 targetBits; ! 3347: ! 3348: ! 3349: targetBits = meshAddr->xFIFO; /***** Read the FIFO *****/ ! 3350: targetBits &= ~gInitiatorIDMask; /* Remove our bit */ ! 3351: if ( targetBits ) ! 3352: { /* Is there another bit? */ ! 3353: bitValue = targetBits; ! 3354: if ( bitValue > 0x0F ) ! 3355: { ! 3356: targetID += 4; ! 3357: bitValue >>= 4; ! 3358: } ! 3359: if ( bitValue > 0x03 ) ! 3360: { ! 3361: targetID += 2; ! 3362: bitValue >>= 2; ! 3363: } ! 3364: if ( bitValue > 0x01 ) ! 3365: { ! 3366: targetID += 1; ! 3367: } ! 3368: targetBits &= ~(1 << targetID); /* Remove the target mask */ ! 3369: if ( targetBits == 0 ) ! 3370: { /* Was exactly one set? */ ! 3371: success = TRUE; /* Yes: success! */ ! 3372: gCurrentTarget = targetID; /* Save the current target */ ! 3373: } ! 3374: } ! 3375: ! 3376: if ( !success ) ! 3377: PAUSE( targetID, targetBits, 'rsl-', "getReselectionTargetID - Expected Identify byte after reselection.\n" ); ! 3378: ! 3379: return success; ! 3380: }/* end getReselectionTargetID */ ! 3381: ! 3382: ! 3383: @end /* AppleMesh_SCSI(MeshInterrupt) */ ! 3384: ! 3385: ! 3386: @implementation AppleMesh_SCSI( Mesh ) ! 3387: ! 3388: /* Reusable hardware initializer function. if resetSCSIBus is TRUE, */ ! 3389: /* this includes a SCSI reset. Handling of ioComplete of active and */ ! 3390: /* disconnected commands must be done elsewhere. Returns IO_R_SUCCESS. */ ! 3391: ! 3392: - (IOReturn) ResetMESH : (Boolean)resetSCSIBus ! 3393: { ! 3394: IOReturn ioReturn = IO_R_SUCCESS; ! 3395: UInt8 defaultSelectionTimeout = 25; // mlj ??? fix this value ! 3396: UInt8 target; ! 3397: ! 3398: ! 3399: /* Reset interrupts, the MESH Hardware Bus Adapter, and the DMA engine. */ ! 3400: ! 3401: // [ self SetIntMask : 0 ]; /* ResetMESH clrs interruptMask */ ! 3402: [ self SetSeqReg : kMeshResetMESH ]; /* completes quickly */ ! 3403: [ self GetHBARegsAndClear : TRUE ]; /* clear cmdDone */ ! 3404: ! 3405: dbdma_reset( DBDMA_MESH_SCSI ); ! 3406: ! 3407: /* Init state variables: */ ! 3408: ! 3409: gFlagIncompleteDBDMA = FALSE; ! 3410: // gBusState = SCS_DISCONNECTED; ! 3411: ! 3412: /* Smash all active command state (just in case): */ ! 3413: ! 3414: gActiveCommand = NULL; ! 3415: gCurrentTarget = kInvalidTarget; ! 3416: gCurrentLUN = kInvalidLUN; ! 3417: gMsgInState = kMsgInInit; ! 3418: msgOutPtr = (UInt8*)CCLAddress( kcclMSGOdata ); ! 3419: ! 3420: if ( resetSCSIBus ) ! 3421: { ! 3422: ASSERT( gInterruptNestingLevel > 0 ); ! 3423: meshAddr->busStatus1 = kMeshRst; /***** ASSERT RESET SIGNAL *****/ ! 3424: SynchronizeIO(); ! 3425: IODelay( 25 ); /* leave asserted for 25 mikes */ ! 3426: meshAddr->busStatus1 = 0; /***** CLEAR RESET SIGNAL *****/ ! 3427: SynchronizeIO(); ! 3428: ! 3429: /* Delay for 250 msec after resetting the bus. */ ! 3430: /* This serves two purposes: it gives the MESH time to */ ! 3431: /* stabilize (about 10 msec is sufficient) and gives */ ! 3432: /* some devices time to re-initialize themselves. */ ! 3433: ! 3434: IOSleep( APPLE_SCSI_RESET_DELAY ); /* Give Targets time to clean up */ ! 3435: [ self SetSeqReg : kMeshResetMESH ]; /* clear Err condition */ ! 3436: [ self GetHBARegsAndClear : TRUE ]; /* check regs */ ! 3437: ! 3438: for ( target = 0; target < SCSI_NTARGETS; target++ ) ! 3439: { ! 3440: gPerTargetData[ target ].syncParms = kSyncParmsAsync; ! 3441: gPerTargetData[ target ].negotiateSDTR = kSyncParmsFast; // negotiate Fast ! 3442: } ! 3443: }/* end IF resetSCSIBus */ ! 3444: ! 3445: meshAddr->selectionTimeOut = defaultSelectionTimeout; ! 3446: SynchronizeIO(); ! 3447: ! 3448: return ioReturn; ! 3449: }/* end ResetMESH */ ! 3450: ! 3451: ! 3452: /* Wait for an immediate (non-interrupting) command to complete. */ ! 3453: /* Note that it spins while waiting. It is timed to prevent a buggy */ ! 3454: /* chip or target from hanging the system. */ ! 3455: ! 3456: - (IOReturn) WaitForMesh : (Boolean) clearInterrupts ! 3457: { ! 3458: ns_time_t startTime, endTime; ! 3459: IOReturn ioReturn = IO_R_SUCCESS; ! 3460: #if USE_ELG ! 3461: UInt8 *logp = g.evLogBufp; ! 3462: #endif /* USE_ELG */ ! 3463: //#define WAIT_TIME (1000000000ULL) ! 3464: //#define WAIT_TIME (3000000ULL) // mlj - make it 3 milliseconds ! 3465: //#define WAIT_TIME 19000000 // mlj - make it 19 milliseconds for ZIP ! 3466: #define WAIT_TIME 250000000 // mlj - make it 250 milliseconds for SONY CD-ROM ! 3467: ! 3468: ! 3469: IOGetTimestamp( &startTime ); ! 3470: ! 3471: for ( g.shadow.mesh.interrupt = 0; g.shadow.mesh.interrupt == 0; ) ! 3472: { ! 3473: #if USE_ELG ! 3474: g.evLogBufp = logp; /* set back the log pointer */ ! 3475: #endif /* USE_ELG */ ! 3476: [ self GetHBARegsAndClear : clearInterrupts ]; ! 3477: ! 3478: IOGetTimestamp( &endTime ); ! 3479: if ( (endTime - startTime) >= WAIT_TIME ) ! 3480: { /* It took too long! We're dead. */ ! 3481: PAUSE( 0, 0, 'WFM-', "WaitForMesh - MESH chip does not respond to command.\n" ); ! 3482: ioReturn = IO_R_INTERNAL; ! 3483: break; ! 3484: } ! 3485: }/* end FOR */ ! 3486: ! 3487: return ioReturn; ! 3488: }/* end WaitForMesh */ ! 3489: ! 3490: ! 3491: /* WaitForReq - spins while waiting. It is timed to prevent a buggy */ ! 3492: /* chip or target from hanging the system. */ ! 3493: ! 3494: - (IOReturn) WaitForReq /* This method is currently unused. */ ! 3495: { ! 3496: ns_time_t startTime, endTime; ! 3497: IOReturn ioReturn = IO_R_SUCCESS; ! 3498: ! 3499: ! 3500: IOGetTimestamp( &startTime ); ! 3501: ! 3502: g.shadow.mesh.busStatus0 = 0; ! 3503: while ( (g.shadow.mesh.busStatus0 & kMeshReq) == 0 ) ! 3504: { ! 3505: [ self GetHBARegsAndClear : FALSE ]; ! 3506: ! 3507: IOGetTimestamp( &endTime ); ! 3508: if ( (endTime - startTime) >= 1000000000L ) ! 3509: { /* It took too long! */ ! 3510: PAUSE( endTime, startTime, 'WFR-', "WaitForReq - Target not in valid phase.\n" ); ! 3511: ioReturn = IO_R_INTERNAL; ! 3512: break; ! 3513: } ! 3514: if ( (endTime - startTime) >= 1000000L ! 3515: && (g.shadow.mesh.busStatus0 & kMeshReq) == 0 ) ! 3516: { ! 3517: IODelay( 1000 ); /* After 1 ms, start yielding time */ ! 3518: } ! 3519: }/* end WHILE REQ not set */ ! 3520: ! 3521: return ioReturn; ! 3522: }/* end WaitForReq */ ! 3523: ! 3524: ! 3525: /* Send a command to the MESH chip. This may cause an interrupt. */ ! 3526: ! 3527: - (void) SetSeqReg : (MeshCommand) meshCommand ! 3528: { ! 3529: ELG( (meshAddr->interruptMask<<16) | meshAddr->interrupt, meshCommand, '=Seq', "SetSeqReg" ); ! 3530: ! 3531: if ( meshAddr->interruptMask & kMeshIntrCmdDone ! 3532: && meshCommand <= kMeshBusFreeCmd ) ! 3533: ELG( meshAddr->interrupt, meshAddr->interruptMask, 'Trig', ! 3534: "SetSeqReg - may trigger interrupt.\n" ); ! 3535: ! 3536: meshAddr->sequence = (UInt8)meshCommand; /***** DO IT *****/ ! 3537: SynchronizeIO(); ! 3538: IODelay( 1 ); /* G3 is too fast */ ! 3539: ! 3540: return; ! 3541: }/* end SetSeqReg */ ! 3542: ! 3543: ! 3544: /* MESH chip self-test. (Minimal: it could be extended.) */ ! 3545: /* @return IO_R_SUCCESS if successful. */ ! 3546: ! 3547: - (IOReturn) DoHBASelfTest ! 3548: { ! 3549: IOReturn ioReturn = IO_R_SUCCESS; ! 3550: UInt8 tempByte; ! 3551: ! 3552: ! 3553: ELG( gMESHPhysAddr, meshAddr, 'MESH', "DoHBASelfTest" ); ! 3554: #ifdef CRAP ! 3555: if ( probe_rb( ((void*)((UInt32)gMESHPhysAddr) + kMeshMESHID) ) == 0 ) ! 3556: { ! 3557: PAUSE( 0, gMESHPhysAddr, 'HBA-', "DoHBASelfTest - Invalid MESH physical address.\n" ); ! 3558: ioReturn = IO_R_NO_DEVICE; ! 3559: } ! 3560: #else ! 3561: ASSERT( probe_rb( ((void*)((UInt32)gMESHPhysAddr) + kMeshMESHID) ) == 0 ); ! 3562: #endif /* CRAP */ ! 3563: ! 3564: if ( ioReturn == IO_R_SUCCESS ) ! 3565: { ! 3566: tempByte = meshAddr->MESHID & 0x1f; ! 3567: if ( tempByte < kMeshMESHID_Value ) ! 3568: { ! 3569: PAUSE( 0, tempByte, 'hba-', "DoHBASelfTest - Invalid MESH chip ID .\n" ); ! 3570: ioReturn = IO_R_NO_DEVICE; ! 3571: } ! 3572: } ! 3573: return ioReturn; ! 3574: }/* end DoHBASelfTest */ ! 3575: ! 3576: ! 3577: /* Start a Channel Program at the given offset */ ! 3578: /* with the specified stage label. */ ! 3579: ! 3580: - (void) RunDBDMA : (UInt32) offset stageLabel : (UInt32) stageLabel ! 3581: { ! 3582: register UInt8 intReg; ! 3583: ns_time_t arbEndTime, curTime; ! 3584: ! 3585: ! 3586: gMsgInFlag = 0; /* clear message-in flags. */ ! 3587: ! 3588: CCLWord( kcclStageLabel ) = stageLabel; /* set the stage */ ! 3589: ! 3590: /* Let MESH interrupt only for errors or exceptions, but not cmdDone */ ! 3591: [ self SetIntMask : (kMeshIntrException | kMeshIntrError) ]; ! 3592: ! 3593: intReg = meshAddr->interrupt; ! 3594: switch ( intReg ) ! 3595: { ! 3596: case kMeshIntrCmdDone: ! 3597: if ( !gFlagReselecting ) // ??? Don't drop ACK fm MSG-IN or Sync data flows ! 3598: /* clear any pending command interrupts (but not reselect et al) */ ! 3599: meshAddr->interrupt = kMeshIntrCmdDone; SynchronizeIO(); ! 3600: /***** fall through *****/ ! 3601: case 0: ! 3602: /* This is a Go: */ ! 3603: /* Flush any CCL and related data to the CCL physical page */ ! 3604: /* that may still be sitting in cache: */ ! 3605: flush_cache_v( (vm_offset_t)cclLogAddr, cclLogAddrSize ); ! 3606: //ELG( *(UInt32*)0xF3000020, 0, 'G C+', "RunDBDMA." ); ! 3607: //ELG( 0, *(UInt32*)0xF300002C, 'G C ', "RunDBDMA." ); ! 3608: ! 3609: if ( offset == kcclStart ) ! 3610: { ! 3611: gFlagReselecting = FALSE; ! 3612: [ self SetSeqReg : kMeshArbitrateCmd ]; /* ARBITRATE */ ! 3613: ! 3614: /* wait 50 mikes or cmdDone, whichever comes first: */ ! 3615: ! 3616: IOGetTimestamp( &arbEndTime ); ! 3617: arbEndTime += 50000; ! 3618: do ! 3619: { ! 3620: [ self GetHBARegsAndClear : FALSE ]; /* get regs without hosing */ ! 3621: IOGetTimestamp( &curTime ); ! 3622: }while ( !(g.shadow.mesh.interrupt & kMeshIntrCmdDone) && curTime < arbEndTime ); ! 3623: ! 3624: if ( g.shadow.mesh.interrupt == kMeshIntrCmdDone ) ! 3625: { /* No err, no exc: Arbitration won: */ ! 3626: meshAddr->interrupt = kMeshIntrCmdDone; ! 3627: SynchronizeIO(); ! 3628: [ self SetSeqReg : kMeshDisableReselect ]; /* disable reselect */ ! 3629: offset = 0x150; // ??? fix this. Point to Select/Atn ! 3630: CCLWord( kcclStageLabel ) = kcclStageArb; /* set stage to Arbitrate */ ! 3631: }/* end IF won Arbitration */ ! 3632: else /* Arbitration not won - CAUTION - HACK AHEAD: */ ! 3633: { /* Sometimes, MESH does not return ArbLost as it says in */ ! 3634: /* the documentation. Instead, it waits for the winner to */ ! 3635: /* get off the bus (usually after the 250 ms timeout) and */ ! 3636: /* then MESH continues its arbitration. This wastes 250 ms */ ! 3637: /* of valuable bus time. Further, IOmega's Zip drive has a */ ! 3638: /* nasty bug whereby if its reselection is snubbed and it */ ! 3639: /* times out, it leaves the I/O signal asserted on the bus */ ! 3640: /* even as other activity on the bus unrelated to the Zip */ ! 3641: /* is ongoing. */ ! 3642: /* We don't need to hack if ArbLost is indicated correctly */ ! 3643: /* or Reselect is indicated. If either is true, don't bother*/ ! 3644: /* starting the DBDMA; rather, let the interrupt already */ ! 3645: /* latched handle the situation. */ ! 3646: ! 3647: if ( !(g.shadow.mesh.exception & (kMeshExcArbLost | kMeshExcResel)) ) ! 3648: { ! 3649: ELG( '****', '****', 'HACK', "RunDBDMA - Arbitrate HACK." ); ! 3650: [ self SetSeqReg : kMeshResetMESH ]; /* hack it: whack it*/ ! 3651: [ self GetHBARegsAndClear : TRUE ]; /* get regs/preserve*/ ! 3652: [ self SetSeqReg : kMeshEnableReselect ]; /* Let reselect again*/ ! 3653: [ self GetHBARegsAndClear : FALSE ]; /* get regs/preserve*/ ! 3654: if ( g.shadow.mesh.interrupt == 0 ) ! 3655: PAUSE( 0, 0, 'Arb*', "RunDBDMA - Arbitrate/Reselect problem." ); ! 3656: } ! 3657: if ( g.shadow.mesh.interrupt ) /* If Err or Exc set: */ ! 3658: { g.intLevel |= kLevelLatched; /* set latched-interrupt flag. */ ! 3659: return; /* let pending Int clean up. */ ! 3660: } ! 3661: }/* end ELSE lost Arbitration */ ! 3662: }/* end IF DBDMA to start at Arbitrate */ ! 3663: ! 3664: [ self GetHBARegsAndClear : FALSE ]; // ??? debug: see if ACK still set ! 3665: ELG( 0, offset<<16 | stageLabel, 'DMA+', "RunDBDMA" ); ! 3666: dbdma_start( DBDMA_MESH_SCSI, (dbdma_command_t*)((UInt32)cclPhysAddr + offset) ); ! 3667: break; ! 3668: ! 3669: default: /* Err or Exc or both are set */ ! 3670: ELG( 'Err ', 'Exc ', 'Pnd-', "RunDBDMA - interrupt probably pending (reselect?)." ); ! 3671: [ self GetHBARegsAndClear : FALSE ]; // display without hosing ! 3672: }/* end SWITCH on interrupt register */ ! 3673: return; ! 3674: }/* end RunDBDMA */ ! 3675: ! 3676: ! 3677: /* Retrieve the MESH volatile register contents, */ ! 3678: /* storing them in the global register shadow. */ ! 3679: /* @param clearInts YES to clear MESH interrupts. */ ! 3680: ! 3681: - (void) GetHBARegsAndClear : (Boolean) clearInts ! 3682: { ! 3683: register MeshRegister *mesh = meshAddr; ! 3684: ! 3685: ! 3686: g.shadow.mesh.interrupt = mesh->interrupt; ! 3687: g.shadow.mesh.error = mesh->error; ! 3688: g.shadow.mesh.exception = mesh->exception; ! 3689: g.shadow.mesh.FIFOCount = mesh->FIFOCount; ! 3690: ! 3691: g.shadow.mesh.busStatus0 = mesh->busStatus0; ! 3692: g.shadow.mesh.busStatus1 = mesh->busStatus1; ! 3693: g.shadow.mesh.transferCount1 = mesh->transferCount1; ! 3694: g.shadow.mesh.transferCount0 = mesh->transferCount0; ! 3695: ! 3696: g.shadow.mesh.sequence = mesh->sequence; // debugging ! 3697: g.shadow.mesh.interruptMask = mesh->interruptMask; // debugging ! 3698: g.shadow.mesh.syncParms = mesh->syncParms; // debugging ! 3699: g.shadow.mesh.destinationID = mesh->destinationID; // debugging ! 3700: ! 3701: ELG( g.shadow.longWord[ 0 ], g.shadow.longWord[ 1 ], clearInts ? 'Regs' : 'regs', "GetHBARegsAndClear." ); ! 3702: ! 3703: if ( g.shadow.mesh.error ) // this occurs when dbdma -> Seq while reselect ! 3704: ELG( g.shadow.mesh.interruptMask, g.shadow.mesh.sequence, 'Err-', ! 3705: "GetHBARegsAndClear - MESH error detected" ); ! 3706: ! 3707: /* It is possible to have the Reselected bit set in the Exception */ ! 3708: /* register without an Exception bit in the interrupt register. */ ! 3709: /* This may be caused by timing window where we clear the interrupt */ ! 3710: /* register with the interrupt register instead of 0x07. */ ! 3711: /* Handle this by faking an exception. */ ! 3712: /* 04may98 - it is also possible to have PhaseMisMatch set in the */ ! 3713: /* Exception register without Exception indicated in the Interrupt */ ! 3714: /* register. This happened when a Synchronous output finished and */ ! 3715: /* the target went to Message-In phase with Save-Data-Pointer. */ ! 3716: ! 3717: ! 3718: if ( g.shadow.mesh.exception ) ! 3719: g.shadow.mesh.interrupt |= kMeshIntrException; ! 3720: ! 3721: if ( clearInts && g.shadow.mesh.interrupt ) ! 3722: { ! 3723: mesh->interrupt = g.shadow.mesh.interrupt; ! 3724: SynchronizeIO(); ! 3725: } ! 3726: return; ! 3727: }/* end GetHBARegsAndClear */ ! 3728: ! 3729: ! 3730: - (void) SetIntMask : (UInt8) mask ! 3731: { ! 3732: ELG( (meshAddr->interrupt<<16) | meshAddr->interruptMask, mask, 'Mask', "SetIntMask" ); ! 3733: meshAddr->interruptMask = mask; /* enable whatever */ ! 3734: SynchronizeIO(); ! 3735: return; ! 3736: }/* end SetIntMask */ ! 3737: ! 3738: ! 3739: - (void) AbortActiveCommand ! 3740: { ! 3741: IOReturn ioReturn; ! 3742: ! 3743: ! 3744: ELG( gActiveCommand, 0, '-AB*', "AbortActiveCommand" ); ! 3745: [ self GetHBARegsAndClear : TRUE ]; /* clear possible cmdDone et al */ ! 3746: [ self SetIntMask : 0 ]; /* Disable MESH interrupts */ ! 3747: ! 3748: gMsgInFlag = 0; /* clear kFlagMsgIn_Reject et al */ ! 3749: ! 3750: meshAddr->busStatus0 = kMeshAtn; /***** Raise ATN signal *****/ ! 3751: SynchronizeIO(); ! 3752: ! 3753: [ self SetSeqReg : kMeshBusFreeCmd ]; /* clear ACK */ ! 3754: [ self WaitForMesh : TRUE ]; /* wait for PhaseMM */ ! 3755: ! 3756: if ( (g.shadow.mesh.busStatus0 & (kMeshPhaseMask | kMeshReq)) ! 3757: == (kBusPhaseMSGO | kMeshReq) ) ! 3758: { /* this is what we want: */ ! 3759: [ self SetSeqReg : kMeshFlushFIFO ]; /* Flush the FIFO */ ! 3760: meshAddr->transferCount0 = 1; /* set TC low = 1 */ ! 3761: meshAddr->transferCount1 = 0; ! 3762: meshAddr->busStatus0 = 0; /***** clear ATN signal *****/ ! 3763: SynchronizeIO(); ! 3764: ! 3765: /* Issue the Message Out sending the Abort on its way. */ ! 3766: /* Note that this will cause an Unexpected-Disconnect. */ ! 3767: [ self SetSeqReg : kMeshMessageOutCmd ]; /* drop ATN signal */ ! 3768: meshAddr->xFIFO = kScsiMsgAbort; /* put out the Abort byte */ ! 3769: ioReturn = [ self WaitForMesh : TRUE ]; /* wait for cmdDone */ ! 3770: if ( ioReturn == IO_R_SUCCESS ) ! 3771: { ! 3772: [ self SetSeqReg : kMeshEnableReselect ];/* bus about to go free */ ! 3773: [ self SetIntMask : kMeshIntrMask ]; /* Enable interrupts */ ! 3774: [ self SetSeqReg : kMeshBusFreeCmd ]; /* Clr ACK & go Bus-Free */ ! 3775: g.intLevel |= kLevelLatched; /* set latched-int flag */ ! 3776: return; ! 3777: } ! 3778: }/* end IF MSGO phase and REQ is set */ ! 3779: ! 3780: /***** USE THE HAMMER - NUKE THE BUS: *****/ ! 3781: ! 3782: ELG( 0, 0, '-AB-', "AbortActiveCommand - target refused to enter MSGO phase" ); ! 3783: [ self ResetHardware : TRUE ]; ! 3784: return; ! 3785: }/* end AbortActiveCommand */ ! 3786: ! 3787: ! 3788: - (void) AbortDisconnectedCommand ! 3789: { ! 3790: CommandBuffer *cmdBuf; ! 3791: IOSCSIRequest *scsiReq; ! 3792: UInt8 msgByte; ! 3793: ! 3794: ! 3795: if ( !queue_empty( &abortCmdQ ) ) ! 3796: { ! 3797: cmdBuf = (CommandBuffer*)queue_first( &abortCmdQ ); ! 3798: scsiReq = cmdBuf->scsiReq; ! 3799: meshAddr->destinationID = scsiReq->target; ! 3800: msgOutPtr = (UInt8*)CCLAddress( kcclMSGOdata ); ! 3801: msgByte = kScsiMsgIdentify | scsiReq->lun; ! 3802: *msgOutPtr++ = msgByte; ! 3803: if ( cmdBuf->queueTag ) ! 3804: { /* Tagged command: */ ! 3805: *msgOutPtr++ = kScsiMsgSimpleQueueTag; ! 3806: *msgOutPtr++ = cmdBuf->queueTag; ! 3807: *msgOutPtr++ = kScsiMsgAbortTag; ! 3808: ELG( cmdBuf, cmdBuf->queueTag, 'AbT-', "AbortDisconnectedCommand - Tag" ); ! 3809: } ! 3810: else ! 3811: { /* Untagged command: */ ! 3812: *msgOutPtr++ = kScsiMsgAbort; ! 3813: ELG( cmdBuf, 0, 'AbU-', "AbortDisconnectedCommand - Abort (untagged)" ); ! 3814: } ! 3815: [ self SetupMsgO ]; /* Setup for Message Out phase. */ ! 3816: ! 3817: [ self RunDBDMA : kcclStart stageLabel : kcclStageInit ]; ! 3818: } ! 3819: return; ! 3820: }/* end AbortDisconnectedCommand */ ! 3821: ! 3822: ! 3823: - (void) logTimestamp : (const char*) reason ! 3824: { ! 3825: #if DEBUG ! 3826: /* kMaxTimestamp should be greater than twice the expected method depth */ ! 3827: /* since, if we dump the timestamp after it has wrapped around, we expect */ ! 3828: /* to lose earlier entries and, hence, the shallower method starts. */ ! 3829: #ifndef kMaxTimestampStack ! 3830: #define kMaxTimestampStack 64 ! 3831: #endif ! 3832: ! 3833: TimestampDataRecord stack[ kMaxTimestampStack + 1 ]; /* Allocate one extra */ ! 3834: UInt32 index = 0; ! 3835: int start; ! 3836: UInt32 count = 0; ! 3837: UInt32 maxDepth = 0; ! 3838: Boolean wasEnabled, unused; ! 3839: char work[ 8 ]; ! 3840: struct timeval tv; ! 3841: ns_time_t lastEventTime; ! 3842: UInt32 elapsed; ! 3843: UInt32 sinceMethodStart; ! 3844: ! 3845: ! 3846: if ( reason ) ! 3847: { ! 3848: IOLog( "%s: *** Log timestamp: %s\n", [ self name ], reason ); ! 3849: } ! 3850: ! 3851: /* In case something we call causes timestamping, */ ! 3852: /* we want to avoid getting into an infinite loop. */ ! 3853: ! 3854: wasEnabled = EnableTimestamp( FALSE ); ! 3855: lastEventTime = 0; ! 3856: while ( ReadTimestamp( &stack[ index ] ) ) ! 3857: { ! 3858: ++count; ! 3859: work[0] = stack[ index ].timestampTag >> 24 & 0xFF; ! 3860: work[1] = stack[ index ].timestampTag >> 16 & 0xFF; ! 3861: work[2] = stack[ index ].timestampTag >> 8 & 0xFF; ! 3862: work[3] = stack[ index ].timestampTag >> 0 & 0xFF; ! 3863: work[4] = '\0'; ! 3864: ! 3865: elapsed = (unsigned)stack[ index ].eventTime - lastEventTime; ! 3866: lastEventTime = stack[ index ].eventTime; ! 3867: ns_time_to_timeval( stack[ index ].eventTime, &tv ); ! 3868: ! 3869: switch ( work[0] ) ! 3870: { ! 3871: case '+': /* Entering a method */ ! 3872: IOLog( "%s: '%s' %u.%06u %u.%03u 0.0 %d\n", ! 3873: [ self name ], work, ! 3874: tv.tv_sec, tv.tv_usec, ! 3875: elapsed / 1000, elapsed - ((elapsed / 1000) * 1000), ! 3876: stack[ index ].timestampValue ); ! 3877: if ( index < kMaxTimestampStack ) ! 3878: { if ( ++index > maxDepth ) ! 3879: maxDepth = index; ! 3880: } ! 3881: break; ! 3882: ! 3883: case '=': /* Intermediate tag: find the method start */ ! 3884: case '-': /* End of method: find the method start */ ! 3885: sinceMethodStart = 0; ! 3886: for ( start = index - 1; start >= 0; --start ) ! 3887: { ! 3888: if ( (stack[ start ].timestampTag & 0x00FFFFFF) ! 3889: == (stack[ index ].timestampTag & 0x00FFFFFF) ) ! 3890: { ! 3891: sinceMethodStart = (unsigned)stack[ index ].eventTime ! 3892: - stack[ start ].eventTime; ! 3893: break; ! 3894: } ! 3895: } ! 3896: IOLog( "%s: '%s' %u.%06u %u.%03u %u.%03u %d\n", ! 3897: [ self name ], work, ! 3898: tv.tv_sec, tv.tv_usec, ! 3899: elapsed / 1000, elapsed - ((elapsed / 1000) * 1000), ! 3900: sinceMethodStart / 1000, ! 3901: sinceMethodStart - ((sinceMethodStart / 1000) * 1000), ! 3902: stack[ index ].timestampValue ); ! 3903: if ( start >= 0 && work[0] == '-' ) ! 3904: index = start; /* Pop the stack */ ! 3905: break; ! 3906: ! 3907: default: ! 3908: IOLog( "%s: '%s' %u.%06u %u.%03u 0.0 %d _NoNestMark_\n", ! 3909: [ self name ], ! 3910: work, ! 3911: tv.tv_sec, ! 3912: tv.tv_usec, ! 3913: elapsed / 1000, ! 3914: elapsed - ((elapsed / 1000) * 1000), ! 3915: stack[ index ].timestampValue ); ! 3916: break; ! 3917: } ! 3918: }/* end WHILE */ ! 3919: ! 3920: IOLog( "%s: *** %d timestamps, %d max method depth\n", ! 3921: [ self name ], count, maxDepth ); ! 3922: unused = EnableTimestamp( wasEnabled ); ! 3923: #endif /* DEBUG */ ! 3924: return; ! 3925: }/* end logTimestamp */ ! 3926: ! 3927: @end /* AppleMesh_SCSI(Mesh) */ ! 3928: ! 3929: ! 3930: @implementation AppleMesh_SCSI( Private ) ! 3931: ! 3932: /* Private chip- and architecture-independent methods. */ ! 3933: ! 3934: /* Pass one CommandBuffer to the IO thread; wait for completion. */ ! 3935: /* (We are called on the client's execution thread.) */ ! 3936: /* Normal completion status is in cmdBuf->scsiReq->driverStatus; */ ! 3937: /* a non-zero return from this function indicates a Mach IPC error. */ ! 3938: /* This method allocates and frees cmdBuf->cmdLock. */ ! 3939: ! 3940: - (IOReturn) executeCmdBuf : (CommandBuffer*) cmdBuf ! 3941: { ! 3942: msg_header_t msg = cmdMessageTemplate; ! 3943: kern_return_t kernelReturn; ! 3944: IOReturn ioReturn = IO_R_SUCCESS; ! 3945: ! 3946: ! 3947: cmdBuf->flagActive = 0; ! 3948: cmdBuf->cmdLock = [ [ NXConditionLock alloc ] initWith : CMD_PENDING ]; ! 3949: [ incomingCmdLock lock ]; ! 3950: queue_enter( &incomingCmdQ, cmdBuf, CommandBuffer*, link ); ! 3951: [ incomingCmdLock unlock ]; ! 3952: ELG( cmdBuf, *(UInt32*)&incomingCmdQ, 'ExeC', "executeCmdBuf" ); ! 3953: ! 3954: /* Create a Mach message and send it in order to wake up the IO thread: */ ! 3955: ! 3956: msg.msg_remote_port = gKernelInterruptPort; ! 3957: kernelReturn = msg_send_from_kernel( &msg, MSG_OPTION_NONE, 0 ); ! 3958: if ( kernelReturn != KERN_SUCCESS ) ! 3959: { ! 3960: PAUSE( 0, kernelReturn, 'exe-', "executeCmdBuf - msg_send_from_kernel() error status .\n" ); ! 3961: ioReturn = IO_R_IPC_FAILURE; ! 3962: } ! 3963: else /* Wait for IO complete: */ ! 3964: { ! 3965: [ cmdBuf->cmdLock lockWhen : CMD_COMPLETE ]; ! 3966: } ! 3967: ! 3968: [ cmdBuf->cmdLock free ]; ! 3969: return ioReturn; ! 3970: }/* end executeCmdBuf */ ! 3971: ! 3972: ! 3973: /* Abort all active and disconnected commands with specified status. */ ! 3974: /* No hardware action. Currently used by threadResetBus and during */ ! 3975: /* processing of a kCommandAbortRequest command. */ ! 3976: ! 3977: - (void) abortAllCommands : (sc_status_t)status ! 3978: { ! 3979: ELG( 0, status, 'AbAl', "abortAllCommands" ); ! 3980: [ incomingCmdLock lock ]; ! 3981: ! 3982: [ self killActiveCommand : status ]; ! 3983: ! 3984: [ self killQueue : &abortCmdQ finalStatus : status ]; ! 3985: [ self killQueue : &disconnectedCmdQ finalStatus : status ]; ! 3986: [ self killQueue : &pendingCmdQ finalStatus : status ]; ! 3987: [ self killQueue : &incomingCmdQ finalStatus : status ]; ! 3988: ! 3989: [ incomingCmdLock unlock ]; ! 3990: return; ! 3991: }/* end abortAllCommands */ ! 3992: ! 3993: ! 3994: /* Abort all active and disconnected commands with status SR_IOST_RESET. */ ! 3995: /* Reset hardware and SCSI bus. */ ! 3996: /* If there is a command in pendingCmdQ, start it up. */ ! 3997: ! 3998: - (void) threadResetBus : (const char*) reason ! 3999: { ! 4000: [ self abortAllCommands : SR_IOST_RESET ]; ! 4001: [ self ResetHardware : TRUE ]; /* Reset SCSI and chip */ ! 4002: [ self selectNextRequest ]; /* This restarts processing commands */ ! 4003: return; ! 4004: }/* end threadResetBus */ ! 4005: ! 4006: ! 4007: /* Commence processing of the specified command. This is called by */ ! 4008: /* commandRequestOccurred when it receives a kCommandExecute message */ ! 4009: /* from IODirectDevice. There is a new SCSI request. Either start it */ ! 4010: /* now, or add it to the end of our pending request queue. */ ! 4011: ! 4012: - (void) threadExecuteRequest : (CommandBuffer*) cmdBuf ! 4013: { ! 4014: HardwareStartResult rc; ! 4015: ! 4016: ! 4017: if ( gActiveCommand || (g.intLevel & kLevelLatched) ) ! 4018: { ! 4019: /* We are currently executing a request. */ ! 4020: ! 4021: ELG( gActiveCommand, cmdBuf, 'Busy', "threadExecuteRequest - bus busy so queue this cmd" ); ! 4022: queue_enter( &pendingCmdQ, cmdBuf, CommandBuffer*, link ); ! 4023: } ! 4024: else if ( [ self commandCanBeStarted : cmdBuf ] == FALSE ) ! 4025: { ! 4026: /* This request can't be started right now (perhaps the */ ! 4027: /* target's tagged command limit has been reached). */ ! 4028: ! 4029: ELG( cmdBuf, 0, 'qFul', "threadExecuteRequest - can't start cmd so queue this cmd" ); ! 4030: queue_enter( &pendingCmdQ, cmdBuf, CommandBuffer*, link ); ! 4031: } ! 4032: else ! 4033: { /* Apparently, we can start this request. Call the hardware layer. */ ! 4034: ! 4035: rc = [ self hardwareStart : cmdBuf ]; ! 4036: switch ( rc ) ! 4037: { ! 4038: case kHardwareStartOK: /* Command started correctly */ ! 4039: case kHardwareStartBusy: /* Hardware can't start now */ ! 4040: break; ! 4041: case kHardwareStartRejected: /* Command rejected, try another */ ! 4042: [ self selectNextRequest ]; /* Try another command */ ! 4043: } ! 4044: } ! 4045: return; ! 4046: }/* end threadExecuteRequest */ ! 4047: ! 4048: ! 4049: /* Called when a transaction associated with cmdBuf is complete. */ ! 4050: /* Notify waiting thread. If cmdBuf->scsiReq exists (i.e., this */ ! 4051: /* is not a reset or an abort), scsiReq->driverStatus must be valid.*/ ! 4052: /* If cmdBuf is active, caller must remove from gActiveCommand. */ ! 4053: /* We decrement activeArray[][] counter if appropriate. */ ! 4054: ! 4055: - (void) ioComplete : (CommandBuffer*) cmdBuf ! 4056: { ! 4057: ns_time_t currentTime; ! 4058: IOSCSIRequest *scsiReq; ! 4059: ! 4060: ! 4061: ASSERT( cmdBuf ); ! 4062: ELG( cmdBuf->scsiReq, cmdBuf->scsiReq->driverStatus, ' IOC', "ioComplete" ); ! 4063: ! 4064: if ( cmdBuf == gActiveCommand ) ! 4065: [ self deactivateCmd : cmdBuf ]; ! 4066: ! 4067: scsiReq = cmdBuf->scsiReq; ! 4068: if ( scsiReq ) ! 4069: { ! 4070: IOGetTimestamp( ¤tTime ); ! 4071: scsiReq->totalTime = currentTime - cmdBuf->startTime; ! 4072: scsiReq->bytesTransferred = cmdBuf->currentDataIndex; ! 4073: ! 4074: /* Catch bad SCSI status now. */ ! 4075: ! 4076: if ( scsiReq->driverStatus == SR_IOST_GOOD ) ! 4077: { ! 4078: if ( cmdBuf->flagIsAutosense ) ! 4079: { ! 4080: /* We are completing an autosense command. Don't touch */ ! 4081: /* the request status (it should still be Check Condition). */ ! 4082: /* Queue full is a real problem. */ ! 4083: ! 4084: ASSERT( scsiReq->scsiStatus == kScsiStatusCheckCondition ); ! 4085: switch ( cmdBuf->autosenseStatus ) ! 4086: { ! 4087: case kScsiStatusGood: ! 4088: scsiReq->driverStatus = SR_IOST_CHKSV; ! 4089: break; ! 4090: ! 4091: case kScsiStatusQueueFull: ! 4092: if ( [ self pushbackFullTargetQueue : cmdBuf ] == SR_IOST_GOOD) ! 4093: { ! 4094: return; /* We'll try this one again */ ! 4095: } ! 4096: /* Fall through to failure */ ! 4097: ! 4098: default: ! 4099: scsiReq->driverStatus = SR_IOST_CHKSNV; ! 4100: break; ! 4101: } ! 4102: } ! 4103: else /* not AutoSense: */ ! 4104: { ! 4105: switch ( scsiReq->scsiStatus ) ! 4106: { ! 4107: case kScsiStatusGood: ! 4108: break; ! 4109: ! 4110: case kScsiStatusCheckCondition: ! 4111: ! 4112: /***** The 386 hardware suppresses autosense for ****/ ! 4113: /***** Test Unit Ready to avoid request sense ****/ ! 4114: /***** when polling for removable devices. This ****/ ! 4115: /***** should be the caller's decision. ****/ ! 4116: ! 4117: ELG( 0, 0, 'Chek', "ioComplete - Check Condition" ); ! 4118: if ( gOptionAutoSenseEnable ! 4119: && (scsiReq->ignoreChkcond == FALSE) ) ! 4120: { ! 4121: cmdBuf->flagIsAutosense = 1;/* We're doing autosense */ ! 4122: queue_enter_first( &pendingCmdQ, cmdBuf, CommandBuffer*, link ); ! 4123: return; ! 4124: } ! 4125: else ! 4126: { /* This command failed and we aren't doing autosense. */ ! 4127: scsiReq->driverStatus = SR_IOST_CHKSNV; ! 4128: } ! 4129: break; ! 4130: ! 4131: case kScsiStatusQueueFull: ! 4132: if ( [ self pushbackFullTargetQueue : cmdBuf ] == SR_IOST_GOOD) ! 4133: { ! 4134: return; ! 4135: } ! 4136: /* Huh? we weren't doing tagged queuing, fall through */ ! 4137: ! 4138: default: ! 4139: scsiReq->driverStatus = SR_IOST_BADST; ! 4140: break; ! 4141: }/* end SWITCH on SCSI status */ ! 4142: ! 4143: }/* end IF driverStatus is SR_IOST_GOOD */ ! 4144: }/* end IF not autosense */ ! 4145: }/* end IF have scsiReq */ ! 4146: ! 4147: if ( cmdBuf->flagActive ) ! 4148: { /* Note that the active flag is false for non-kCommandExecute */ ! 4149: /* commands and commands aborted from pendingCmdQ. */ ! 4150: ! 4151: ASSERT( cmdBuf == gActiveCommand ); ! 4152: [ self deactivateCmd : cmdBuf ]; ! 4153: } ! 4154: ! 4155: [ cmdBuf->cmdLock lock ]; ! 4156: [ cmdBuf->cmdLock unlockWith : YES ]; ! 4157: ! 4158: return; ! 4159: }/* end ioComplete */ ! 4160: ! 4161: ! 4162: /* A target reported a full queue. Push this command back */ ! 4163: /* on the pending queue and try it again, later. */ ! 4164: /* Return SR_IOST_GOOD if successful, SR_IOST_BADST on failure. */ ! 4165: ! 4166: - (sc_status_t) pushbackFullTargetQueue : (CommandBuffer*) cmdBuf ! 4167: { ! 4168: IOSCSIRequest *scsiReq; ! 4169: int target, lun; ! 4170: IOReturn ioReturn; ! 4171: ! 4172: ! 4173: ASSERT( cmdBuf && cmdBuf->scsiReq ); ! 4174: /* Avoid notifying client of this condition; update */ ! 4175: /* perTarget.maxQueue and place this request on pendingCmdQ. */ ! 4176: /* We'll try this again when we ioComplete at least one */ ! 4177: /* command in this target's queue. */ ! 4178: /* Note that this can execute commands out of order. */ ! 4179: /* This can be disastrous for directory commands. */ ! 4180: /* In the long run, the client (disk/tape/whatever) */ ! 4181: /* needs to tell us how to execute the command */ ! 4182: /* (in-order, out-of-order, etc.) For example, */ ! 4183: /* virtual-memory page faults can be executed */ ! 4184: /* out of order, but directory and volume bitmap */ ! 4185: /* updates must be executed in-order to preserve */ ! 4186: /* volume integrity. */ ! 4187: if ( cmdBuf->queueTag == QUEUE_TAG_NONTAGGED ) ! 4188: { ! 4189: /* Huh? We're not doing command queueing... */ ! 4190: ioReturn = SR_IOST_BADST; ! 4191: } ! 4192: else ! 4193: { ! 4194: scsiReq = cmdBuf->scsiReq; ! 4195: target = scsiReq->target; ! 4196: lun = scsiReq->lun; ! 4197: gPerTargetData[ target ].maxQueue = gActiveArray[ target ][ lun ]; ! 4198: [ self pushbackCurrentRequest : cmdBuf ]; ! 4199: ioReturn = SR_IOST_GOOD; ! 4200: } ! 4201: return ioReturn; ! 4202: }/* end pushbackFullTargetQueue */ ! 4203: ! 4204: ! 4205: /* Push this request back on the pending queue. */ ! 4206: ! 4207: - (void) pushbackCurrentRequest : (CommandBuffer*) cmdBuf ! 4208: { ! 4209: ASSERT( cmdBuf ); ! 4210: if ( cmdBuf->flagActive ) ! 4211: { ! 4212: ASSERT( cmdBuf == gActiveCommand ); ! 4213: [ self deactivateCmd : cmdBuf ]; ! 4214: } ! 4215: queue_enter_first( &pendingCmdQ, cmdBuf, CommandBuffer*, link ); ! 4216: return; ! 4217: }/* end pushbackCurrentRequest */ ! 4218: ! 4219: ! 4220: /* Kill a request that can't be continued. */ ! 4221: ! 4222: - (void) killCurrentRequest ! 4223: { ! 4224: CommandBuffer *cmdBuf; ! 4225: IOSCSIRequest *scsiReq; ! 4226: ! 4227: ! 4228: if ( gActiveCommand ) ! 4229: { ! 4230: cmdBuf = gActiveCommand; ! 4231: ASSERT( cmdBuf->scsiReq ); ! 4232: scsiReq = cmdBuf->scsiReq; ! 4233: ! 4234: if ( cmdBuf->flagRequestSelectOK == FALSE ) ! 4235: scsiReq->driverStatus = SR_IOST_SELTO; /* No such device */ ! 4236: else scsiReq->driverStatus = SR_IOST_HW; /* Target went away */ ! 4237: ! 4238: [ self deactivateCmd : cmdBuf ]; ! 4239: [ self ioComplete : cmdBuf ]; ! 4240: } ! 4241: return; ! 4242: }/* end killCurrentRequest */ ! 4243: ! 4244: ! 4245: /* IO associated with gActiveCommand has disconnected. */ ! 4246: /* Place it on the disconnected command queue and */ ! 4247: /* enable another transaction. */ ! 4248: ! 4249: - (void) disconnect ! 4250: { ! 4251: ASSERT( gActiveCommand ); ! 4252: queue_enter( &disconnectedCmdQ, gActiveCommand, CommandBuffer*, link ); ! 4253: ! 4254: ! 4255: /* Record this time so that gActiveCommand can be billed */ ! 4256: /* for disconnect latency at reselect time. */ ! 4257: ! 4258: IOGetTimestamp( &gActiveCommand->disconnectTime ); ! 4259: gActiveCommand = NULL; ! 4260: gCurrentTarget = kInvalidTarget; ! 4261: gCurrentLUN = kInvalidLUN; ! 4262: ! 4263: /* Since there is no active command, the caller */ ! 4264: /* must configure the bus interface to wait for */ ! 4265: /* bus free, then allow reselection. */ ! 4266: ! 4267: return; ! 4268: }/* end disconnect */ ! 4269: ! 4270: ! 4271: /* The specified target, LUN, and queueTag is trying to reselect. */ ! 4272: /* If we have a CommandBuffer for this TLQ nexus on disconnectQ, */ ! 4273: /* remove it, make it the current gActiveCommand, and return YES. */ ! 4274: /* Else return NO. A value of zero for queueTag indicates a */ ! 4275: /* nontagged command (zero is never used as the queue tag value for */ ! 4276: /* a tagged command). */ ! 4277: ! 4278: - (IOReturn) reselectNexus : (UInt8) target ! 4279: lun : (UInt8) lun ! 4280: queueTag : (UInt8) queueTag ! 4281: { ! 4282: CommandBuffer *cmdBuf; ! 4283: IOSCSIRequest *scsiReq; ! 4284: ns_time_t currentTime; ! 4285: IOReturn ioReturn = SR_IOST_BV; /* Presume failure */ ! 4286: ! 4287: ! 4288: /* Scan the disconnected queue looking for */ ! 4289: /* a command for this nexus. */ ! 4290: ! 4291: ASSERT( gActiveCommand == NULL ); ! 4292: ! 4293: cmdBuf = (CommandBuffer*)queue_first( &disconnectedCmdQ ); ! 4294: ! 4295: while ( !queue_end( &disconnectedCmdQ, (queue_t)cmdBuf ) ) ! 4296: { ! 4297: scsiReq = cmdBuf->scsiReq; ! 4298: if (scsiReq->target == target ! 4299: && scsiReq->lun == lun ! 4300: && cmdBuf->queueTag == queueTag ) ! 4301: { ! 4302: /* We found the correct command. */ ! 4303: ! 4304: queue_remove( &disconnectedCmdQ, cmdBuf, CommandBuffer*, link ); ! 4305: gActiveCommand = cmdBuf; ! 4306: ASSERT( scsiReq->target == gCurrentTarget && scsiReq->lun == gCurrentLUN ); ! 4307: ! 4308: /* Bill this operation for latency time: */ ! 4309: ! 4310: IOGetTimestamp( ¤tTime ); ! 4311: scsiReq->latentTime += (currentTime - gActiveCommand->disconnectTime); ! 4312: ioReturn = IO_R_SUCCESS; ! 4313: break; ! 4314: } ! 4315: /* Try next element in queue. */ ! 4316: ! 4317: cmdBuf = (CommandBuffer*)cmdBuf->link.next; ! 4318: }/* end WHILE */ ! 4319: ! 4320: return ioReturn; ! 4321: }/* end reselectNexus */ ! 4322: ! 4323: ! 4324: /* Determine if gActiveArray[][], maxQueue, cmdQueueEnable, and a cmd's */ ! 4325: /* Target and LUN show that it's OK to start processing cmdBuf. */ ! 4326: /* Returns YES if this command can be started. */ ! 4327: /***** Here's where we can test for a frozen LUN queue. *****/ ! 4328: ! 4329: - (Boolean) commandCanBeStarted : (CommandBuffer*) cmdBuf ! 4330: { ! 4331: IOSCSIRequest *scsiReq; ! 4332: unsigned target; ! 4333: unsigned lun; ! 4334: UInt8 active; ! 4335: UInt8 maxQ; ! 4336: Boolean result; ! 4337: ! 4338: ! 4339: ASSERT( cmdBuf && cmdBuf->scsiReq ); ! 4340: ! 4341: scsiReq = cmdBuf->scsiReq; ! 4342: target = scsiReq->target; ! 4343: lun = scsiReq->lun; ! 4344: active = gActiveArray[ target ][ lun ]; ! 4345: if ( active == 0 ) ! 4346: { /* No commands are active for this target, always ok. */ ! 4347: result = TRUE; ! 4348: } ! 4349: else if ( gOptionCmdQueueEnable == FALSE ! 4350: || ((gPerTargetData[ target ].inquiry_7 & 0x02) == 0) ) ! 4351: { ! 4352: ELG( active, gOptionCmdQueueEnable, 'CQE-', "commandCanBeStarted - cmd q'n disabled" ); ! 4353: result = FALSE; /* q'ing is disabled for target (or disabled globally) */ ! 4354: } ! 4355: else ! 4356: { ! 4357: maxQ = gPerTargetData[ target ].maxQueue; ! 4358: if ( maxQ == 0 || active < maxQ ) ! 4359: { /* If maxQ is zero, we haven't reached the target's limit. */ ! 4360: /* Otherwise, we're under the limit. */ ! 4361: /* In both cases, we can (presumably) start this command. */ ! 4362: ! 4363: result = TRUE; ! 4364: } ! 4365: else ! 4366: { ! 4367: ELG( maxQ, active, 'QLm-', "commandCanBeStarted - queue limit reached." ); ! 4368: result = FALSE; /* We're over the target limit. Wait on this one. */ ! 4369: } ! 4370: } ! 4371: return result; ! 4372: }/* end commandCanBeStarted */ ! 4373: ! 4374: ! 4375: /* The bus has gone free. Start up a command from pendingCmdQ, */ ! 4376: /* if any, and if allowed by cmdQueueEnable and gActiveArray[][]. */ ! 4377: /* This is called from the interrupt routine when it is about to exit */ ! 4378: /* (and the bus is free and there is no active command). It may also */ ! 4379: /* be called from threadExecuteRequest when the selected command */ ! 4380: /* couldn't be started. */ ! 4381: ! 4382: - (void) selectNextRequest ! 4383: { ! 4384: CommandBuffer *cmdBuf = NULL; ! 4385: Boolean foundRequest = FALSE; ! 4386: ! 4387: ! 4388: if ( !queue_empty( &abortCmdQ ) ) ! 4389: { ! 4390: [ self AbortDisconnectedCommand ]; ! 4391: return; ! 4392: } ! 4393: ! 4394: if ( !queue_empty( &pendingCmdQ ) ) ! 4395: { ! 4396: /* Attempt to find a CommandBuffer in pendingCmdQ */ ! 4397: /* which we are in a position to process: */ ! 4398: ! 4399: cmdBuf = (CommandBuffer*)queue_first( &pendingCmdQ ); ! 4400: while ( !queue_end( &pendingCmdQ, (queue_entry_t)cmdBuf ) ) ! 4401: { ! 4402: if ( [ self commandCanBeStarted : cmdBuf ] ) ! 4403: { ! 4404: queue_remove( &pendingCmdQ, cmdBuf, CommandBuffer*, link); ! 4405: ELG( cmdBuf, cmdBuf->scsiReq->lun<<16 | cmdBuf->scsiReq->target, 'De Q', "selectNextRequest - dequeued one." ); ! 4406: foundRequest = TRUE; ! 4407: break; ! 4408: ! 4409: /* Note that threadExecuteRequest may call selectNextRequest */ ! 4410: /* if the command was rejected. If so, the rejected */ ! 4411: /* command will have been returned (with an error */ ! 4412: /* status) to its client, so there is no chance of */ ! 4413: /* an infinite loop here. */ ! 4414: } ! 4415: else ! 4416: { ! 4417: cmdBuf = (CommandBuffer*)queue_next( &cmdBuf->link ); ! 4418: } ! 4419: }/* end WHILE */ ! 4420: }/* end IF queue not empty */ ! 4421: ! 4422: if ( foundRequest ) ! 4423: [ self threadExecuteRequest : cmdBuf ]; ! 4424: ! 4425: return; ! 4426: }/* end selectNextRequest */ ! 4427: ! 4428: ! 4429: - (void) killActiveCommand : (sc_status_t)status ! 4430: { ! 4431: ELG( gActiveCommand, status, 'KilA', "killActiveCommand" ); ! 4432: if ( gActiveCommand ) ! 4433: { ! 4434: gActiveCommand->scsiReq->driverStatus = status; ! 4435: [ self ioComplete : gActiveCommand ]; ! 4436: gActiveCommand = NULL; ! 4437: gCurrentTarget = kInvalidTarget; ! 4438: gCurrentLUN = kInvalidLUN; ! 4439: } ! 4440: return; ! 4441: }/* end killActiveCommand */ ! 4442: ! 4443: ! 4444: /* Called by chip level to indicate that a command */ ! 4445: /* has gone out to the hardware. */ ! 4446: ! 4447: - (void) activateCommand : (CommandBuffer*)cmdBuf ! 4448: { ! 4449: ASSERT( gActiveCommand == NULL ); ! 4450: ! 4451: /* This is the only place where a gActiveArray[][] counter */ ! 4452: /* is incremented (and, hence, the only place where */ ! 4453: /* cmdBuf->active is set). The only other place gActiveCommand */ ! 4454: /* is set to non-NULL is in reselectNexus:target:lun:queueTag */ ! 4455: /* (but that doesn't increment the active command counter) */ ! 4456: ! 4457: gActiveCommand = cmdBuf; ! 4458: gCurrentTarget = cmdBuf->scsiReq->target; ! 4459: gCurrentLUN = cmdBuf->scsiReq->lun; ! 4460: gActiveArray[ gCurrentTarget ][ gCurrentLUN ]++; ! 4461: gActiveCount++; ! 4462: ! 4463: cmdBuf->flagActive = TRUE; ! 4464: ! 4465: /* Accumulate statistics. */ ! 4466: ! 4467: gMaxQueueLen = MAX( gMaxQueueLen, gActiveCount ); ! 4468: gQueueLenTotal += gActiveCount; ! 4469: gTotalCommands++; ! 4470: return; ! 4471: }/* end activateCommand */ ! 4472: ! 4473: ! 4474: /* Remove specified cmdBuf from "active" status. */ ! 4475: /* Update activeArray, activeCount, and unschedule pending timer. */ ! 4476: ! 4477: - (void) deactivateCmd : (CommandBuffer*)cmdBuf ! 4478: { ! 4479: IOSCSIRequest *scsiReq; ! 4480: unsigned target, lun; ! 4481: ! 4482: ! 4483: ASSERT( cmdBuf && cmdBuf->scsiReq ); ! 4484: ASSERT( cmdBuf == gActiveCommand ); /* ?? */ ! 4485: gActiveCommand = NULL; ! 4486: gCurrentTarget = kInvalidTarget; ! 4487: gCurrentLUN = kInvalidLUN; ! 4488: scsiReq = cmdBuf->scsiReq; ! 4489: target = scsiReq->target; ! 4490: lun = scsiReq->lun; ! 4491: ! 4492: ASSERT( gActiveArray[ target ][ lun ] ); ! 4493: gActiveArray[ target ][ lun ]--; ! 4494: ASSERT( gActiveCount ); ! 4495: gActiveCount--; ! 4496: ! 4497: /* Cancel pending timeout request. */ ! 4498: /* Commands which timed out don't have a timer request pending anymore. */ ! 4499: ! 4500: if ( scsiReq->driverStatus != SR_IOST_IOTO ) ! 4501: { ! 4502: IOUnscheduleFunc( serviceTimeoutInterrupt, cmdBuf ); ! 4503: } ! 4504: cmdBuf->flagActive = FALSE; ! 4505: return; ! 4506: }/* end deactivateCmd */ ! 4507: ! 4508: ! 4509: /* Kill everything in the indicated queue. Called after bus reset. */ ! 4510: ! 4511: - (void) killQueue : (queue_head_t*)queuePtr finalStatus : (sc_status_t)scsiStatus ! 4512: { ! 4513: CommandBuffer *cmdBuf; ! 4514: ! 4515: ! 4516: ELG( 0, queuePtr, 'KilQ', "killQueue" ); ! 4517: while ( !queue_empty( queuePtr ) ) ! 4518: { ! 4519: cmdBuf = (CommandBuffer*)queue_first( queuePtr ); ! 4520: queue_remove( queuePtr, cmdBuf, CommandBuffer*, link ); ! 4521: cmdBuf->scsiReq->driverStatus = scsiStatus; ! 4522: [ self ioComplete : cmdBuf ]; ! 4523: } ! 4524: return; ! 4525: }/* end killQueue */ ! 4526: ! 4527: ! 4528: - (void) UpdateCurrentIndex ! 4529: { ! 4530: CommandBuffer *cmdBuf = gActiveCommand; ! 4531: IOSCSIRequest *scsiReq = cmdBuf->scsiReq; ! 4532: UInt32 count; /* DMA transfer count */ ! 4533: UInt32 length = g.shadow.mesh.FIFOCount; ! 4534: UInt8 buffer[ 16 ]; ! 4535: UInt32 i; ! 4536: ! 4537: /* Calculate the number of bytes xferred by this channel command. */ ! 4538: /* We don't trust the DBDMA residual count. */ ! 4539: ! 4540: count = CCLWord( kcclBatchSize ); /* Our transfer count */ ! 4541: if ( count == 0 ) /* If batch is empty, */ ! 4542: return; /* look at nothing else.*/ ! 4543: count -= g.shadow.mesh.transferCount1 << 8; /* MESH residual high */ ! 4544: count -= g.shadow.mesh.transferCount0; /* MESH residual low */ ! 4545: cmdBuf->currentDataIndex += count; /* Increment data index */ ! 4546: if ( cmdBuf->mem ) ! 4547: [ cmdBuf->mem setPosition : cmdBuf->currentDataIndex ]; ! 4548: CCLWord( kcclBatchSize ) = 0; /* Clear our count */ ! 4549: ! 4550: /* Check the FIFO, if empty, increment the current data pointer. */ ! 4551: /* If there is stuff in it, we have more work to do. */ ! 4552: ! 4553: if ( g.shadow.mesh.FIFOCount ) /* If data in FIFO: */ ! 4554: { ! 4555: if ( scsiReq->read == FALSE ) /* If Writing: */ ! 4556: { ! 4557: [ self SetSeqReg : kMeshFlushFIFO ]; ! 4558: /* We didn't write these bytes in the FIFO - adjust index */ ! 4559: cmdBuf->currentDataIndex -= g.shadow.mesh.FIFOCount; ! 4560: if ( cmdBuf->mem ) ! 4561: [ cmdBuf->mem setPosition : cmdBuf->currentDataIndex ]; ! 4562: ! 4563: } ! 4564: else /* Must be Reading: */ ! 4565: { /* On a Read with data left in the FIFO, we must copy */ ! 4566: /* the FIFO directly into the user's data buffer: */ ! 4567: ! 4568: ELG( cmdBuf->currentDataIndex, g.shadow.mesh.FIFOCount, 'FIFO', ! 4569: "UpdateCurrentIndex - copy FIFO to user buffer." ); ! 4570: [ cmdBuf->mem setPosition : cmdBuf->currentDataIndex ]; ! 4571: count = scsiReq->maxTransfer - cmdBuf->currentDataIndex; ! 4572: if ( count > length ) ! 4573: count = length; ! 4574: ! 4575: /* FYI - emptying the FIFO causes cmdDone to get set. */ ! 4576: ! 4577: for ( i = 0; i < count; i++ ) ! 4578: buffer[ i ] = meshAddr->xFIFO; ! 4579: ! 4580: [ cmdBuf->mem writeToClient : buffer count : count ]; ! 4581: cmdBuf->currentDataIndex += count; ! 4582: [ cmdBuf->mem setPosition : cmdBuf->currentDataIndex ]; ! 4583: }/* end if/ELSE must be Reading */ ! 4584: }/* end IF FIFO was not empty */ ! 4585: ELG( 0, cmdBuf->currentDataIndex, 'UpIx', "UpdateCurrentIndex" ); ! 4586: return; ! 4587: }/* end UpdateCurrentIndex */ ! 4588: ! 4589: ! 4590: @end /* AppleMesh_SCSI( Private ) */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.