Annotation of kernel/bsd/dev/ppc/drvApple96_SCSI/Apple96SCSIPrivate.m, revision 1.1.1.1

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 1997 Apple Computer Inc. All Rights Reserved.
                     28:  * @author  Martin Minow    mailto:[email protected]
                     29:  * @revision   1997.02.18  Initial conversion from AMDPCSCSIDriver sources.
                     30:  *
                     31:  * Apple96SCSIPrivate.m - "Main" private methods for the 53C96 PCI SCSI interface.
                     32:  * Apple96SCSI is closely based on Doug Mitchell's AMD 53C974/79C974 driver.
                     33:  *
                     34:  * Hmm, these methods are probably common to all Apple SCSI implementations.
                     35:  *
                     36:  * Edit History
                     37:  * 1997.02.13  MM      Initial conversion from AMDPCSCSIDriver sources. 
                     38:  */
                     39: 
                     40: #import "Apple96SCSI.h"
                     41: #import "Apple96SCSIPrivate.h"
                     42: #import "Apple96CurioPublic.h"
                     43: #import "Apple96Hardware.h"
                     44: #import "bringup.h"
                     45: #import "MacSCSICommand.h"
                     46: #import <mach/message.h>
                     47: #import <driverkit/generalFuncs.h>
                     48: #import <driverkit/interruptMsg.h>
                     49: #import <driverkit/align.h>
                     50: #import <driverkit/kernelDriver.h>
                     51: #import <kernserv/prototypes.h>
                     52: 
                     53: /* 
                     54:  * Template for command message sent to the I/O thread.
                     55:  */
                     56: static const msg_header_t cmdMessageTemplate = {
                     57:     0,                         /* msg_unused           */
                     58:     1,                         /* msg_simple           */
                     59:     sizeof(msg_header_t),      /* msg_size             */ 
                     60:     MSG_TYPE_NORMAL,           /* msg_type             */
                     61:     PORT_NULL,                 /* msg_local_port       */
                     62:     PORT_NULL,                 /* msg_remote_port - filled in  */
                     63:     IO_COMMAND_MSG             /* msg_id               */
                     64: };
                     65: 
                     66: /*
                     67:  * Template for timeout message.
                     68:  */
                     69: static const msg_header_t timeoutMsgTemplate = {
                     70:     0,                          /* msg_unused          */
                     71:     1,                         /* msg_simple           */
                     72:     sizeof(msg_header_t),      /* msg_size             */ 
                     73:     MSG_TYPE_NORMAL,           /* msg_type             */
                     74:     PORT_NULL,                 /* msg_local_port       */
                     75:     PORT_NULL,                 /* msg_remote_port - filled in  */
                     76:     IO_TIMEOUT_MSG             /* msg_id               */
                     77: };
                     78: 
                     79: static void                serviceTimeoutInterrupt(
                     80:        void                    *arg
                     81:     );
                     82: 
                     83: @implementation Apple96_SCSI(Private)
                     84: 
                     85: /*
                     86:  * Private chip- and architecture-independent methods.
                     87:  */
                     88: /*
                     89:  * Pass one CommandBuffer to the I/O thread; wait for completion.
                     90:  * (We are called on the client's execution thread.)
                     91:  * Normal completion status is in cmdBuf->scsiReq->driverStatus; 
                     92:  * a non-zero return from this function indicates a Mach IPC error.
                     93:  *
                     94:  * This method allocates and frees cmdBuf->cmdLock.
                     95:  */
                     96: - (IOReturn)   executeCmdBuf
                     97:                        : (CommandBuffer *) cmdBuf
                     98: {
                     99:        msg_header_t        msg             = cmdMessageTemplate;
                    100:        kern_return_t       kernelReturn;
                    101:        IOReturn            ioReturn        = IO_R_SUCCESS;
                    102: 
                    103:        ENTRY("Sex executeCmdBuf");
                    104:        cmdBuf->flagActive = 0;
                    105:        cmdBuf->cmdLock = [[NXConditionLock alloc] initWith:CMD_PENDING];
                    106:        [gIncomingCommandLock lock];
                    107:        queue_enter(&gIncomingCommandQueue, cmdBuf, CommandBuffer *, link);
                    108:        [gIncomingCommandLock unlock];
                    109: 
                    110:        /*
                    111:         * Create a Mach message and send it in order to wake up the 
                    112:         * I/O thread.
                    113:         */
                    114:        msg.msg_remote_port = gKernelInterruptPort;
                    115:        kernelReturn = msg_send_from_kernel(&msg, MSG_OPTION_NONE, 0);
                    116:        if (kernelReturn != KERN_SUCCESS) {
                    117:            IOLog("%s: msg_send_from_kernel() error status %d\n", 
                    118:                [self name],
                    119:                kernelReturn
                    120:            );
                    121:            ioReturn = IO_R_IPC_FAILURE;
                    122:            goto exit;
                    123:        }
                    124: 
                    125:        /*
                    126:         * Wait for I/O complete.
                    127:         */
                    128:        ddmExported("executeCmdBuf: waiting for completion on cmdBuf 0x%x\n",
                    129:            cmdBuf, 2,3,4,5);
                    130:        [cmdBuf->cmdLock lockWhen:CMD_COMPLETE];
                    131: exit:  RESULT(ioReturn);       /* Log the completion before clearing the lock */
                    132:        [cmdBuf->cmdLock free];
                    133:        return (ioReturn);
                    134: }
                    135: 
                    136: /*
                    137:  * Abort all active and disconnected commands with specified status. No 
                    138:  * hardware action. Currently used by threadResetBus and during processing
                    139:  * of a kCommandAbortRequest command. 
                    140:  */
                    141: - (void) abortAllCommands : (sc_status_t) status
                    142: {
                    143:        ENTRY("Sab abortAllCommands");
                    144:        ddmThread("abortAllCommands\n", 1,2,3,4,5);
                    145:        [gIncomingCommandLock lock];
                    146:        [self killActiveCommand : status];
                    147:        [self killQueue  : &gDisconnectedCommandQueue   finalStatus : status];
                    148:        [self killQueue  : &gPendingCommandQueue        finalStatus : status];
                    149:        [self killQueue  : &gIncomingCommandQueue       finalStatus : status];
                    150: #ifdef DEBUG
                    151:        /*
                    152:         * activeArray "should be" empty...if not, make sure it is for debug.
                    153:         */
                    154:        do {
                    155:            int         target;
                    156:            int         lun;
                    157:            int         active;
                    158:            
                    159:            for (target = 0; target < SCSI_NTARGETS; target++) {
                    160:                for (lun = 0; lun < SCSI_NLUNS; lun++) {
                    161:                    active = gActiveArray[target][lun];
                    162:                    if (active > 0) {
                    163:                        IOLog("%s: (abortAllCommands) gActiveArray[%d][%d] = %d, should be zero\n",
                    164:                            [self name],
                    165:                            target, lun, active
                    166:                        );
                    167:                        gActiveCount -= active;
                    168:                        gActiveArray[target][lun] = 0;
                    169:                    }
                    170:                }
                    171:            }
                    172:            if (gActiveCount != 0) {
                    173:                IOLog("%s: (abortAllCommands) activeCount = %d, should be zero\n",
                    174:                    [self name],
                    175:                    gActiveCount
                    176:                );
                    177:                gActiveCount = 0;
                    178:            }
                    179:        } while (0);
                    180: #endif DEBUG
                    181:        [gIncomingCommandLock unlock];
                    182:        EXIT();
                    183: }
                    184: 
                    185: /*
                    186:  * Abort all active and disconnected commands with status SR_IOST_RESET.
                    187:  * Reset hardware and SCSI bus. If there is a command in gPendingCommandQueue,
                    188:  * start it up.
                    189:  */
                    190: - (void)    threadResetBus
                    191:                        : (const char *) reason
                    192: {
                    193:        ENTRY("Str threadResetBus");
                    194:        [self abortAllCommands : SR_IOST_RESET];
                    195:        [self hardwareReset : TRUE      reason : reason];   /* Reset SCSI and chip  */
                    196:        [self busFree];                                 /* Restart commands     */
                    197:        EXIT();
                    198: }
                    199: 
                    200: /*
                    201:  * Commence processing of the specified command. This is called by
                    202:  * commandRequestOccurred when it receives a kCommandExecute message
                    203:  * from IODirectDevice. There is a new SCSI request. Either start
                    204:  * it now, or add it to the end of our pending request queue.
                    205:  */
                    206: - (void)    threadExecuteRequest
                    207:                        : (CommandBuffer *) cmdBuf
                    208: {
                    209:        ENTRY("Stx threadExecuteRequest");
                    210:        if (gActiveCommand != NULL) {
                    211:            /*
                    212:             * We are currently executing a request.
                    213:             */
                    214:            ddmThread("threadExecuteRequest: ACTIVE; deferring 0x%x\n",
                    215:                cmdBuf,
                    216:                2,3,4,5
                    217:            );
                    218:            queue_enter(&gPendingCommandQueue, cmdBuf, CommandBuffer *, link);
                    219:        }       
                    220:        else if ([self commandCanBeStarted:cmdBuf] == NO) {
                    221:            /*
                    222:             * This request can't be started right now (perhaps the
                    223:             * target's tagged command limit has been reached).
                    224:             */
                    225:            ddmThread("threadExecuteRequest: !commandCanBeStarted; deferring 0x%x\n",
                    226:                cmdBuf,
                    227:                2,3,4,5
                    228:            );
                    229:            queue_enter(&gPendingCommandQueue, cmdBuf, CommandBuffer *, link);
                    230:        }
                    231:        else {
                    232:            /*
                    233:             * Apparently, we can start this request. Call the hardware layer.
                    234:             */
                    235:            ddmThread("calling hardwareStart: cmdBuf 0x%x activeArray[%d][%d] = %d\n",
                    236:                cmdBuf,
                    237:                cmdBuf->scsiReq->target,
                    238:                cmdBuf->scsiReq->lun,
                    239:                gActiveArray[cmdBuf->scsiReq->target][cmdBuf->scsiReq->lun],
                    240:                5
                    241:            );  
                    242:            switch ([self hardwareStart : cmdBuf]) {
                    243:            case kHardwareStartBusy:            /* Hardware can't start now     */
                    244:                ddmThread("threadExecuteRequest: hardware busy\n", 1, 2, 3, 4, 5);
                    245:                break;
                    246:            case kHardwareStartOK:              /* Command started correctly    */
                    247:                break;
                    248:            case kHardwareStartRejected:        /* Command rejected, try another */
                    249:                ddmThread("threadExecuteRequest: calling busFree\n", 1,2,3,4,5);
                    250:                [self busFree];                 /* Try another command          */
                    251:            }
                    252:            ddmThread("threadExecuteRequest(0x%x): DONE\n", cmdBuf, 2,3,4,5);
                    253:        }
                    254:        EXIT();
                    255: }
                    256: 
                    257: /*
                    258:  * Methods called by hardware-dependent modules.
                    259:  */
                    260: 
                    261: #if TEST_QUEUE_FULL
                    262: int testQueueFull; /* Set in the debugger to return queue full status */
                    263: #endif TEST_QUEUE_FULL
                    264: 
                    265: /*
                    266:  * Called when a transaction associated with cmdBuf is complete. Notify 
                    267:  * waiting thread. If cmdBuf->scsiReq exists (i.e., this is not a reset
                    268:  * or an abort), scsiReq->driverStatus must be valid. If cmdBuf is active,
                    269:  * caller must remove from gActiveCommand. We decrement activeArray[][] counter
                    270:  * if appropriate.
                    271:  */
                    272: - (void) ioComplete
                    273:            : (CommandBuffer *) cmdBuf
                    274:        finalStatus : (sc_status_t) finalStatus
                    275: {
                    276:        ns_time_t               currentTime;
                    277:        IOSCSIRequest           *scsiReq;
                    278:        
                    279:        ENTRY("Sic ioComplete");
                    280:        ASSERT(cmdBuf != NULL);
                    281:        if (cmdBuf == gActiveCommand) {
                    282:            [self deactivateCmd];
                    283:        }
                    284:        scsiReq = cmdBuf->scsiReq;
                    285:        if (scsiReq != NULL) {
                    286:            if (scsiReq->driverStatus == SR_IOST_INVALID) {
                    287:                scsiReq->driverStatus = finalStatus;
                    288:            }
                    289:            IOGetTimestamp(&currentTime);
                    290:            scsiReq->totalTime = currentTime - cmdBuf->startTime;
                    291:            scsiReq->bytesTransferred = cmdBuf->currentDataIndex;
                    292: #if SERIOUS_DEBUGGING || LOG_COMMAND_AND_RESULT
                    293:            IOLog("%s: ioComplete %d.%d: Transferred %d bytes, status %d: %s\n",
                    294:                    [self name],
                    295:                    scsiReq->target,
                    296:                    scsiReq->lun,
                    297:                    scsiReq->bytesTransferred,
                    298:                    scsiReq->driverStatus,
                    299:                    IOFindNameForValue(scsiReq->driverStatus, IOScStatusStrings)
                    300:                );
                    301: #endif /* SERIOUS_DEBUGGING || LOG_COMMAND_AND_RESULT */
                    302: #if (SERIOUS_DEBUGGING || DUMP_USER_BUFFER) && __IO_MEMORY_DESCRIPTOR_DEBUG__
                    303:            if (cmdBuf->mem != NULL) {
                    304:                IOMemoryDescriptorState         state;
                    305:                LogicalRange                    range;
                    306:                unsigned int                    newPosition = 0;
                    307:     
                    308:                [cmdbuf->mem state : &state];
                    309:                [cmdbuf->mem setPosition : 0];
                    310:                while (newPosition < scsiReq->bytesTransferred
                    311:                    && [cmdbuf->mem getLogicalRange : 1
                    312:                            maxByteCount            : scsiReq->bytesTransferred - newPosition
                    313:                            newPosition             : &newPosition
                    314:                            actualRanges            : NULL
                    315:                            logicalRanges           : &range] != 0) {
                    316:                    [self logMemory         : range.address
                    317:                            length          : range.length
                    318:                            reason          : "User buffer at I/O Complete"
                    319:                    ];
                    320:                }
                    321:                [cmdbuf->mem setState : &state];
                    322:            }
                    323: #endif /* (SERIOUS_DEBUGGING || DUMP_USER_BUFFER) && __IO_MEMORY_DESCRIPTOR_DEBUG__ */
                    324:            /*
                    325:             * Catch bad SCSI status now.
                    326:             */
                    327:            if (scsiReq->driverStatus == SR_IOST_GOOD) {
                    328: #if TEST_QUEUE_FULL
                    329:                if (testQueueFull
                    330:                 && (activeArray[scsiReq->target][scsiReq->lun] > 1)) {
                    331:                    scsiReq->scsiStatus = STAT_QUEUE_FULL;
                    332:                    testQueueFull = 0;
                    333:                }
                    334: #endif  TEST_QUEUE_FULL
                    335:                if (cmdBuf->flagIsAutosense) {
                    336:                    /*
                    337:                     * We are completing an autosense command. Don't touch
                    338:                     * the request status (it should still be Check Condition).
                    339:                     * Queue full is a real problem.
                    340:                     */
                    341:                    ASSERT(scsiReq->scsiStatus == kScsiStatusCheckCondition);
                    342:                    switch (cmdBuf->autosenseStatus) {
                    343:                    case kScsiStatusGood:
                    344:                        bcopy(gAutosenseArea, &scsiReq->senseData, sizeof (esense_reply_t));
                    345: #if SERIOUS_DEBUGGING || DUMP_USER_BUFFER
                    346:                        [self logMemory         : &scsiReq->senseData
                    347:                                length          : sizeof (esense_reply_t)
                    348:                                reason          : "Autosense at I/O completion"
                    349:                        ];
                    350: #endif /* SERIOUS_DEBUGGING || DUMP_USER_BUFFER */
                    351:                        scsiReq->driverStatus = SR_IOST_CHKSV;
                    352:                        break;
                    353:                    case kScsiStatusQueueFull:
                    354:                        if ([self pushbackFullTargetQueue : cmdBuf] == SR_IOST_GOOD) {
                    355:                            ddmThread("ioComplete: queue full after autosense\n", 1, 2, 3, 4, 5);
                    356:                            goto exit;
                    357:                        }
                    358:                        /* Fall through to failure */
                    359:                    default:
                    360:                        scsiReq->driverStatus = SR_IOST_CHKSNV;
                    361:                        break;
                    362:                    }
                    363:                }
                    364:                else { /* Normal command: not autosense */
                    365:                    switch (scsiReq->scsiStatus) {
                    366:                    case kScsiStatusGood:
                    367:        
                    368:                        /* If this was an Inquiry, peek at the data     */
                    369:                        /* for Synchronous and Tagged Queuing support:  */
                    370:        
                    371:                        if ( (*(UInt8*)&scsiReq->cdb == kScsiCmdInquiry)
                    372:                         &&  (cmdBuf->currentDataIndex > 7)
                    373:                         &&  (cmdBuf->mem != NULL) ) {
                    374:                            IOMemoryDescriptorState             state;
                    375:                            UInt8                               byte;
                    376:                            unsigned int                        count;
                    377:                            
                    378:                            [cmdBuf->mem state : &state];
                    379:                            [cmdBuf->mem setPosition : 7];
                    380:                            count = [cmdBuf->mem readFromClient : &byte count : 1];
                    381:                            [cmdBuf->mem setState : &state];
                    382:                            if (count == 1) {
                    383:                                gPerTargetData[ scsiReq->target ].inquiry_7 = byte;
                    384:                                if ( (byte & 0x02) == 0) {
                    385:                                    gPerTargetData[ scsiReq->target ].cmdQueueDisable = TRUE;
                    386:                                }
                    387:                            }
                    388:                        } /* end if successful inquiry completion */
                    389:                        break;
                    390:                    case kScsiStatusCheckCondition:
                    391:                        /*
                    392:                         * ** ** ** The 386 SIM supresses autosense for Test
                    393:                         * ** ** ** Unit Ready to avoid request sense when polling
                    394:                         * ** ** ** for removable devices. This should be the caller's
                    395:                         * ** ** ** decision.
                    396:                         */
                    397:                        if ( gOptionAutoSenseEnable                     /* Supress for debugging        */
                    398:                         && (scsiReq->ignoreChkcond == FALSE) ) {
                    399:                            cmdBuf->flagIsAutosense = 1;    /* We're doing autosense    */
                    400:                            queue_enter_first(&gPendingCommandQueue, cmdBuf, CommandBuffer *, link);
                    401:                            ddmThread("ioComplete: queuing for autosense\n", 1, 2, 3, 4, 5);
                    402:                            goto exit;
                    403:                        }
                    404:                        else {
                    405:                            /*
                    406:                             * This command failed and we aren't doing autosense.
                    407:                             */                 
                    408:                            scsiReq->driverStatus = SR_IOST_CHKSNV;
                    409:                        }
                    410:                        break;
                    411:                    case kScsiStatusQueueFull:
                    412:                        if ([self pushbackFullTargetQueue : cmdBuf] == SR_IOST_GOOD) {
                    413:                            /*
                    414:                             * ioComplete target tagged, queue full
                    415:                             */
                    416:                            ddmThread("ioComplete: target tagged, queue full\n", 1, 2, 3, 4, 5);
                    417:                            goto exit;
                    418:                        }
                    419:                        /* Huh? we weren't doing tagged queuing, fall through */
                    420:                    default:
                    421:                        scsiReq->driverStatus = SR_IOST_BADST;
                    422:                        break;
                    423:                    } /* switch */
                    424:                } /* If not autosense */
                    425:            } /* if scsiReq->driverStatus == SR_IOST_GOOD on entrance */
                    426:        } /* if (scsiReq != NULL) */
                    427: #if DDM_DEBUG
                    428:        do {
                    429:            const char          *status;
                    430:            unsigned            moved;
                    431:            
                    432:            if (scsiReq != NULL) {
                    433:                status = IOFindNameForValue(scsiReq->driverStatus, 
                    434:                    IOScStatusStrings);
                    435:                moved = scsiReq->bytesTransferred;
                    436:            }
                    437:            else {
                    438:                status = "Complete";
                    439:                moved = 0;
                    440:            }
                    441:            ddmThread("ioComplete: cmdBuf 0x%x status %s bytesXfr 0x%x\n", 
                    442:                cmdBuf, status, moved,4,5);
                    443:        } while (0);
                    444: #if LOG_RESULT_ON_ERROR
                    445:        if (scsiReq != NULL && scsiReq->driverStatus != SR_IOST_GOOD) {
                    446:            [self logCommand
                    447:                                : cmdBuf
                    448:                logMemory       : TRUE
                    449:                reason          : "Unsuccessful result"
                    450:            ];
                    451:        }
                    452: #endif /* LOG_RESULT_ON_ERROR */
                    453: #endif DDM_DEBUG
                    454:        [cmdBuf->cmdLock lock];
                    455:        [cmdBuf->cmdLock unlockWith:YES];
                    456: exit:  ; /* This semicolon is needed to prevent errors in the EXIT macro */
                    457:        EXIT();
                    458: }
                    459: 
                    460: /**
                    461:  * A target reported a full queue. Push this command back on the pending
                    462:  * queue and try it again, later. (Return SR_IOST_GOOD if successful,
                    463:  * SR_IOST_BADST on failure.
                    464:  */
                    465: - (sc_status_t) pushbackFullTargetQueue
                    466:                            : (CommandBuffer *) cmdBuf
                    467: {
                    468:        IOSCSIRequest           *scsiReq;
                    469:        int                     target;
                    470:        int                     lun;
                    471:        IOReturn                ioReturn;
                    472: 
                    473:        ENTRY("Spf pushbackFullTargetQueue");
                    474:        ASSERT(cmdBuf != NULL && cmdBuf->scsiReq != NULL);
                    475:        ASSERT(cmdBuf != gActiveCommand);
                    476:        /*
                    477:         * Avoid notifying client of this condition; update
                    478:         * perTarget.maxQueue and place this request on gPendingCommandQueue.
                    479:         * We'll try this again when we ioComplete at least one
                    480:         * command in this target's queue.
                    481:         ** ** ** Note that this can execute commands out of order.
                    482:         ** ** ** This can be disasterous for directory commands.
                    483:         ** ** ** In the long run, the client (disk/tape/whatever)
                    484:         ** ** ** needs to tell us how to execute the command
                    485:         ** ** ** (in-order, out-of-order, etc.) For example,
                    486:         ** ** ** virtual-memory page faults can be executed
                    487:         ** ** ** out of order, but directory and volume bitmap
                    488:         ** ** ** updates must be executed in-order to preserve
                    489:         ** ** ** volume integrity.
                    490:         */
                    491:        if (cmdBuf->queueTag == QUEUE_TAG_NONTAGGED) {
                    492:            /*
                    493:             * Huh? We're not doing command queueing...
                    494:             */
                    495:            ioReturn = SR_IOST_BADST;
                    496:        }
                    497:        else {
                    498:            scsiReq     = cmdBuf->scsiReq;
                    499:            target      = scsiReq->target;
                    500:            lun         = scsiReq->lun;
                    501:            gPerTargetData[target].maxQueue = gActiveArray[target][lun];
                    502:            ddmThread("Target %d queue full, maxQueue set to %d\n",
                    503:                target,
                    504:                gPerTargetData[target].maxQueue,
                    505:                3,4,5
                    506:            );
                    507:            [self pushbackCurrentRequest : cmdBuf];
                    508:            ioReturn = SR_IOST_GOOD;
                    509:        }
                    510:        RESULT(ioReturn);
                    511:        return (ioReturn);
                    512: }
                    513: 
                    514: /**
                    515:  * Push this request back on the pending queue.
                    516:  */
                    517: - (void) pushbackCurrentRequest
                    518:                : (CommandBuffer *) cmdBuf
                    519: {
                    520:        ENTRY("Spc pushbackCurrentRequest");
                    521:        if (gActiveCommand != NULL) {
                    522:            [self deactivateCmd];
                    523:        }
                    524:        queue_enter_first(&gPendingCommandQueue, cmdBuf, CommandBuffer *, link);
                    525:        EXIT();
                    526: }
                    527: 
                    528: #if 0
                    529: /**
                    530:  * Kill a request that can't be continued.
                    531:  */
                    532: - (void) killCurrentRequest
                    533: {
                    534:        ENTRY("Skc killCurrentRequest");
                    535:        if (gActiveCommand != NULL) {
                    536:            if (gActiveCommand->scsiReq != NULL
                    537:             && gActiveCommand->scsiReq->driverStatus == SR_IOST_INVALID) {
                    538:                if (gCurrentBusPhase == kBusPhaseBusFree) {
                    539:            gActiveCommand->scsiReq->driverStatus = SR_IOST_SELTO;  /* No such device   */
                    540:                }
                    541:                else {
                    542:            gActiveCommand->scsiReq->driverStatus = SR_IOST_HW;     /* Target went away */
                    543:                }
                    544:            }
                    545:            [self ioComplete
                    546:                                : gActiveCommand
                    547:                finalStatus     : SR_IOST_INVALID
                    548:            ];
                    549:        }
                    550:        EXIT();
                    551: }
                    552: #endif
                    553: 
                    554: /*
                    555:  * I/O associated with gActiveCommand has disconnected. Place it on the
                    556:  * disconnected command queue and enable another transaction.
                    557:  */ 
                    558: - (void) disconnect
                    559: {
                    560:        ENTRY("Sdi disconnect");
                    561:        ASSERT(gActiveCommand != NULL);
                    562:        ddmThread("DISCONNECT: cmdBuf 0x%x target %d lun %d tag %d\n",
                    563:            gActiveCommand, gActiveCommand->scsiReq->target,
                    564:            gActiveCommand->scsiReq->lun, gActiveCommand->queueTag, 5);
                    565:        queue_enter(
                    566:            &gDisconnectedCommandQueue,
                    567:            gActiveCommand,
                    568:            CommandBuffer *,
                    569:            link
                    570:        );
                    571: #if DDM_DEBUG
                    572:        if ((gActiveCommand->currentDataIndex != gActiveCommand->scsiReq->maxTransfer)
                    573:         && (gActiveCommand->currentDataIndex != 0)) {
                    574:            ddmThread("disconnect after partial DMA (max 0x%d curr 0x%x)\n",
                    575:                gActiveCommand->scsiReq->maxTransfer, 
                    576:                gActiveCommand->currentDataIndex, 3,4,5);
                    577:        }
                    578: #endif DDM_DEBUG
                    579:        /*
                    580:         * Record this time so that gActiveCommand can be billed for
                    581:         * disconnect latency at reselect time.
                    582:         */
                    583:        IOGetTimestamp(&gActiveCommand->disconnectTime);
                    584:        gActiveCommand      = NULL;
                    585:        gCurrentTarget      = kInvalidTarget;
                    586:        gCurrentLUN         = kInvalidLUN;
                    587:        /*
                    588:         * Since there is no active command, the caller must configure the
                    589:         * bus interface to wait for bus free, then allow reselection.
                    590:         */
                    591:        EXIT();
                    592: }
                    593: 
                    594: /*
                    595:  * Specified target, lun is trying to reselect. If we have a CommandBuffer
                    596:  * for this TL nexus and it is not tagged, remove it, make it the current
                    597:  * gActiveCommand, and return IO_R_SUCCESS. If the first disconnected
                    598:  * command is tagged, we need to read the queue tag message, return
                    599:  * IO_R_INTERNAL to signal this (it is not an error).
                    600:  */
                    601: - (IOReturn)   reselectNexusWithoutTag
                    602: {
                    603:        CommandBuffer       *cmdBuf;
                    604:        IOSCSIRequest       *scsiReq;
                    605:        ns_time_t           currentTime;
                    606:        IOReturn            ioReturn        = SR_IOST_HW;   /* Presume failure  */
                    607: 
                    608:        ENTRY("Srn reselectNexusWithoutTag");
                    609:        ASSERT(gActiveCommand == NULL);
                    610:        cmdBuf = (CommandBuffer *) queue_first(&gDisconnectedCommandQueue);
                    611:        while (queue_end(&gDisconnectedCommandQueue, (queue_t) cmdBuf) == FALSE) {
                    612:            scsiReq = cmdBuf->scsiReq;
                    613:            if (scsiReq->target == gCurrentTarget
                    614:             && scsiReq->lun == gCurrentLUN
                    615:             && cmdBuf->queueTag == QUEUE_TAG_NONTAGGED) {
                    616:                /*
                    617:                 * We found the correct command.
                    618:                 */
                    619:                ddmThread("RESELECT: target %d.%d no tag %FOUND; cmdBuf 0x%x\n",
                    620:                    gCurrentTarget,
                    621:                    gCurrentLUN,
                    622:                    cmdBuf,
                    623:                    4, 5
                    624:                );
                    625:                queue_remove(
                    626:                    &gDisconnectedCommandQueue,
                    627:                    cmdBuf,
                    628:                    CommandBuffer *,
                    629:                    link
                    630:                );
                    631:                gActiveCommand = cmdBuf;
                    632:                /*
                    633:                 * Bill this operation for latency time.
                    634:                 */
                    635:                IOGetTimestamp(&currentTime);
                    636:                scsiReq->latentTime += 
                    637:                            (currentTime - gActiveCommand->disconnectTime);
                    638:                ioReturn = IO_R_SUCCESS;
                    639:                break;
                    640:            }
                    641:            /*
                    642:             * Try next element in queue.
                    643:             */
                    644:            cmdBuf = (CommandBuffer *) cmdBuf->link.next;
                    645:        }
                    646:        RESULT(ioReturn);
                    647:        return (ioReturn);
                    648: }
                    649: /*
                    650:  * Specified target, lun, and queueTag is trying to reselect. If we have 
                    651:  * a CommandBuffer for this TLQ nexus on disconnectQ, remove it, make it the
                    652:  * current activeCmd, and return IO_R_SUCCESS. Else return IO_R_HW.
                    653:  */
                    654: - (IOReturn) reselectNexusWithTag
                    655:                    : (UInt8) queueTag
                    656: {
                    657:        CommandBuffer       *cmdBuf;
                    658:        IOSCSIRequest       *scsiReq;
                    659:        ns_time_t           currentTime;
                    660:        IOReturn            ioReturn        = SR_IOST_HW;   /* Presume failure  */
                    661: 
                    662:        ENTRY("Srt reselectNexusWithTag");
                    663:        ASSERT(gActiveCommand == NULL);
                    664:        ASSERT(queueTag != QUEUE_TAG_NONTAGGED);
                    665:        /*
                    666:         * Scan the disconnected queue looking for a command for this
                    667:         * T/L/Q nexus.
                    668:         */
                    669:        cmdBuf = (CommandBuffer *) queue_first(&gDisconnectedCommandQueue);
                    670:        while (queue_end(&gDisconnectedCommandQueue, (queue_t) cmdBuf) == FALSE) {
                    671:            scsiReq = cmdBuf->scsiReq;
                    672:            if (scsiReq->target == gCurrentTarget
                    673:             && scsiReq->lun == gCurrentLUN
                    674:             && cmdBuf->queueTag == queueTag) {
                    675:                /*
                    676:                 * We found the correct command.
                    677:                 */
                    678:                ddmThread("RESELECT: target %d.%d tag %d FOUND; cmdBuf 0x%x\n",
                    679:                    gCurrentTarget,
                    680:                    gCurrentLUN,
                    681:                    queueTag,
                    682:                    cmdBuf,
                    683:                    5
                    684:                );
                    685:                queue_remove(
                    686:                    &gDisconnectedCommandQueue,
                    687:                    cmdBuf,
                    688:                    CommandBuffer *,
                    689:                    link
                    690:                );
                    691:                gActiveCommand = cmdBuf;
                    692:                ASSERT(scsiReq->target == gCurrentTarget && scsiReq->lun == gCurrentLUN);
                    693:                /*
                    694:                 * Bill this operation for latency time.
                    695:                 */
                    696:                IOGetTimestamp(&currentTime);
                    697:                scsiReq->latentTime += 
                    698:                            (currentTime - gActiveCommand->disconnectTime);
                    699:                ioReturn = IO_R_SUCCESS;
                    700:                break;
                    701:            }
                    702:            /*
                    703:             * Try next element in queue.
                    704:             */
                    705:            cmdBuf = (CommandBuffer *) cmdBuf->link.next;
                    706:        }
                    707:        if (ioReturn != IO_R_SUCCESS) {
                    708:            /*
                    709:             * Hmm...this is not good! We don't want to talk to this target.
                    710:             * Perhaps it's reselecting after we have already aborted the
                    711:             * original command request.
                    712:            */ 
                    713:            IOLog("%s: Unexpected reselect from target %d lun %d tag %d\n",
                    714:                [self name],
                    715:                gCurrentTarget,
                    716:                gCurrentLUN,
                    717:                queueTag
                    718:            );
                    719:        }
                    720:        RESULT(ioReturn);
                    721:        return (ioReturn);
                    722: }
                    723: 
                    724: /*
                    725:  * Determine if gActiveArray[][], maxQueue, cmdQueueEnable, and a 
                    726:  * command's target and lun show that it's OK to start processing cmdBuf.
                    727:  * Returns YES if this command can be started.
                    728:  * ** ** **
                    729:  * ** ** ** Here's where we can test for a frozen LUN queue.
                    730:  * ** ** **
                    731:  */
                    732: - (Boolean) commandCanBeStarted : (CommandBuffer *) cmdBuf
                    733: {
                    734:        IOSCSIRequest       *scsiReq;
                    735:        unsigned            target      = scsiReq->target;
                    736:        unsigned            lun         = scsiReq->lun;
                    737:        Boolean             result;
                    738: 
                    739:        ENTRY("Scs commandCanBeStarted");
                    740:        ASSERT(cmdBuf != NULL && cmdBuf->scsiReq != NULL);
                    741:        scsiReq     = cmdBuf->scsiReq;
                    742:        target      = scsiReq->target;
                    743:        lun         = scsiReq->lun;
                    744:        if (gActiveArray[target][lun] == 0) {
                    745:            /*
                    746:             * No commands are active for this target, always ok.
                    747:             */
                    748:            result = TRUE;
                    749:        }
                    750:        else if (gOptionCmdQueueEnable == FALSE
                    751:              || gPerTargetData[target].cmdQueueDisable) {
                    752:            /*
                    753:             * Command queueing is disabled for this target
                    754:             * (or disabled globally); only one command at a time
                    755:             * can be issued.
                    756:             */
                    757:            result = FALSE;
                    758:        }
                    759:        else {
                    760:            if (gPerTargetData[target].maxQueue == 0
                    761:             || gActiveArray[target][lun] < gPerTargetData[target].maxQueue) {
                    762:                /*
                    763:                 * If maxQueueDepth is zero, we haven't reached the
                    764:                 * target's limit. Otherwise, we're under the limit. In
                    765:                 * both cases, we can (presumably) start this command.
                    766:                 * 
                    767:                 */
                    768:                result = TRUE;
                    769:            }
                    770:            else {
                    771:                /*
                    772:                 * We're over the target limit. Wait on this one.
                    773:                 */
                    774:                result = FALSE;
                    775:            }   
                    776:        }
                    777:        RESULT(result);
                    778:        return (result);
                    779: }
                    780: 
                    781: /*
                    782:  * The bus has gone free. Start up a command from gPendingCommandQueue, if any, and
                    783:  * if allowed by cmdQueueEnable and gActiveArray[][].
                    784:  *
                    785:  * This is called from the interrupt routine when it is about to exit (and the
                    786:  * bus is free and there is no active command). It may also be called from
                    787:  * threadExecuteRequest when the selected command couldn't be started.
                    788:  * 
                    789:  */
                    790: - (void) busFree
                    791: {
                    792:        CommandBuffer       *cmdBuf;
                    793: 
                    794:        ENTRY("Sbf busFree");
                    795:        cmdBuf = [self selectNextRequest];
                    796:        if (cmdBuf != NULL) {
                    797:            [self threadExecuteRequest:cmdBuf];
                    798:        }
                    799:        EXIT();
                    800: }
                    801: 
                    802: /*
                    803:  * Scan through the pending queue for the first command that can be started.
                    804:  * Return the command record, or NULL if there is no pending command.
                    805:  */
                    806: - (CommandBuffer *)    selectNextRequest
                    807: {
                    808:        CommandBuffer       *cmdBuf = NULL;
                    809:        Boolean             foundRequest;
                    810: 
                    811:        ENTRY("Ssn selectNextRequest");
                    812:        ASSERT(gActiveCommand == NULL);
                    813:        foundRequest = FALSE;
                    814:        if (queue_empty(&gPendingCommandQueue) == FALSE) {
                    815:            /*
                    816:             * Attempt to find a CommandBuffer in gPendingCommandQueue which we are in a position
                    817:             * to process.
                    818:             */
                    819:            cmdBuf = (CommandBuffer *) queue_first(&gPendingCommandQueue);
                    820:            while (queue_end(&gPendingCommandQueue, (queue_entry_t) cmdBuf) == FALSE) {
                    821:                if ([self commandCanBeStarted:cmdBuf]) {
                    822:                    queue_remove(&gPendingCommandQueue, cmdBuf, CommandBuffer *, link);
                    823:                    ddmThread("busFree: starting pending cmd 0x%x\n", cmdBuf,
                    824:                        2,3,4,5);
                    825:                    foundRequest = TRUE;
                    826:                    break;
                    827:                    /*
                    828:                     * Note that threadExecuteRequest may call busFree if the command
                    829:                     * was rejected. If so, the rejected command will have been
                    830:                     * returned (with an error status) to its client, so there is
                    831:                     * no chance of an infinite loop here.
                    832:                     */
                    833:                }
                    834:                else {
                    835:                    cmdBuf = (CommandBuffer *) queue_next(&cmdBuf->link);
                    836:                }
                    837:            }
                    838:        }
                    839:        if (foundRequest == FALSE) {
                    840:            cmdBuf = NULL;
                    841:        }
                    842:        RESULT(cmdBuf);
                    843:        return (cmdBuf);
                    844: }
                    845: 
                    846: /*
                    847:  * Abort gActiveCommand (if any) and any disconnected I/Os (if any) and reset 
                    848:  * the bus due to gross hardware failure.
                    849:  *
                    850:  * If gActiveCommand is valid, its scsiReq->driverStatus will be set to 'status'.
                    851:  * This is called when the bus state machine detects an error that it can't
                    852:  * recover from, such as a "gross error" in the chip. It is a very big hammer.
                    853:  */
                    854: - (void) killActiveCommandAndResetBus
                    855:                        : (sc_status_t) status
                    856:             reason     : (const char *) reason
                    857: {
                    858:        ENTRY("Skr killActiveCommandAndResetBus");
                    859:        [self killActiveCommand : status];
                    860:        [self logRegisters : TRUE reason : "Killing Active Command and Resetting Bus"];
                    861:        [self threadResetBus : reason];
                    862:        EXIT();
                    863: }
                    864: 
                    865: - (void) killActiveCommand
                    866:                        : (sc_status_t) status
                    867: {
                    868:     CommandBuffer      *activeCmd;     /* -> The currently executing command   */
                    869: 
                    870:        ENTRY("Ski killActiveCommand");
                    871:        if (gActiveCommand != NULL) {
                    872:            activeCmd = gActiveCommand;
                    873:            [self deactivateCmd];
                    874:            [self ioComplete
                    875:                                : activeCmd
                    876:                finalStatus     : status
                    877:             ];
                    878:        }
                    879:        EXIT();
                    880: }
                    881: 
                    882: /*
                    883:  * Called by chip level to indicate that a command has gone out to the 
                    884:  * hardware.
                    885:  */
                    886: - (void) activateCommand
                    887:                    : (CommandBuffer *) cmdBuf
                    888: {
                    889:        ENTRY("Sac activateCommand");
                    890:        ASSERT(gActiveCommand == NULL);
                    891:        /*
                    892:         * Start timeout timer for this I/O. The timer request is cancelled
                    893:         * in ioComplete.
                    894:         */
                    895:        cmdBuf->timeoutPort = gKernelInterruptPort;
                    896: #if LONG_TIMEOUT
                    897:        cmdBuf->scsiReq->timeoutLength = OUR_TIMEOUT;
                    898: #endif LONG_TIMEOUT
                    899:        IOScheduleFunc(serviceTimeoutInterrupt, cmdBuf, cmdBuf->scsiReq->timeoutLength);
                    900: 
                    901:        /*
                    902:         * This is the only place where an gActiveArray[][] counter is 
                    903:         * incremented (and, hence, the only place where cmdBuf->active is 
                    904:         * set). The only other place gActiveCommand is set to non-NULL is
                    905:         * in reselectNexux... (but those methods don't increment the
                    906:         * active command counter)
                    907:         */
                    908:        gActiveCommand          = cmdBuf;
                    909:        gCurrentTarget          = cmdBuf->scsiReq->target;
                    910:        gCurrentLUN             = cmdBuf->scsiReq->lun;
                    911:        TAG(__tag__, (gCurrentTarget << 4) | gCurrentLUN);
                    912:        gActiveArray[gCurrentTarget][gCurrentLUN]++;
                    913:        gActiveCount++;
                    914:        cmdBuf->flagActive          = TRUE;
                    915:        /*
                    916:         * Accumulate statistics.
                    917:         */
                    918:        gMaxQueueLen = MAX(gMaxQueueLen, gActiveCount);
                    919:        gQueueLenTotal += gActiveCount;
                    920:        gTotalCommands++;
                    921:        ddmThread("activateCommand: cmdBuf 0x%x target %d lun %d\n",
                    922:            cmdBuf, gCurrentTarget, gCurrentLUN,
                    923:            4, 5
                    924:        );
                    925:        EXIT();
                    926: }
                    927: 
                    928: /*
                    929:  * Remove the current command from "active" status. Update activeArray,
                    930:  * activeCount, and unschedule pending timer.
                    931:  */
                    932: - (void) deactivateCmd
                    933: {
                    934:        CommandBuffer       *cmdBuf;
                    935:        unsigned            target, lun;
                    936: 
                    937:        ENTRY("Sdc deactivateCmd");
                    938:        /*
                    939:         * If we are pushing back a full target queue, it's possible
                    940:         * to be called twice on the same command: once from ioComplete
                    941:         * and once from pushbackCurrentRequest. To prevent the second
                    942:         * call from de-referencing NULL, we exit if gActiveCommand
                    943:         * is already NULL. Radar 2206796.
                    944:         */
                    945:        if (gActiveCommand != NULL) {
                    946:            ASSERT(gActiveCommand->scsiReq != NULL);
                    947:            TAG(__tag__,
                    948:                (gActiveCommand->scsiReq->target << 4)
                    949:                | gActiveCommand->scsiReq->lun
                    950:            );
                    951:            ddmThread("deactivate cmd 0x%x target %d lun %d activeArray %d\n",
                    952:                gActiveCommand,
                    953:                gActiveCommand->scsiReq->target,
                    954:                gActiveCommand->scsiReq->lun,
                    955:                gActiveArray[gActiveCommand->scsiReq->target]
                    956:                        [gActiveCommand->scsiReq->lun],
                    957:                5
                    958:            );
                    959:            cmdBuf              = gActiveCommand;
                    960:            gActiveCommand      = NULL;
                    961:            gCurrentTarget      = kInvalidTarget;
                    962:            gCurrentLUN         = kInvalidLUN;
                    963:            target              = cmdBuf->scsiReq->target;
                    964:            lun                 = cmdBuf->scsiReq->lun;
                    965:            ASSERT(gActiveArray[target][lun] > 0);
                    966:            gActiveArray[target][lun]--;
                    967:            ASSERT(gActiveCount > 0);
                    968:            gActiveCount--;
                    969:            /*
                    970:             * Cancel pending timeout request. We don't have to do this
                    971:             * for commands that have timed out as they won't have a
                    972:             * timeout request in the schedular.
                    973:             */
                    974:            if (cmdBuf->scsiReq->driverStatus != SR_IOST_IOTO) {
                    975:                IOUnscheduleFunc(serviceTimeoutInterrupt, cmdBuf);
                    976:            }
                    977:            cmdBuf->flagActive = FALSE;
                    978:        }
                    979:        EXIT();
                    980: }
                    981: 
                    982: /**
                    983:  * Kill everything in the indicated queue. Called after bus reset.
                    984:  */
                    985: - (void) killQueue  : (queue_head_t *) queuePtr
                    986:     finalStatus : (sc_status_t) scsiStatus
                    987: {
                    988:        CommandBuffer           *cmdBuf;
                    989: 
                    990:        ENTRY("Skq killQueue");
                    991:        while (queue_empty(queuePtr) == FALSE) {
                    992:            cmdBuf = (CommandBuffer *) queue_first(queuePtr);
                    993:            queue_remove(queuePtr, cmdBuf, CommandBuffer *, link);
                    994:            cmdBuf->scsiReq->driverStatus = scsiStatus;
                    995:            [self ioComplete
                    996:                                : cmdBuf
                    997:                finalStatus     : scsiStatus
                    998:            ];
                    999:        }
                   1000:        EXIT();
                   1001: }
                   1002: 
                   1003: @end   /* Apple96_SCSI(Private) */
                   1004: 
                   1005: /*
                   1006:  *  Handle timeouts.  We just send a timeout message to the I/O thread
                   1007:  *  so it wakes up.
                   1008:  */
                   1009: static void serviceTimeoutInterrupt(void *arg)
                   1010: {
                   1011:        CommandBuffer       *cmdBuf = arg;
                   1012:        msg_header_t        msg = timeoutMsgTemplate;
                   1013: 
                   1014:        ENTRY("Sto serviceTimeoutInterrupt");
                   1015:        ASSERT(cmdBuf != NULL && cmdBuf->scsiReq != NULL);
                   1016:        ddmError("Apple96SCSI: timeout: cmdBuf 0x%x target %d\n",
                   1017:            cmdBuf,
                   1018:            cmdBuf->scsiReq->target,
                   1019:            3,4,5
                   1020:        );
                   1021:        if (cmdBuf->flagActive == FALSE) {
                   1022:            /*
                   1023:             * Should never happen...
                   1024:             */
                   1025:            IOLog("Apple96SCSI: Timeout on inactive command\n");
                   1026:        }
                   1027:        else {
                   1028:            msg.msg_remote_port = cmdBuf->timeoutPort;
                   1029:            IOLog("Apple96SCSI: SCSI Timeout on target %d, lun %d\n",
                   1030:                cmdBuf->scsiReq->target,
                   1031:                cmdBuf->scsiReq->lun
                   1032:            );
                   1033:            (void) msg_send_from_kernel(&msg, MSG_OPTION_NONE, 0);
                   1034:        }
                   1035:        EXIT();
                   1036: }
                   1037: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.