home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-02-10 | 29.1 KB | 1,205 lines |
- /*
- * AMD_SCSI.m - top-level module for AMD 53C974/79C974 PCI SCSI driver.
- *
- * HISTORY
- * 21 Oct 94 Doug Mitchell at NeXT
- * Created.
- */
-
- #import "AMD_SCSI.h"
- #import "AMD_Private.h"
- #import "AMD_x86.h"
- #import "AMD_Chip.h"
- #import "AMD_ddm.h"
- #import "AMD_Regs.h"
- #import <mach/message.h>
- #import <driverkit/generalFuncs.h>
- #import <driverkit/interruptMsg.h>
- #import <driverkit/align.h>
- #import <driverkit/kernelDriver.h>
- #import <kernserv/prototypes.h>
-
- static void AMDTimeout(void *arg);
-
- /*
- * Template for command message sent to the I/O thread.
- */
- static msg_header_t cmdMessageTemplate = {
- 0, // msg_unused
- 1, // msg_simple
- sizeof(msg_header_t), // msg_size
- MSG_TYPE_NORMAL, // msg_type
- PORT_NULL, // msg_local_port
- PORT_NULL, // msg_remote_port - TO
- // BE FILLED IN
- IO_COMMAND_MSG // msg_id
- };
-
- /*
- * Template for timeout message.
- */
- static msg_header_t timeoutMsgTemplate = {
- 0, // msg_unused
- 1, // msg_simple
- sizeof(msg_header_t), // msg_size
- MSG_TYPE_NORMAL, // msg_type
- PORT_NULL, // msg_local_port
- PORT_NULL, // msg_remote_port - TO
- // BE FILLED IN
- IO_TIMEOUT_MSG // msg_id
- };
-
-
- @implementation AMD_SCSI
-
- /*
- * Create and initialize one instance of AMD_SCSI. The work is done by
- * architecture- and chip-specific modules.
- */
- + (BOOL)probe:deviceDescription
- {
- AMD_SCSI *inst = [self alloc];
-
-
- if([inst archInit:deviceDescription] == nil) {
- return NO;
- }
- else {
- return YES;
- }
- }
-
- - free
- {
- commandBuf cmdBuf;
-
- /*
- * First kill the I/O thread if running.
- */
- if(ioThreadRunning) {
- cmdBuf.op = CO_Abort;
- cmdBuf.scsiReq = NULL;
- [self executeCmdBuf:&cmdBuf];
- }
-
- if(commandLock) {
- [commandLock free];
- }
- if(mdlFree) {
- IOFree(mdlFree, MDL_SIZE * 2 * sizeof(vm_address_t));
- }
- return [super free];
- }
-
- /*
- * Our max DMA size is 64 K, derived from using 18 MDL entries (note the
- * first and last entries can refer to chunks as small as 4 bytes).
- */
- - (unsigned)maxDmaSize
- {
- return AMD_DMA_PAGE_SIZE * (MDL_SIZE - 2);
- }
-
- /*
- * Return required DMA alignment for current architecture.
- */
- - (void)getDMAAlignment : (IODMAAlignment *)alignment;
- {
- alignment->readStart = AMD_READ_START_ALIGN;
- alignment->writeStart = AMD_WRITE_START_ALIGN;
- alignment->readLength = AMD_READ_LENGTH_ALIGN;
- alignment->writeLength = AMD_WRITE_LENGTH_ALIGN;
- }
-
- /*
- * Statistics support.
- */
- - (unsigned int) numQueueSamples
- {
- return totalCommands;
- }
-
-
- - (unsigned int) sumQueueLengths
- {
- return queueLenTotal;
- }
-
-
- - (unsigned int) maxQueueLength
- {
- return maxQueueLen;
- }
-
-
- - (void)resetStats
- {
- totalCommands = 0;
- queueLenTotal = 0;
- maxQueueLen = 0;
- }
-
- /*
- * Do a SCSI command, as specified by an IOSCSIRequest. All the
- * work is done by the I/O thread.
- */
- - (sc_status_t) executeRequest : (IOSCSIRequest *)scsiReq
- buffer : (void *)buffer
- client : (vm_task_t)client
- {
- commandBuf cmdBuf;
-
- ddm_exp("executeRequest: cmdBuf 0x%x maxTransfer 0x%x\n",
- &cmdBuf, scsiReq->maxTransfer,3,4,5);
-
- bzero(&cmdBuf, sizeof(commandBuf));
- cmdBuf.op = CO_Execute;
- cmdBuf.scsiReq = scsiReq;
- cmdBuf.buffer = buffer;
- cmdBuf.client = client;
- scsiReq->driverStatus = SR_IOST_INVALID;
-
- [self executeCmdBuf:&cmdBuf];
-
- ddm_exp("executeRequest: cmdBuf 0x%x complete; driverStatus %s\n",
- &cmdBuf, IOFindNameForValue(scsiReq->driverStatus,
- IOScStatusStrings), 3,4,5);
- return cmdBuf.scsiReq->driverStatus;
- }
-
-
- /*
- * Reset the SCSI bus. All the work is done by the I/O thread.
- */
- - (sc_status_t)resetSCSIBus
- {
- commandBuf cmdBuf;
-
- ddm_exp("resetSCSIBus: cmdBuf 0x%x\n", &cmdBuf, 2,3,4,5);
-
- cmdBuf.op = CO_Reset;
- cmdBuf.scsiReq = NULL;
- [self executeCmdBuf:&cmdBuf];
- ddm_exp("resetSCSIBus: cmdBuf 0x%x DONE\n", &cmdBuf, 2,3,4,5);
- return SR_IOST_GOOD; // can not fail
- }
-
- /*
- * The following 6 methods are all called from the I/O thread in
- * IODirectDevice.
- */
-
- /*
- * Called from the I/O thread when it receives an interrupt message.
- * Currently all work is done by chip-specific module; maybe we should
- * put this method there....
- */
- - (void)interruptOccurred
- {
- #if DDM_DEBUG
- /*
- * calculate interrupt service time if enabled.
- */
- ns_time_t startTime, endTime, elapsedNs;
- unsigned elapsedUs = 0;
-
- if(IODDMMasks[AMD_DDM_INDEX] & DDM_INTR) {
- IOGetTimestamp(&startTime);
- }
- ddm_thr("interruptOccurred: TOP\n", 1,2,3,4,5);
- #endif DDM_DEBUG
-
- [self hwInterrupt];
-
- #if DDM_DEBUG
- if(IODDMMasks[AMD_DDM_INDEX] & DDM_INTR) {
- IOGetTimestamp(&endTime);
- elapsedNs = endTime - startTime;
- elapsedUs = (unsigned)((elapsedNs + 999ULL) / 1000ULL);
- }
- ddm_intr("interruptOccurred: DONE; elapsed time %d us\n",
- elapsedUs, 2,3,4,5);
- #endif DDM_DEBUG
- }
-
- /*
- * These three should not occur; they are here as error traps. All three are
- * called out from the I/O thread upon receipt of messages which it should
- * not be seeing.
- */
- - (void)interruptOccurredAt:(int)localNum
- {
- IOLog("%s: interruptOccurredAt:%d\n", [self name], localNum);
- }
-
- - (void)otherOccurred:(int)id
- {
- IOLog("%s: otherOccurred:%d\n", [self name], id);
- }
-
- - (void)receiveMsg
- {
- IOLog("%s: receiveMsg\n", [self name]);
-
- /*
- * We have to let IODirectDevice take care of this (i.e., dequeue the
- * bogus message).
- */
- [super receiveMsg];
- }
-
- /*
- * Used in -timeoutOccurred to determine if specified cmdBuf has timed out.
- * Returns YES if timeout, else NO.
- */
- static inline BOOL
- isCmdTimedOut(commandBuf *cmdBuf, ns_time_t now)
- {
- IOSCSIRequest *scsiReq;
- ns_time_t expire;
-
- scsiReq = cmdBuf->scsiReq;
- expire = cmdBuf->startTime +
- (1000000000ULL * (unsigned long long)scsiReq->timeoutLength);
- return ((now > expire) ? YES : NO);
- }
-
- /*
- * Called from the I/O thread when it receives a timeout
- * message. We send these messages ourself from AMDTimeout().
- */
- - (void)timeoutOccurred
- {
- ns_time_t now;
- BOOL cmdTimedOut = NO;
- commandBuf *cmdBuf;
- commandBuf *nextCmdBuf;
-
- ddm_thr("timeoutOccurred: TOP\n", 1,2,3,4,5);
- IOGetTimestamp(&now);
-
- /*
- * Scan activeCmd and disconnectQ looking for tardy I/Os.
- */
- if(activeCmd) {
- cmdBuf = activeCmd;
- if(isCmdTimedOut(cmdBuf, now)) {
- ddm_thr("activeCmd TIMEOUT, cmd 0x%x\n",
- cmdBuf, 2,3,4,5);
- activeCmd = NULL;
- ASSERT(cmdBuf->scsiReq != NULL);
- cmdBuf->scsiReq->driverStatus = SR_IOST_IOTO;
- [self ioComplete:cmdBuf];
- cmdTimedOut = YES;
- }
- }
-
- cmdBuf = (commandBuf *)queue_first(&disconnectQ);
- while(!queue_end(&disconnectQ, (queue_entry_t)cmdBuf)) {
- if(isCmdTimedOut(cmdBuf, now)) {
- ddm_thr("disconnected cmd TIMEOUT, cmd 0x%x\n",
- cmdBuf, 2,3,4,5);
-
- /*
- * Remove cmdBuf from disconnectQ and
- * complete it.
- */
- nextCmdBuf = (commandBuf *)queue_next(&cmdBuf->link);
- queue_remove(&disconnectQ, cmdBuf, commandBuf *, link);
- ASSERT(cmdBuf->scsiReq != NULL);
- cmdBuf->scsiReq->driverStatus = SR_IOST_IOTO;
- [self ioComplete:cmdBuf];
- cmdBuf = nextCmdBuf;
- cmdTimedOut = YES;
- }
- else {
- cmdBuf = (commandBuf *)queue_next(&cmdBuf->link);
- }
- }
-
- /*
- * Reset bus. This also completes all I/Os in disconnectQ with
- * status CS_Reset.
- */
- if(cmdTimedOut) {
- [self logRegs];
- [self threadResetBus:NULL];
- }
- ddm_thr("timeoutOccurred: DONE\n", 1,2,3,4,5);
- }
-
- /*
- * Process all commands in commandQ. At most one of these will become
- * activeCmd. The remainder of CO_Execute commands go to pendingQ. Other
- * types of commands are executed immediately.
- */
- - (void)commandRequestOccurred
- {
- commandBuf *cmdBuf;
- commandBuf *pendCmd;
-
- ddm_thr("commandRequestOccurred: top\n", 1,2,3,4,5);
- [commandLock lock];
- while(!queue_empty(&commandQ)) {
- cmdBuf = (commandBuf *) queue_first(&commandQ);
- queue_remove(&commandQ, cmdBuf, commandBuf *, link);
- [commandLock unlock];
-
- switch(cmdBuf->op) {
- case CO_Reset:
- /*
- * Note all active and disconnected commands will
- * be terminted.
- */
- [self threadResetBus:"Reset Command Received"];
- [self ioComplete:cmdBuf];
- break;
-
- case CO_Abort:
- /*
- * 1. Abort all active, pending, and disconnected
- * commands.
- * 2. Notify caller of completion.
- * 3. Self-terminate.
- */
- [self swAbort:SR_IOST_INT];
- pendCmd = (commandBuf *)queue_first(&pendingQ);
- while(!queue_end(&pendingQ,
- (queue_entry_t)pendCmd)) {
- pendCmd->scsiReq->driverStatus = SR_IOST_INT;
- [self ioComplete:pendCmd];
- pendCmd = (commandBuf *)
- queue_next(&pendCmd->link);
- }
- [cmdBuf->cmdLock lock];
- [cmdBuf->cmdLock unlockWith:CMD_COMPLETE];
- IOExitThread();
- /* not reached */
-
- case CO_Execute:
- [self threadExecuteRequest:cmdBuf];
- break;
-
- }
- [commandLock lock];
- }
- [commandLock unlock];
- ddm_thr("commandRequestOccurred: DONE\n", 1,2,3,4,5);
- return;
- }
-
- /*
- * Power management methods. All we care about is power off, when we must
- * reset the SCSI bus due to the Compaq BIOS's lack of a SCSI reset, which
- * causes a hang if we have set up targets for sync data transfer mode.
- */
- - (IOReturn)getPowerState:(PMPowerState *)state_p
- {
- ddm_exp("getPowerState called\n", 1,2,3,4,5);
- return IO_R_UNSUPPORTED;
- }
-
- - (IOReturn)setPowerState:(PMPowerState)state
- {
- #ifdef DEBUG
- IOLog("%s: received setPowerState: with %x\n", [self name],
- (unsigned)state);
- #endif DEBUG
- if (state == PM_OFF) {
- [self scsiReset];
- return IO_R_SUCCESS;
- }
- return IO_R_UNSUPPORTED;
- }
-
- - (IOReturn)getPowerManagement:(PMPowerManagementState *)state_p
- {
- ddm_exp("getPowerManagement called\n", 1,2,3,4,5);
- return IO_R_UNSUPPORTED;
- }
-
- - (IOReturn)setPowerManagement:(PMPowerManagementState)state
- {
- ddm_exp("setPowerManagement called\n", 1,2,3,4,5);
- return IO_R_UNSUPPORTED;
- }
-
- #if AMD_ENABLE_GET_SET
-
- - (IOReturn)setIntValues:(unsigned *)parameterArray
- forParameter:(IOParameterName)parameterName
- count:(unsigned int)count
- {
- if(strcmp(parameterName, AMD_AUTOSENSE) == 0) {
- if (count != 1) {
- return IO_R_INVALID_ARG;
- }
- autoSenseEnable = (parameterArray[0] ? 1 : 0);
- IOLog("%s: autoSense %s\n", [self name],
- (autoSenseEnable ? "Enabled" : "Disabled"));
- return IO_R_SUCCESS;
- }
- else if(strcmp(parameterName, AMD_CMD_QUEUE) == 0) {
- if (count != 1) {
- return IO_R_INVALID_ARG;
- }
- cmdQueueEnable = (parameterArray[0] ? 1 : 0);
- IOLog("%s: cmdQueue %s\n", [self name],
- (cmdQueueEnable ? "Enabled" : "Disabled"));
- return IO_R_SUCCESS;
- }
- else if(strcmp(parameterName, AMD_SYNC) == 0) {
- if (count != 1) {
- return IO_R_INVALID_ARG;
- }
- syncModeEnable = (parameterArray[0] ? 1 : 0);
- IOLog("%s: syncMode %s\n", [self name],
- (syncModeEnable ? "Enabled" : "Disabled"));
- return IO_R_SUCCESS;
- }
- else if(strcmp(parameterName, AMD_FAST_SCSI) == 0) {
- if (count != 1) {
- return IO_R_INVALID_ARG;
- }
- fastModeEnable = (parameterArray[0] ? 1 : 0);
- IOLog("%s: fastMode %s\n", [self name],
- (fastModeEnable ? "Enabled" : "Disabled"));
- return IO_R_SUCCESS;
- }
- else if(strcmp(parameterName, AMD_RESET_TARGETS) == 0) {
- int target;
- perTargetData *perTargetPtr;
-
- if (count != 0) {
- return IO_R_INVALID_ARG;
- }
-
- /*
- * Re-enable sync and command queueing. The
- * disable bits persist after a reset.
- */
- for(target=0; target<SCSI_NTARGETS; target++) {
- perTargetPtr = &perTarget[target];
- perTargetPtr->cmdQueueDisable = 0;
- perTargetPtr->syncDisable = 0;
- perTargetPtr->maxQueue = 0;
- }
- IOLog("%s: Per Target disable flags cleared\n", [self name]);
- return IO_R_SUCCESS;
- }
- else {
- return [super setIntValues:parameterArray
- forParameter:parameterName
- count:count];
- }
- }
-
- - (IOReturn)getIntValues : (unsigned *)parameterArray
- forParameter : (IOParameterName)parameterName
- count : (unsigned *)count; // in/out
- {
- if(strcmp(parameterName, AMD_AUTOSENSE) == 0) {
- if(*count != 1) {
- return IO_R_INVALID_ARG;
- }
- parameterArray[0] = autoSenseEnable;
- return IO_R_SUCCESS;
- }
- else if(strcmp(parameterName, AMD_CMD_QUEUE) == 0) {
- if(*count != 1) {
- return IO_R_INVALID_ARG;
- }
- parameterArray[0] = cmdQueueEnable;
- return IO_R_SUCCESS;
- }
- else if(strcmp(parameterName, AMD_SYNC) == 0) {
- if(*count != 1) {
- return IO_R_INVALID_ARG;
- }
- parameterArray[0] = syncModeEnable;
- return IO_R_SUCCESS;
- }
- else if(strcmp(parameterName, AMD_FAST_SCSI) == 0) {
- if(*count != 1) {
- return IO_R_INVALID_ARG;
- }
- parameterArray[0] = fastModeEnable;
- return IO_R_SUCCESS;
- }
- else {
- return [super getIntValues : parameterArray
- forParameter : parameterName
- count : count];
-
- }
- }
-
-
- #endif AMD_ENABLE_GET_SET
-
- @end /* AMD_SCSI */
-
- @implementation AMD_SCSI(Private)
-
- /*
- * Private chip- and architecture-independent methods.
- */
-
- /*
- * Pass one commandBuf to the I/O thread; wait for completion.
- * Normal completion status is in cmdBuf->scsiReq->driverStatus;
- * a non-zero return from this function indicates a Mach IPC error.
- *
- * This method allocates and frees cmdBuf->cmdLock.
- */
- - (IOReturn)executeCmdBuf : (commandBuf *)cmdBuf
- {
- msg_header_t msg = cmdMessageTemplate;
- kern_return_t krtn;
- IOReturn rtn = IO_R_SUCCESS;
-
- cmdBuf->cmdPendingSense = NULL;
- cmdBuf->active = 0;
- cmdBuf->cmdLock = [[NXConditionLock alloc] initWith:CMD_PENDING];
- [commandLock lock];
- queue_enter(&commandQ, cmdBuf, commandBuf *, link);
- [commandLock unlock];
-
- /*
- * Create a Mach message and send it in order to wake up the
- * I/O thread.
- */
- msg.msg_remote_port = interruptPortKern;
- krtn = msg_send_from_kernel(&msg, MSG_OPTION_NONE, 0);
- if(krtn) {
- IOLog("%s: msg_send_from_kernel() returned %d\n",
- [self name], krtn);
- rtn = IO_R_IPC_FAILURE;
- goto out;
- }
-
- /*
- * Wait for I/O complete.
- */
- ddm_exp("executeCmdBuf: waiting for completion on cmdBuf 0x%x\n",
- cmdBuf, 2,3,4,5);
- [cmdBuf->cmdLock lockWhen:CMD_COMPLETE];
- out:
- [cmdBuf->cmdLock free];
- return rtn;
- }
-
- /*
- * Abort all active and disconnected commands with specified status. No
- * hardware action. Currently used by threadResetBus and during processing
- * of a CO_Abort command.
- */
- - (void)swAbort : (sc_status_t)status
- {
- commandBuf *cmdBuf;
- commandBuf *nextCmdBuf;
-
- ddm_thr("swAbort\n", 1,2,3,4,5);
- if(activeCmd) {
- activeCmd->scsiReq->driverStatus = status;
- [self ioComplete:activeCmd];
- activeCmd = NULL;
- }
- cmdBuf = (commandBuf *)queue_first(&disconnectQ);
- while(!queue_end(&disconnectQ, (queue_entry_t)cmdBuf)) {
- queue_remove(&disconnectQ, cmdBuf, commandBuf *, link);
- nextCmdBuf = (commandBuf *)
- queue_next(&cmdBuf->link);
- cmdBuf->scsiReq->driverStatus = status;
- [self ioComplete:cmdBuf];
- cmdBuf = nextCmdBuf;
- }
- #ifdef DEBUG
- /*
- * activeArray "should be" empty...if not, make sure it is for debug.
- */
- {
- int target, lun;
- int active;
-
- for(target=0; target<SCSI_NTARGETS; target++) {
- for(lun=0; lun<SCSI_NLUNS; lun++) {
- active = activeArray[target][lun];
- if(active) {
- IOLog("swAbort: activeArray[%d][%d] = %d\n",
- target, lun, active);
- activeCount -= active;
- activeArray[target][lun] = 0;
- }
- }
- }
- if(activeCount != 0) {
- IOLog("swAbort: activeCount = %d\n", activeCount);
- activeCount = 0;
- }
- }
- #endif DEBUG
- }
-
- /*
- * Abort all active and disconnected commands with status SR_IOST_RESET.
- * Reset hardware and SCSI bus. If there is a command in pendingQ, start
- * it up.
- */
- - (void)threadResetBus : (const char *)reason
- {
- [self swAbort:SR_IOST_RESET];
- [self hwReset : reason];
- [self busFree];
- }
-
- /*
- * Commence processing of the specified command.
- *
- * If activeCmd is non-NULL or cmdBufOK says we can't process this command,
- * we just enqueue the command on the end of pendingQ.
- */
- - (void)threadExecuteRequest : (commandBuf *)cmdBuf
- {
- #if DDM_DEBUG
- unsigned char target = cmdBuf->scsiReq->target;
- unsigned char lun = cmdBuf->scsiReq->lun;
- #endif DDM_DEBUG
-
- if(activeCmd != NULL) {
- ddm_thr("threadExecuteRequest: ACTIVE; adding 0x%x to "
- "pendingQ\n", cmdBuf, 2,3,4,5);
- queue_enter(&pendingQ, cmdBuf, commandBuf *, link);
- return;
- }
- else if([self cmdBufOK:cmdBuf] == NO) {
- ddm_thr("threadExecuteRequest: !cmdBufOK; adding 0x%x to "
- "pendingQ\n", cmdBuf, 2,3,4,5);
- queue_enter(&pendingQ, cmdBuf, commandBuf *, link);
- return;
- }
-
- ddm_thr("calling hwStart: cmdBuf 0x%x activeArray[%d][%d] = %d\n",
- cmdBuf, target, lun, activeArray[target][lun], 5);
-
- switch([self hwStart:cmdBuf]) {
- case HWS_OK: // cool
- case HWS_BUSY: // h/w can't take cmd now
- break;
- case HWS_REJECT: // hw ready for new cmd
- ddm_thr("threadExecuteRequest: calling busFree\n", 1,2,3,4,5);
- [self busFree];
- }
-
- ddm_thr("threadExecuteRequest(0x%x): DONE\n", cmdBuf, 2,3,4,5);
- }
-
- /*
- * Methods called by hardware-dependent modules.
- */
-
- #if TEST_QUEUE_FULL
- int testQueueFull;
- #endif TEST_QUEUE_FULL
-
- /*
- * Called when a transaction associated with cmdBuf is complete. Notify
- * waiting thread. If cmdBuf->scsiReq exists (i.e., this is not a reset
- * or an abort), scsiReq->driverStatus must be valid. If cmdBuf is active,
- * caller must remove from activeCmd. We decrement activeArray[][] counter
- * if appropriate.
- */
- - (void)ioComplete:(commandBuf *)cmdBuf
- {
- ns_time_t currentTime;
- IOSCSIRequest *scsiReq = cmdBuf->scsiReq;
- int target;
- int lun;
-
- if(cmdBuf->cmdPendingSense != NULL) {
- /*
- * This was an autosense request.
- */
-
- commandBuf *origCmdBuf = cmdBuf->cmdPendingSense;
- esense_reply_t *alignedSense = cmdBuf->buffer;
-
- ASSERT(cmdBuf->scsiReq != NULL);
- ddm_thr("autosense buf 0x%x complete for cmd 0x%x sense "
- "key 0x%x\n",
- cmdBuf, origCmdBuf, alignedSense->er_sensekey, 4,5);
- if(cmdBuf->scsiReq->driverStatus == SR_IOST_GOOD) {
- /*
- * Copy aligned sense data to caller's buffer.
- */
- alignedSense = cmdBuf->buffer;
- origCmdBuf->scsiReq->senseData = *alignedSense;
- origCmdBuf->scsiReq->driverStatus = SR_IOST_CHKSV;
- }
- else {
- IOLog("AMD53C974: Autosense request for target %d"
- " FAILED (%s)\n",
- cmdBuf->scsiReq->target,
- IOFindNameForValue(scsiReq->driverStatus,
- IOScStatusStrings));
- origCmdBuf->scsiReq->driverStatus = SR_IOST_CHKSNV;
- }
-
- /*
- * Free all of the allocated memory associated with
- * this autosense request.
- */
- [self deactivateCmd:cmdBuf];
- IOFree(cmdBuf->scsiReq, sizeof(*cmdBuf->scsiReq));
- IOFree(cmdBuf->unalignedSense,
- sizeof(esense_reply_t) + (2 * AMD_READ_START_ALIGN));
- IOFree(cmdBuf, sizeof(commandBuf));
-
- /*
- * Now complete the I/O for the original commandBuf.
- */
- [origCmdBuf->cmdLock lock];
- [origCmdBuf->cmdLock unlockWith:YES];
- return;
- }
- if(scsiReq != NULL) {
- IOGetTimestamp(¤tTime);
- scsiReq->totalTime = currentTime - cmdBuf->startTime;
- scsiReq->bytesTransferred =
- scsiReq->maxTransfer - cmdBuf->currentByteCount;
-
- /*
- * Catch bad SCSI status now.
- */
- if(scsiReq->driverStatus == SR_IOST_GOOD) {
- #if TEST_QUEUE_FULL
- if(testQueueFull &&
- (activeArray[scsiReq->target][scsiReq->lun] > 1)) {
- scsiReq->scsiStatus = STAT_QUEUE_FULL;
- testQueueFull = 0;
- }
- #endif TEST_QUEUE_FULL
- switch(scsiReq->scsiStatus) {
- case STAT_GOOD:
- break;
- case STAT_CHECK:
- if(autoSenseEnable) {
- /*
- * Generate an autosense request, enqueue
- * on pendingQ.
- */
- [self generateAutoSense:cmdBuf];
- if(cmdBuf->active) {
- [self deactivateCmd:cmdBuf];
- }
- return;
- }
- else {
- scsiReq->driverStatus = SR_IOST_CHKSNV;
- }
- break;
-
- case STAT_QUEUE_FULL:
- /*
- * Avoid notifying client of this condition;
- * update perTarget.maxQueue and place this
- * request on pendingQ. We'll try this
- * again when we ioComplete at least one
- * command in this target's queue.
- */
- if(cmdBuf->queueTag == QUEUE_TAG_NONTAGGED) {
- /*
- * Huh? We're not doing command
- * queueing...
- */
- scsiReq->driverStatus = SR_IOST_BADST;
- break;
- }
- target = scsiReq->target;
- lun = scsiReq->lun;
- if(cmdBuf->active) {
- [self deactivateCmd:cmdBuf];
- }
- perTarget[target].maxQueue =
- activeArray[target][lun];
- ddm_thr("Target %d QUEUE FULL, maxQueue %d\n",
- target, perTarget[target].maxQueue,
- 3,4,5);
- queue_enter(&pendingQ, cmdBuf, commandBuf *,
- link);
- return;
-
- default:
- scsiReq->driverStatus = SR_IOST_BADST;
- break;
- }
- }
- }
- if(cmdBuf->active) {
- /*
- * Note that the active flag is false for non-CO_Execute
- * commands and commands aborted from pendingQ.
- */
- [self deactivateCmd:cmdBuf];
- }
-
- #if DDM_DEBUG
- {
- const char *status;
- unsigned moved;
-
- if(scsiReq != NULL) {
- status = IOFindNameForValue(scsiReq->driverStatus,
- IOScStatusStrings);
- moved = scsiReq->bytesTransferred;
- }
- else {
- status = "Complete";
- moved = 0;
- }
- ddm_thr("ioComplete: cmdBuf 0x%x status %s bytesXfr 0x%x\n",
- cmdBuf, status, moved,4,5);
- }
- #endif DDM_DEBUG
-
- [cmdBuf->cmdLock lock];
- [cmdBuf->cmdLock unlockWith:YES];
- }
-
- /*
- * Generate autosense request for specified cmdBuf, place it
- * at head of pendingQ.
- */
- - (void)generateAutoSense : (commandBuf *)cmdBuf
- {
- IOSCSIRequest *scsiReq = cmdBuf->scsiReq;
- commandBuf *senseCmdBuf;
- IOSCSIRequest *senseScsiReq;
- cdb_6_t *cdbp;
-
- senseCmdBuf = IOMalloc(sizeof(commandBuf));
- senseScsiReq = IOMalloc(sizeof(IOSCSIRequest));
- bzero(senseCmdBuf, sizeof(commandBuf));
- bzero(senseScsiReq, sizeof(IOSCSIRequest));
-
- /*
- * commandBuf fields....
- */
- senseCmdBuf->cmdPendingSense = cmdBuf;
- senseCmdBuf->op = CO_Execute;
- senseCmdBuf->scsiReq = senseScsiReq;
-
- /*
- * Get aligned sense buffer.
- */
- senseCmdBuf->unalignedSense = IOMalloc(sizeof(esense_reply_t) +
- (2 * AMD_READ_START_ALIGN));
- senseCmdBuf->buffer = IOAlign(void *,
- senseCmdBuf->unalignedSense,
- AMD_READ_START_ALIGN);
- senseCmdBuf->client = IOVmTaskSelf();
-
- /*
- * Now IOSCSIRequest fields for request sense.
- */
- senseScsiReq->target = scsiReq->target;
- senseScsiReq->lun = scsiReq->lun;
- senseScsiReq->read = YES;
- senseScsiReq->maxTransfer = sizeof(esense_reply_t);
- senseScsiReq->timeoutLength = 10;
- senseScsiReq->disconnect = 0;
-
- cdbp = &senseScsiReq->cdb.cdb_c6;
- cdbp->c6_opcode = C6OP_REQSENSE;
- cdbp->c6_lun = scsiReq->lun;
- cdbp->c6_len = sizeof(esense_reply_t);
- senseScsiReq->driverStatus = SR_IOST_INVALID;
-
- /*
- * This goes at the head of pendingQ; hopefully it'll be the
- * next command out to the bus.
- */
- ddm_thr("generateAutoSense: autosense buf 0x%x enqueued for "
- "cmdBuf 0x%x\n", senseCmdBuf, cmdBuf, 3,4,5);
- queue_enter_first(&pendingQ, senseCmdBuf, commandBuf *, link);
-
- }
-
- /*
- * I/O associated with activeCmd has disconnected. Place it on disconnectQ
- * and enable another transaction.
- */
- - (void)disconnect
- {
- ddm_thr("DISCONNECT: cmdBuf 0x%x target %d lun %d tag %d\n",
- activeCmd, activeCmd->scsiReq->target,
- activeCmd->scsiReq->lun, activeCmd->queueTag, 5);
- queue_enter(&disconnectQ,
- activeCmd,
- commandBuf *,
- link);
- #if DDM_DEBUG
- if((activeCmd->currentByteCount != activeCmd->scsiReq->maxTransfer) &&
- (activeCmd->currentByteCount != 0)) {
- ddm_thr("disconnect after partial DMA (max 0x%d curr 0x%x)\n",
- activeCmd->scsiReq->maxTransfer,
- activeCmd->currentByteCount, 3,4,5);
- }
- #endif DDM_DEBUG
- /*
- * Record this time so that activeCmd can be billed for
- * disconnect latency at reselect time.
- */
- IOGetTimestamp(&activeCmd->disconnectTime);
- activeCmd = NULL;
- /* [self busFree]; NO! fsm does this at end of hwInterrupt! */
- }
-
- /*
- * Specified target, lun, and queueTag is trying to reselect. If we have
- * a commandBuf for this TLQ nexus on disconnectQ, remove it, make it the
- * current activeCmd, and return YES. Else return NO.
- * A value of zero for queueTag indicates a nontagged command (zero is never
- * used as the queue tag value for a tagged command).
- */
- - (BOOL)reselect : (unsigned char)target_id
- lun : (unsigned char)lun
- queueTag : (unsigned char)queueTag
- {
- commandBuf *cmdBuf;
- IOSCSIRequest *scsiReq;
- ns_time_t currentTime;
-
- cmdBuf = (commandBuf *)queue_first(&disconnectQ);
- while(!queue_end(&disconnectQ, (queue_t)cmdBuf)) {
-
- scsiReq = cmdBuf->scsiReq;
- if((scsiReq->target == target_id) &&
- (scsiReq->lun == lun) &&
- (cmdBuf->queueTag == queueTag)) {
- ddm_thr("RESELECT: target %d lun %d tag %d FOUND;"
- "cmdBuf 0x%x\n",
- target_id, lun, queueTag, cmdBuf, 5);
- queue_remove(&disconnectQ,
- cmdBuf,
- commandBuf *,
- link);
- activeCmd = cmdBuf;
-
- /*
- * Bill this operation for latency time.
- */
- IOGetTimestamp(¤tTime);
- scsiReq->latentTime +=
- (currentTime - activeCmd->disconnectTime);
- return(YES);
- }
- /*
- * Try next element in queue.
- */
- cmdBuf = (commandBuf *)cmdBuf->link.next;
- }
-
- /*
- * Hmm...this is not good! We don't want to talk to this target.
- */
- IOLog("%s: ILLEGAL RESELECT target %d lun %d tag %d\n",
- [self name], target_id, lun, queueTag);
- return(NO);
- }
-
- /*
- * Determine if activeArray[][], maxQueue, cmdQueueEnable, and a
- * command's target and lun show that it's OK to start processing cmdBuf.
- * Returns YES if copacetic.
- */
- - (BOOL)cmdBufOK : (commandBuf *)cmdBuf
- {
- IOSCSIRequest *scsiReq = cmdBuf->scsiReq;
- unsigned target = scsiReq->target;
- unsigned lun = scsiReq->lun;
- unsigned char active;
- unsigned char maxQ;
-
- active = activeArray[target][lun];
- if(active == 0) {
- /*
- * Trivial quiescent case, always OK.
- */
- return YES;
- }
- if((cmdQueueEnable == 0) ||
- (perTarget[target].cmdQueueDisable)) {
- /*
- * No command queueing (either globally or for this target),
- * only one at a time.
- */
- return NO;
- }
- maxQ = perTarget[target].maxQueue;
- if(maxQ == 0) {
- /*
- * We don't know what the target's limit is; go for it.
- */
- return YES;
- }
- if(active >= maxQ) {
- /*
- * T/L's queue full; hold off.
- */
- return NO;
- }
- else {
- return YES;
- }
- }
-
- /*
- * The bus has gone free. Start up a command from pendingQ, if any, and
- * if allowed by cmdQueueEnable and activeArray[][].
- */
- - (void)busFree
- {
- commandBuf *cmdBuf;
-
- ASSERT(activeCmd == NULL);
- if(queue_empty(&pendingQ)) {
- ddm_thr("busFree: pendingQ empty\n", 1,2,3,4,5);
- return;
- }
-
- /*
- * Attempt to find a commandBuf in pendingQ which we are in a position
- * to process.
- */
- cmdBuf = (commandBuf *)queue_first(&pendingQ);
- while(!queue_end(&pendingQ, (queue_entry_t)cmdBuf)) {
- if([self cmdBufOK:cmdBuf]) {
- queue_remove(&pendingQ, cmdBuf, commandBuf *, link);
- ddm_thr("busFree: starting pending cmd 0x%x\n", cmdBuf,
- 2,3,4,5);
- [self threadExecuteRequest:cmdBuf];
- return;
- }
- else {
- cmdBuf = (commandBuf *)queue_next(&cmdBuf->link);
- }
- }
- ddm_thr("busFree: pendingQ non-empty, no commands available\n",
- 1,2,3,4,5);
- }
-
- /*
- * Abort activeCmd (if any) and any disconnected I/Os (if any) and reset
- * the bus due to gross hardware failure.
- * If activeCmd is valid, its scsiReq->driverStatus will be set to 'status'.
- */
- - (void)hwAbort : (sc_status_t)status
- reason : (const char *)reason
- {
- if(activeCmd) {
- activeCmd->scsiReq->driverStatus = status;
- [self ioComplete:activeCmd];
- activeCmd = NULL;
- }
- [self logRegs];
- [self threadResetBus:reason];
- }
-
- /*
- * Called by chip level to indicate that a command has gone out to the
- * hardware.
- */
- - (void)activateCommand : (commandBuf *)cmdBuf
- {
- unsigned char target;
- unsigned char lun;
-
- /*
- * Start timeout timer for this I/O. The timer request is cancelled
- * in ioComplete.
- */
- cmdBuf->timeoutPort = interruptPortKern;
- #if LONG_TIMEOUT
- cmdBuf->scsiReq->timeoutLength = OUR_TIMEOUT;
- #endif LONG_TIMEOUT
- IOScheduleFunc(AMDTimeout, cmdBuf, cmdBuf->scsiReq->timeoutLength);
-
- /*
- * This is the only place where an activeArray[][] counter is
- * incremented (and, hence, the only place where cmdBuf->active is
- * set). The only other place activeCmd is set to non-NULL
- * is in reselect:lun:queueTag.
- */
- activeCmd = cmdBuf;
- target = cmdBuf->scsiReq->target;
- lun = cmdBuf->scsiReq->lun;
- activeArray[target][lun]++;
- activeCount++;
- cmdBuf->active = 1;
-
- /*
- * Accumulate statistics.
- */
- maxQueueLen = MAX(maxQueueLen, activeCount);
- queueLenTotal += activeCount;
- totalCommands++;
- ddm_thr("activateCommand: cmdBuf 0x%x target %d lun %d\n",
- cmdBuf, target, lun, 4,5);
- }
-
- /*
- * Remove specified cmdBuf from "active" status. Update activeArray,
- * activeCount, and unschedule pending timer.
- */
- - (void)deactivateCmd : (commandBuf *)cmdBuf
- {
- IOSCSIRequest *scsiReq = cmdBuf->scsiReq;
- int target, lun;
-
- ASSERT(scsiReq != NULL);
- target = scsiReq->target;
- lun = scsiReq->lun;
- ddm_thr("deactivate cmdBuf 0x%x target %d lun %d activeArray %d\n",
- cmdBuf, target, lun, activeArray[target][lun], 5);
- ASSERT(activeArray[target][lun] != 0);
- activeArray[target][lun]--;
- ASSERT(activeCount != 0);
- activeCount--;
-
- /*
- * Cancel pending timeout request. Commands which timed out don't
- * have a timer request pending anymore.
- */
- if(scsiReq->driverStatus != SR_IOST_IOTO) {
- IOUnscheduleFunc(AMDTimeout, cmdBuf);
- }
- cmdBuf->active = 0;
- }
-
- @end /* AMD_SCSI(Private) */
-
- /*
- * Handle timeouts. We just send a timeout message to the I/O thread
- * so it wakes up.
- */
- static void AMDTimeout(void *arg)
- {
- commandBuf *cmdBuf = arg;
- msg_header_t msg = timeoutMsgTemplate;
-
- ddm_err("AMDTimeout: cmdBuf 0x%x target %d\n", cmdBuf,
- cmdBuf->scsiReq->target, 3,4,5);
- if(!cmdBuf->active) {
- /*
- * Should never happen...
- */
- IOLog("AMD53C974: Timeout on non-active cmdBuf\n");
- return;
- }
- msg.msg_remote_port = cmdBuf->timeoutPort;
- IOLog("AMD53C974: SCSI Timeout\n");
- (void) msg_send_from_kernel(&msg, MSG_OPTION_NONE, 0);
- }
-
-