home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.3 (Developer) / NeXT_Developer-3.3.iso / NextDeveloper / Examples / DriverKit / AMDPCSCSIDriver / AMDPCSCSIDriver_reloc.tproj / AMD_SCSI.m < prev    next >
Encoding:
Text File  |  1995-02-10  |  29.1 KB  |  1,205 lines

  1. /*
  2.  * AMD_SCSI.m - top-level module for AMD 53C974/79C974 PCI SCSI driver. 
  3.  *
  4.  * HISTORY
  5.  * 21 Oct 94    Doug Mitchell at NeXT
  6.  *      Created. 
  7.  */
  8.  
  9. #import "AMD_SCSI.h"
  10. #import "AMD_Private.h"
  11. #import "AMD_x86.h"
  12. #import "AMD_Chip.h"
  13. #import "AMD_ddm.h"
  14. #import "AMD_Regs.h"
  15. #import <mach/message.h>
  16. #import <driverkit/generalFuncs.h>
  17. #import <driverkit/interruptMsg.h>
  18. #import <driverkit/align.h>
  19. #import <driverkit/kernelDriver.h>
  20. #import <kernserv/prototypes.h>
  21.  
  22. static void AMDTimeout(void *arg);
  23.  
  24. /* 
  25.  * Template for command message sent to the I/O thread.
  26.  */
  27. static msg_header_t cmdMessageTemplate = {
  28.     0,                    // msg_unused 
  29.     1,                    // msg_simple 
  30.     sizeof(msg_header_t),            // msg_size 
  31.     MSG_TYPE_NORMAL,            // msg_type 
  32.     PORT_NULL,                // msg_local_port 
  33.     PORT_NULL,                // msg_remote_port - TO
  34.                         // BE FILLED IN 
  35.     IO_COMMAND_MSG                // msg_id 
  36. };
  37.  
  38. /*
  39.  * Template for timeout message.
  40.  */
  41. static msg_header_t timeoutMsgTemplate = {
  42.     0,                    // msg_unused 
  43.     1,                    // msg_simple 
  44.     sizeof(msg_header_t),            // msg_size 
  45.     MSG_TYPE_NORMAL,            // msg_type 
  46.     PORT_NULL,                // msg_local_port 
  47.     PORT_NULL,                // msg_remote_port - TO
  48.                         // BE FILLED IN 
  49.     IO_TIMEOUT_MSG                // msg_id 
  50. };
  51.  
  52.  
  53. @implementation AMD_SCSI
  54.  
  55. /*
  56.  * Create and initialize one instance of AMD_SCSI. The work is done by
  57.  * architecture- and chip-specific modules. 
  58.  */
  59. + (BOOL)probe:deviceDescription
  60. {
  61.     AMD_SCSI *inst = [self alloc];
  62.  
  63.  
  64.     if([inst archInit:deviceDescription] == nil) {
  65.         return NO;
  66.     }
  67.     else {
  68.         return YES;
  69.     }
  70. }
  71.  
  72. - free
  73. {
  74.     commandBuf cmdBuf;
  75.     
  76.     /*
  77.      * First kill the I/O thread if running. 
  78.      */
  79.     if(ioThreadRunning) {
  80.         cmdBuf.op = CO_Abort;
  81.         cmdBuf.scsiReq = NULL;
  82.         [self executeCmdBuf:&cmdBuf];
  83.     }
  84.     
  85.     if(commandLock) {
  86.         [commandLock free];
  87.     }
  88.     if(mdlFree) {
  89.         IOFree(mdlFree, MDL_SIZE * 2 * sizeof(vm_address_t));
  90.     }
  91.     return [super free];
  92. }
  93.  
  94. /*
  95.  * Our max DMA size is 64 K, derived from using 18 MDL entries (note the 
  96.  * first and last entries can refer to chunks as small as 4 bytes). 
  97.  */
  98. - (unsigned)maxDmaSize
  99. {
  100.     return AMD_DMA_PAGE_SIZE * (MDL_SIZE - 2);
  101. }
  102.  
  103. /*
  104.  * Return required DMA alignment for current architecture.
  105.  */
  106. - (void)getDMAAlignment : (IODMAAlignment *)alignment;
  107. {
  108.     alignment->readStart   = AMD_READ_START_ALIGN;
  109.     alignment->writeStart  = AMD_WRITE_START_ALIGN;
  110.     alignment->readLength  = AMD_READ_LENGTH_ALIGN;
  111.     alignment->writeLength = AMD_WRITE_LENGTH_ALIGN;
  112. }
  113.  
  114. /*
  115.  * Statistics support.
  116.  */
  117. - (unsigned int) numQueueSamples
  118. {
  119.     return totalCommands;
  120. }
  121.  
  122.  
  123. - (unsigned int) sumQueueLengths
  124. {
  125.     return queueLenTotal;
  126. }
  127.  
  128.  
  129. - (unsigned int) maxQueueLength
  130. {
  131.     return maxQueueLen;
  132. }
  133.  
  134.  
  135. - (void)resetStats
  136. {
  137.     totalCommands = 0;
  138.     queueLenTotal = 0;
  139.     maxQueueLen   = 0;
  140. }
  141.  
  142. /*
  143.  * Do a SCSI command, as specified by an IOSCSIRequest. All the 
  144.  * work is done by the I/O thread.
  145.  */
  146. - (sc_status_t) executeRequest : (IOSCSIRequest *)scsiReq 
  147.             buffer : (void *)buffer 
  148.             client : (vm_task_t)client
  149. {
  150.     commandBuf cmdBuf;
  151.     
  152.     ddm_exp("executeRequest: cmdBuf 0x%x maxTransfer 0x%x\n", 
  153.         &cmdBuf, scsiReq->maxTransfer,3,4,5);
  154.     
  155.     bzero(&cmdBuf, sizeof(commandBuf));
  156.     cmdBuf.op      = CO_Execute;
  157.     cmdBuf.scsiReq = scsiReq;
  158.     cmdBuf.buffer  = buffer;
  159.     cmdBuf.client  = client;
  160.     scsiReq->driverStatus = SR_IOST_INVALID;
  161.     
  162.     [self executeCmdBuf:&cmdBuf];
  163.     
  164.     ddm_exp("executeRequest: cmdBuf 0x%x complete; driverStatus %s\n", 
  165.         &cmdBuf, IOFindNameForValue(scsiReq->driverStatus, 
  166.                     IOScStatusStrings), 3,4,5);
  167.     return cmdBuf.scsiReq->driverStatus;
  168. }
  169.  
  170.  
  171. /*
  172.  *  Reset the SCSI bus. All the work is done by the I/O thread.
  173.  */
  174. - (sc_status_t)resetSCSIBus
  175. {
  176.     commandBuf cmdBuf;
  177.     
  178.     ddm_exp("resetSCSIBus: cmdBuf 0x%x\n", &cmdBuf, 2,3,4,5);
  179.  
  180.     cmdBuf.op = CO_Reset;
  181.     cmdBuf.scsiReq = NULL;
  182.     [self executeCmdBuf:&cmdBuf];
  183.     ddm_exp("resetSCSIBus: cmdBuf 0x%x DONE\n", &cmdBuf, 2,3,4,5);
  184.     return SR_IOST_GOOD;        // can not fail
  185. }
  186.  
  187. /*
  188.  * The following 6 methods are all called from the I/O thread in 
  189.  * IODirectDevice. 
  190.  */
  191.  
  192. /*
  193.  * Called from the I/O thread when it receives an interrupt message.
  194.  * Currently all work is done by chip-specific module; maybe we should 
  195.  * put this method there....
  196.  */
  197. - (void)interruptOccurred
  198. {
  199.     #if    DDM_DEBUG
  200.     /*
  201.      * calculate interrupt service time if enabled.
  202.      */
  203.     ns_time_t    startTime, endTime, elapsedNs;
  204.     unsigned    elapsedUs = 0;
  205.     
  206.     if(IODDMMasks[AMD_DDM_INDEX] & DDM_INTR) {
  207.         IOGetTimestamp(&startTime);
  208.     }
  209.     ddm_thr("interruptOccurred: TOP\n", 1,2,3,4,5);
  210.     #endif    DDM_DEBUG
  211.  
  212.     [self hwInterrupt];
  213.     
  214.     #if    DDM_DEBUG
  215.     if(IODDMMasks[AMD_DDM_INDEX] & DDM_INTR) {
  216.         IOGetTimestamp(&endTime);
  217.         elapsedNs = endTime - startTime;
  218.         elapsedUs = (unsigned)((elapsedNs + 999ULL) / 1000ULL);
  219.     }
  220.     ddm_intr("interruptOccurred: DONE; elapsed time %d us\n", 
  221.         elapsedUs, 2,3,4,5);
  222.     #endif    DDM_DEBUG
  223. }
  224.  
  225. /*
  226.  * These three should not occur; they are here as error traps. All three are 
  227.  * called out from the I/O thread upon receipt of messages which it should
  228.  * not be seeing.
  229.  */
  230. - (void)interruptOccurredAt:(int)localNum
  231. {
  232.     IOLog("%s: interruptOccurredAt:%d\n", [self name], localNum);
  233. }
  234.  
  235. - (void)otherOccurred:(int)id
  236. {
  237.     IOLog("%s: otherOccurred:%d\n", [self name], id);
  238. }
  239.  
  240. - (void)receiveMsg
  241. {
  242.     IOLog("%s: receiveMsg\n", [self name]);
  243.     
  244.     /*
  245.      * We have to let IODirectDevice take care of this (i.e., dequeue the
  246.      * bogus message).
  247.      */
  248.     [super receiveMsg];
  249. }
  250.  
  251. /*
  252.  * Used in -timeoutOccurred to determine if specified cmdBuf has timed out.
  253.  * Returns YES if timeout, else NO.
  254.  */
  255. static inline BOOL
  256. isCmdTimedOut(commandBuf *cmdBuf, ns_time_t now)
  257. {
  258.     IOSCSIRequest    *scsiReq;
  259.     ns_time_t    expire;
  260.     
  261.     scsiReq = cmdBuf->scsiReq;
  262.     expire  = cmdBuf->startTime + 
  263.         (1000000000ULL * (unsigned long long)scsiReq->timeoutLength);
  264.     return ((now > expire) ? YES : NO);
  265. }
  266.  
  267. /*
  268.  * Called from the I/O thread when it receives a timeout
  269.  * message. We send these messages ourself from AMDTimeout().
  270.  */
  271. - (void)timeoutOccurred
  272. {
  273.     ns_time_t    now;
  274.     BOOL        cmdTimedOut = NO;
  275.     commandBuf    *cmdBuf;
  276.     commandBuf    *nextCmdBuf;
  277.     
  278.     ddm_thr("timeoutOccurred: TOP\n", 1,2,3,4,5);
  279.     IOGetTimestamp(&now);
  280.  
  281.     /*
  282.      *  Scan activeCmd and disconnectQ looking for tardy I/Os.
  283.      */
  284.     if(activeCmd) {
  285.         cmdBuf = activeCmd;
  286.         if(isCmdTimedOut(cmdBuf, now)) {
  287.             ddm_thr("activeCmd TIMEOUT, cmd 0x%x\n", 
  288.                 cmdBuf, 2,3,4,5);
  289.             activeCmd = NULL;
  290.             ASSERT(cmdBuf->scsiReq != NULL);
  291.             cmdBuf->scsiReq->driverStatus = SR_IOST_IOTO;
  292.             [self ioComplete:cmdBuf];
  293.             cmdTimedOut = YES;
  294.         }
  295.     }
  296.  
  297.     cmdBuf = (commandBuf *)queue_first(&disconnectQ);
  298.     while(!queue_end(&disconnectQ, (queue_entry_t)cmdBuf)) {
  299.         if(isCmdTimedOut(cmdBuf, now)) {
  300.             ddm_thr("disconnected cmd TIMEOUT, cmd 0x%x\n", 
  301.                 cmdBuf, 2,3,4,5);
  302.         
  303.             /*
  304.              *  Remove cmdBuf from disconnectQ and
  305.              *  complete it.
  306.              */
  307.             nextCmdBuf = (commandBuf *)queue_next(&cmdBuf->link);
  308.             queue_remove(&disconnectQ, cmdBuf, commandBuf *, link);
  309.             ASSERT(cmdBuf->scsiReq != NULL);
  310.             cmdBuf->scsiReq->driverStatus = SR_IOST_IOTO;
  311.             [self ioComplete:cmdBuf];
  312.             cmdBuf = nextCmdBuf;
  313.             cmdTimedOut = YES;
  314.         }
  315.         else {
  316.             cmdBuf = (commandBuf *)queue_next(&cmdBuf->link);
  317.         }
  318.     }
  319.  
  320.     /*
  321.      * Reset bus. This also completes all I/Os in disconnectQ with
  322.      * status CS_Reset.
  323.      */
  324.     if(cmdTimedOut) {
  325.         [self logRegs];
  326.         [self threadResetBus:NULL];
  327.     }
  328.     ddm_thr("timeoutOccurred: DONE\n", 1,2,3,4,5);
  329. }
  330.  
  331. /*
  332.  * Process all commands in commandQ. At most one of these will become
  333.  * activeCmd. The remainder of CO_Execute commands go to pendingQ. Other
  334.  * types of commands are executed immediately.
  335.  */
  336. - (void)commandRequestOccurred
  337. {
  338.     commandBuf *cmdBuf;
  339.     commandBuf *pendCmd;
  340.     
  341.     ddm_thr("commandRequestOccurred: top\n", 1,2,3,4,5);
  342.     [commandLock lock];
  343.     while(!queue_empty(&commandQ)) {
  344.         cmdBuf = (commandBuf *) queue_first(&commandQ);
  345.         queue_remove(&commandQ, cmdBuf, commandBuf *, link);
  346.         [commandLock unlock];
  347.         
  348.         switch(cmdBuf->op) {
  349.             case CO_Reset:
  350.                 /* 
  351.              * Note all active and disconnected commands will
  352.              * be terminted.
  353.              */
  354.                 [self threadResetBus:"Reset Command Received"];
  355.             [self ioComplete:cmdBuf];
  356.             break;
  357.             
  358.             case CO_Abort:
  359.             /*
  360.              * 1. Abort all active, pending, and disconnected
  361.              *    commands.
  362.              * 2. Notify caller of completion.
  363.              * 3. Self-terminate.
  364.              */
  365.             [self swAbort:SR_IOST_INT];
  366.             pendCmd = (commandBuf *)queue_first(&pendingQ);
  367.             while(!queue_end(&pendingQ, 
  368.                     (queue_entry_t)pendCmd)) {
  369.                 pendCmd->scsiReq->driverStatus = SR_IOST_INT;
  370.                 [self ioComplete:pendCmd];
  371.                 pendCmd = (commandBuf *)
  372.                     queue_next(&pendCmd->link);
  373.             }
  374.             [cmdBuf->cmdLock lock];
  375.             [cmdBuf->cmdLock unlockWith:CMD_COMPLETE];
  376.             IOExitThread();
  377.             /* not reached */
  378.             
  379.             case CO_Execute:
  380.             [self threadExecuteRequest:cmdBuf];
  381.             break;
  382.             
  383.         }
  384.         [commandLock lock];
  385.     }
  386.     [commandLock unlock];
  387.     ddm_thr("commandRequestOccurred: DONE\n", 1,2,3,4,5);
  388.     return;
  389. }
  390.  
  391. /*
  392.  * Power management methods. All we care about is power off, when we must 
  393.  * reset the SCSI bus due to the Compaq BIOS's lack of a SCSI reset, which
  394.  * causes a hang if we have set up targets for sync data transfer mode.
  395.  */
  396. - (IOReturn)getPowerState:(PMPowerState *)state_p
  397. {
  398.      ddm_exp("getPowerState called\n", 1,2,3,4,5);
  399.        return IO_R_UNSUPPORTED;
  400. }
  401.  
  402. - (IOReturn)setPowerState:(PMPowerState)state
  403. {
  404. #ifdef DEBUG
  405.     IOLog("%s: received setPowerState: with %x\n", [self name],
  406.         (unsigned)state);
  407. #endif DEBUG
  408.     if (state == PM_OFF) {
  409.         [self scsiReset];
  410.         return IO_R_SUCCESS;
  411.     }
  412.     return IO_R_UNSUPPORTED;
  413. }
  414.  
  415. - (IOReturn)getPowerManagement:(PMPowerManagementState *)state_p
  416. {
  417.     ddm_exp("getPowerManagement called\n", 1,2,3,4,5);
  418.         return IO_R_UNSUPPORTED;
  419. }
  420.  
  421. - (IOReturn)setPowerManagement:(PMPowerManagementState)state
  422. {
  423.     ddm_exp("setPowerManagement called\n", 1,2,3,4,5);
  424.         return IO_R_UNSUPPORTED;
  425. }
  426.  
  427. #if    AMD_ENABLE_GET_SET
  428.  
  429. - (IOReturn)setIntValues:(unsigned *)parameterArray
  430.     forParameter:(IOParameterName)parameterName
  431.     count:(unsigned int)count
  432. {
  433.         if(strcmp(parameterName, AMD_AUTOSENSE) == 0) {
  434.         if (count != 1) {
  435.             return IO_R_INVALID_ARG;
  436.         }
  437.         autoSenseEnable = (parameterArray[0] ? 1 : 0);
  438.         IOLog("%s: autoSense %s\n", [self name], 
  439.             (autoSenseEnable ? "Enabled" : "Disabled"));
  440.         return IO_R_SUCCESS;
  441.     }
  442.     else if(strcmp(parameterName, AMD_CMD_QUEUE) == 0) {
  443.         if (count != 1) {
  444.             return IO_R_INVALID_ARG;
  445.         }
  446.         cmdQueueEnable = (parameterArray[0] ? 1 : 0);
  447.         IOLog("%s: cmdQueue %s\n", [self name], 
  448.             (cmdQueueEnable ? "Enabled" : "Disabled"));
  449.         return IO_R_SUCCESS;
  450.     }
  451.     else if(strcmp(parameterName, AMD_SYNC) == 0) {
  452.         if (count != 1) {
  453.             return IO_R_INVALID_ARG;
  454.         }
  455.         syncModeEnable = (parameterArray[0] ? 1 : 0);
  456.         IOLog("%s: syncMode %s\n", [self name], 
  457.             (syncModeEnable ? "Enabled" : "Disabled"));
  458.         return IO_R_SUCCESS;
  459.     }
  460.     else if(strcmp(parameterName, AMD_FAST_SCSI) == 0) {
  461.         if (count != 1) {
  462.             return IO_R_INVALID_ARG;
  463.         }
  464.         fastModeEnable = (parameterArray[0] ? 1 : 0);
  465.         IOLog("%s: fastMode %s\n", [self name], 
  466.             (fastModeEnable ? "Enabled" : "Disabled"));
  467.         return IO_R_SUCCESS;
  468.     }
  469.     else if(strcmp(parameterName, AMD_RESET_TARGETS) == 0) {
  470.         int target;
  471.         perTargetData *perTargetPtr;
  472.         
  473.         if (count != 0) {
  474.             return IO_R_INVALID_ARG;
  475.         }
  476.         
  477.         /*
  478.          * Re-enable sync and command queueing. The
  479.          * disable bits persist after a reset.
  480.          */
  481.         for(target=0; target<SCSI_NTARGETS; target++) {
  482.             perTargetPtr = &perTarget[target];
  483.             perTargetPtr->cmdQueueDisable = 0;
  484.             perTargetPtr->syncDisable = 0;
  485.             perTargetPtr->maxQueue = 0;
  486.         }
  487.         IOLog("%s: Per Target disable flags cleared\n", [self name]);
  488.         return IO_R_SUCCESS;
  489.     }
  490.     else {
  491.         return [super setIntValues:parameterArray
  492.                 forParameter:parameterName
  493.                 count:count];
  494.     }
  495. }
  496.  
  497. - (IOReturn)getIntValues        : (unsigned *)parameterArray
  498.                forParameter : (IOParameterName)parameterName
  499.                       count : (unsigned *)count;    // in/out
  500. {    
  501.     if(strcmp(parameterName, AMD_AUTOSENSE) == 0) {
  502.         if(*count != 1) {
  503.             return IO_R_INVALID_ARG;
  504.         }
  505.         parameterArray[0] = autoSenseEnable;
  506.         return IO_R_SUCCESS;
  507.     }
  508.     else if(strcmp(parameterName, AMD_CMD_QUEUE) == 0) {
  509.         if(*count != 1) {
  510.             return IO_R_INVALID_ARG;
  511.         }
  512.         parameterArray[0] = cmdQueueEnable;
  513.         return IO_R_SUCCESS;
  514.     }
  515.     else if(strcmp(parameterName, AMD_SYNC) == 0) {
  516.         if(*count != 1) {
  517.             return IO_R_INVALID_ARG;
  518.         }
  519.         parameterArray[0] = syncModeEnable;
  520.         return IO_R_SUCCESS;
  521.     }
  522.     else if(strcmp(parameterName, AMD_FAST_SCSI) == 0) {
  523.         if(*count != 1) {
  524.             return IO_R_INVALID_ARG;
  525.         }
  526.         parameterArray[0] = fastModeEnable;
  527.         return IO_R_SUCCESS;
  528.     }
  529.     else {
  530.         return [super getIntValues : parameterArray
  531.             forParameter : parameterName
  532.             count : count];
  533.  
  534.     }
  535. }                    
  536.  
  537.  
  538. #endif    AMD_ENABLE_GET_SET
  539.  
  540. @end    /* AMD_SCSI */
  541.  
  542. @implementation AMD_SCSI(Private)
  543.  
  544. /*
  545.  * Private chip- and architecture-independent methods.
  546.  */
  547.  
  548. /*
  549.  * Pass one commandBuf to the I/O thread; wait for completion. 
  550.  * Normal completion status is in cmdBuf->scsiReq->driverStatus; 
  551.  * a non-zero return from this function indicates a Mach IPC error.
  552.  *
  553.  * This method allocates and frees cmdBuf->cmdLock.
  554.  */
  555. - (IOReturn)executeCmdBuf : (commandBuf *)cmdBuf
  556. {
  557.     msg_header_t msg = cmdMessageTemplate;
  558.     kern_return_t krtn;
  559.     IOReturn rtn = IO_R_SUCCESS;
  560.     
  561.     cmdBuf->cmdPendingSense = NULL;
  562.     cmdBuf->active = 0;
  563.     cmdBuf->cmdLock = [[NXConditionLock alloc] initWith:CMD_PENDING];
  564.     [commandLock lock];
  565.     queue_enter(&commandQ, cmdBuf, commandBuf *, link);
  566.     [commandLock unlock];
  567.     
  568.     /*
  569.      * Create a Mach message and send it in order to wake up the 
  570.      * I/O thread.
  571.      */
  572.     msg.msg_remote_port = interruptPortKern;
  573.     krtn = msg_send_from_kernel(&msg, MSG_OPTION_NONE, 0);
  574.     if(krtn) {
  575.         IOLog("%s: msg_send_from_kernel() returned %d\n", 
  576.             [self name], krtn);
  577.         rtn = IO_R_IPC_FAILURE;
  578.         goto out;
  579.     }
  580.     
  581.     /*
  582.      * Wait for I/O complete.
  583.      */
  584.     ddm_exp("executeCmdBuf: waiting for completion on cmdBuf 0x%x\n",
  585.         cmdBuf, 2,3,4,5);
  586.     [cmdBuf->cmdLock lockWhen:CMD_COMPLETE];
  587. out:
  588.     [cmdBuf->cmdLock free];
  589.     return rtn;
  590. }
  591.  
  592. /*
  593.  * Abort all active and disconnected commands with specified status. No 
  594.  * hardware action. Currently used by threadResetBus and during processing
  595.  * of a CO_Abort command.
  596.  */
  597. - (void)swAbort : (sc_status_t)status
  598. {
  599.     commandBuf *cmdBuf;
  600.     commandBuf *nextCmdBuf;
  601.     
  602.     ddm_thr("swAbort\n", 1,2,3,4,5);
  603.     if(activeCmd) {
  604.         activeCmd->scsiReq->driverStatus = status;
  605.         [self ioComplete:activeCmd];
  606.         activeCmd = NULL;
  607.     }
  608.     cmdBuf = (commandBuf *)queue_first(&disconnectQ);
  609.     while(!queue_end(&disconnectQ, (queue_entry_t)cmdBuf)) {
  610.         queue_remove(&disconnectQ, cmdBuf, commandBuf *, link);
  611.         nextCmdBuf = (commandBuf *)
  612.             queue_next(&cmdBuf->link);
  613.         cmdBuf->scsiReq->driverStatus = status;
  614.         [self ioComplete:cmdBuf];
  615.         cmdBuf = nextCmdBuf;
  616.     }
  617. #ifdef    DEBUG
  618.     /*
  619.      * activeArray "should be" empty...if not, make sure it is for debug.
  620.      */
  621.     {
  622.         int target, lun;
  623.         int active;
  624.         
  625.         for(target=0; target<SCSI_NTARGETS; target++) {
  626.         for(lun=0; lun<SCSI_NLUNS; lun++) {
  627.             active = activeArray[target][lun];
  628.             if(active) {
  629.                 IOLog("swAbort: activeArray[%d][%d] = %d\n",
  630.                 target, lun, active);
  631.             activeCount -= active;
  632.             activeArray[target][lun] = 0;
  633.             }
  634.         }
  635.         }
  636.         if(activeCount != 0) {
  637.             IOLog("swAbort: activeCount = %d\n", activeCount);
  638.         activeCount = 0;
  639.         }
  640.     }
  641. #endif    DEBUG
  642. }
  643.  
  644. /*
  645.  * Abort all active and disconnected commands with status SR_IOST_RESET.
  646.  * Reset hardware and SCSI bus. If there is a command in pendingQ, start
  647.  * it up.
  648.  */
  649. - (void)threadResetBus : (const char *)reason
  650. {
  651.     [self swAbort:SR_IOST_RESET];
  652.     [self hwReset : reason];
  653.     [self busFree];
  654. }
  655.  
  656. /*
  657.  * Commence processing of the specified command. 
  658.  *
  659.  * If activeCmd is non-NULL or cmdBufOK says we can't process this command,
  660.  * we just enqueue the command on the end of pendingQ. 
  661.  */
  662. - (void)threadExecuteRequest : (commandBuf *)cmdBuf
  663. {
  664.     #if    DDM_DEBUG
  665.     unsigned char target = cmdBuf->scsiReq->target;
  666.     unsigned char lun = cmdBuf->scsiReq->lun;
  667.     #endif    DDM_DEBUG
  668.     
  669.     if(activeCmd != NULL) {
  670.         ddm_thr("threadExecuteRequest: ACTIVE; adding 0x%x to "
  671.             "pendingQ\n", cmdBuf, 2,3,4,5);
  672.         queue_enter(&pendingQ, cmdBuf, commandBuf *, link);
  673.         return;
  674.     }    
  675.     else if([self cmdBufOK:cmdBuf] == NO) {
  676.         ddm_thr("threadExecuteRequest: !cmdBufOK; adding 0x%x to "
  677.             "pendingQ\n", cmdBuf, 2,3,4,5);
  678.         queue_enter(&pendingQ, cmdBuf, commandBuf *, link);
  679.         return;
  680.     }
  681.  
  682.     ddm_thr("calling hwStart: cmdBuf 0x%x activeArray[%d][%d] = %d\n",
  683.         cmdBuf, target, lun, activeArray[target][lun], 5);
  684.         
  685.     switch([self hwStart:cmdBuf]) {
  686.         case HWS_OK:        // cool
  687.         case HWS_BUSY:        // h/w can't take cmd now
  688.             break;
  689.         case HWS_REJECT:        // hw ready for new cmd
  690.         ddm_thr("threadExecuteRequest: calling busFree\n", 1,2,3,4,5);
  691.         [self busFree];
  692.     }
  693.     
  694.     ddm_thr("threadExecuteRequest(0x%x): DONE\n", cmdBuf, 2,3,4,5);
  695. }
  696.  
  697. /*
  698.  * Methods called by hardware-dependent modules.
  699.  */
  700.  
  701. #if    TEST_QUEUE_FULL
  702. int     testQueueFull;
  703. #endif    TEST_QUEUE_FULL
  704.  
  705. /*
  706.  * Called when a transaction associated with cmdBuf is complete. Notify 
  707.  * waiting thread. If cmdBuf->scsiReq exists (i.e., this is not a reset
  708.  * or an abort), scsiReq->driverStatus must be valid. If cmdBuf is active,
  709.  * caller must remove from activeCmd. We decrement activeArray[][] counter
  710.  * if appropriate.
  711.  */
  712. - (void)ioComplete:(commandBuf *)cmdBuf
  713. {
  714.     ns_time_t     currentTime;
  715.     IOSCSIRequest     *scsiReq = cmdBuf->scsiReq;
  716.     int         target;
  717.     int         lun;
  718.     
  719.     if(cmdBuf->cmdPendingSense != NULL) {
  720.         /*
  721.          * This was an autosense request.
  722.          */
  723.         
  724.         commandBuf *origCmdBuf = cmdBuf->cmdPendingSense;
  725.         esense_reply_t *alignedSense = cmdBuf->buffer;
  726.         
  727.         ASSERT(cmdBuf->scsiReq != NULL);
  728.         ddm_thr("autosense buf 0x%x complete for cmd 0x%x sense "
  729.             "key 0x%x\n",
  730.             cmdBuf, origCmdBuf, alignedSense->er_sensekey, 4,5);
  731.         if(cmdBuf->scsiReq->driverStatus == SR_IOST_GOOD) {
  732.             /*
  733.              * Copy aligned sense data to caller's buffer.
  734.              */
  735.             alignedSense = cmdBuf->buffer;
  736.             origCmdBuf->scsiReq->senseData = *alignedSense;
  737.             origCmdBuf->scsiReq->driverStatus = SR_IOST_CHKSV;
  738.         }
  739.         else {
  740.             IOLog("AMD53C974: Autosense request for target %d"
  741.                 " FAILED (%s)\n",
  742.                 cmdBuf->scsiReq->target, 
  743.                 IOFindNameForValue(scsiReq->driverStatus, 
  744.                     IOScStatusStrings));
  745.             origCmdBuf->scsiReq->driverStatus = SR_IOST_CHKSNV;
  746.         }
  747.         
  748.         /*
  749.          * Free all of the allocated memory associated with 
  750.          * this autosense request. 
  751.          */
  752.         [self deactivateCmd:cmdBuf];
  753.         IOFree(cmdBuf->scsiReq, sizeof(*cmdBuf->scsiReq));
  754.         IOFree(cmdBuf->unalignedSense, 
  755.             sizeof(esense_reply_t) + (2 * AMD_READ_START_ALIGN));
  756.         IOFree(cmdBuf, sizeof(commandBuf));
  757.         
  758.         /*
  759.          * Now complete the I/O for the original commandBuf.
  760.          */
  761.         [origCmdBuf->cmdLock lock];
  762.         [origCmdBuf->cmdLock unlockWith:YES];
  763.         return;
  764.     }
  765.     if(scsiReq != NULL) {
  766.         IOGetTimestamp(¤tTime);
  767.         scsiReq->totalTime = currentTime - cmdBuf->startTime;
  768.         scsiReq->bytesTransferred = 
  769.             scsiReq->maxTransfer - cmdBuf->currentByteCount;
  770.             
  771.         /*
  772.          * Catch bad SCSI status now.
  773.          */
  774.         if(scsiReq->driverStatus == SR_IOST_GOOD) {
  775.             #if    TEST_QUEUE_FULL
  776.             if(testQueueFull && 
  777.                (activeArray[scsiReq->target][scsiReq->lun] > 1)) {
  778.                 scsiReq->scsiStatus = STAT_QUEUE_FULL;
  779.                 testQueueFull = 0;
  780.             }
  781.             #endif    TEST_QUEUE_FULL
  782.             switch(scsiReq->scsiStatus) {
  783.                 case STAT_GOOD:
  784.                 break;
  785.                 case STAT_CHECK:
  786.                 if(autoSenseEnable) {
  787.                     /*
  788.                      * Generate an autosense request, enqueue
  789.                      * on pendingQ.
  790.                      */
  791.                     [self generateAutoSense:cmdBuf];
  792.                     if(cmdBuf->active) {
  793.                     [self deactivateCmd:cmdBuf];
  794.                     }
  795.                     return;
  796.                 }
  797.                 else {
  798.                     scsiReq->driverStatus = SR_IOST_CHKSNV;
  799.                 }
  800.                 break;
  801.                 
  802.                 case STAT_QUEUE_FULL:
  803.                     /*
  804.                  * Avoid notifying client of this condition;
  805.                  * update perTarget.maxQueue and place this 
  806.                  * request on pendingQ. We'll try this 
  807.                  * again when we ioComplete at least one
  808.                  * command in this target's queue.
  809.                  */
  810.                 if(cmdBuf->queueTag == QUEUE_TAG_NONTAGGED) {
  811.                     /*
  812.                      * Huh? We're not doing command
  813.                      * queueing...
  814.                      */
  815.                     scsiReq->driverStatus = SR_IOST_BADST;
  816.                     break;
  817.                 }
  818.                 target = scsiReq->target;
  819.                 lun = scsiReq->lun;
  820.                 if(cmdBuf->active) {
  821.                     [self deactivateCmd:cmdBuf];
  822.                 }
  823.                 perTarget[target].maxQueue = 
  824.                     activeArray[target][lun];
  825.                 ddm_thr("Target %d QUEUE FULL, maxQueue %d\n",
  826.                     target, perTarget[target].maxQueue,
  827.                     3,4,5);
  828.                 queue_enter(&pendingQ, cmdBuf, commandBuf *,
  829.                     link);
  830.                 return;
  831.                 
  832.                 default:
  833.                 scsiReq->driverStatus = SR_IOST_BADST;
  834.                 break;
  835.             }
  836.         }
  837.     }
  838.     if(cmdBuf->active) {
  839.         /*
  840.          * Note that the active flag is false for non-CO_Execute
  841.          * commands and commands aborted from pendingQ.
  842.          */
  843.         [self deactivateCmd:cmdBuf];
  844.     }
  845.     
  846.     #if    DDM_DEBUG
  847.     {
  848.         const char *status;
  849.         unsigned moved;
  850.         
  851.         if(scsiReq != NULL) {
  852.             status = IOFindNameForValue(scsiReq->driverStatus, 
  853.                 IOScStatusStrings);
  854.             moved = scsiReq->bytesTransferred;
  855.         }
  856.         else {
  857.             status = "Complete";
  858.             moved = 0;
  859.         }
  860.         ddm_thr("ioComplete: cmdBuf 0x%x status %s bytesXfr 0x%x\n", 
  861.             cmdBuf, status, moved,4,5);
  862.     }
  863.     #endif    DDM_DEBUG
  864.     
  865.     [cmdBuf->cmdLock lock];
  866.     [cmdBuf->cmdLock unlockWith:YES];
  867. }
  868.  
  869. /*
  870.  * Generate autosense request for specified cmdBuf, place it 
  871.  * at head of pendingQ.
  872.  */
  873. - (void)generateAutoSense : (commandBuf *)cmdBuf
  874. {
  875.     IOSCSIRequest     *scsiReq = cmdBuf->scsiReq;
  876.     commandBuf     *senseCmdBuf;
  877.     IOSCSIRequest     *senseScsiReq;
  878.     cdb_6_t        *cdbp;
  879.     
  880.     senseCmdBuf  = IOMalloc(sizeof(commandBuf));
  881.     senseScsiReq = IOMalloc(sizeof(IOSCSIRequest));
  882.     bzero(senseCmdBuf,  sizeof(commandBuf));
  883.     bzero(senseScsiReq, sizeof(IOSCSIRequest));
  884.     
  885.     /*
  886.      * commandBuf fields....
  887.      */
  888.     senseCmdBuf->cmdPendingSense = cmdBuf;
  889.     senseCmdBuf->op              = CO_Execute;
  890.     senseCmdBuf->scsiReq         = senseScsiReq;
  891.     
  892.     /*
  893.      * Get aligned sense buffer.
  894.      */
  895.     senseCmdBuf->unalignedSense = IOMalloc(sizeof(esense_reply_t) + 
  896.                     (2 * AMD_READ_START_ALIGN));
  897.     senseCmdBuf->buffer         = IOAlign(void *, 
  898.                     senseCmdBuf->unalignedSense,
  899.                     AMD_READ_START_ALIGN);
  900.     senseCmdBuf->client         = IOVmTaskSelf();
  901.     
  902.     /* 
  903.      * Now IOSCSIRequest fields for request sense.
  904.      */
  905.     senseScsiReq->target        = scsiReq->target;
  906.     senseScsiReq->lun           = scsiReq->lun;
  907.     senseScsiReq->read          = YES;
  908.     senseScsiReq->maxTransfer   = sizeof(esense_reply_t);
  909.     senseScsiReq->timeoutLength = 10;
  910.     senseScsiReq->disconnect    = 0;
  911.     
  912.     cdbp                 = &senseScsiReq->cdb.cdb_c6;
  913.     cdbp->c6_opcode         = C6OP_REQSENSE;
  914.     cdbp->c6_lun             = scsiReq->lun;
  915.     cdbp->c6_len             = sizeof(esense_reply_t);
  916.     senseScsiReq->driverStatus  = SR_IOST_INVALID;
  917.     
  918.     /*
  919.      * This goes at the head of pendingQ; hopefully it'll be the 
  920.      * next command out to the bus.
  921.      */
  922.     ddm_thr("generateAutoSense: autosense buf 0x%x enqueued for "
  923.         "cmdBuf 0x%x\n", senseCmdBuf, cmdBuf, 3,4,5);
  924.     queue_enter_first(&pendingQ, senseCmdBuf, commandBuf *, link);
  925.  
  926. }
  927.  
  928. /*
  929.  * I/O associated with activeCmd has disconnected. Place it on disconnectQ
  930.  * and enable another transaction.
  931.  */ 
  932. - (void)disconnect
  933. {
  934.     ddm_thr("DISCONNECT: cmdBuf 0x%x target %d lun %d tag %d\n",
  935.         activeCmd, activeCmd->scsiReq->target,
  936.         activeCmd->scsiReq->lun, activeCmd->queueTag, 5);
  937.     queue_enter(&disconnectQ,
  938.         activeCmd,
  939.         commandBuf *,
  940.         link);
  941.     #if    DDM_DEBUG
  942.     if((activeCmd->currentByteCount != activeCmd->scsiReq->maxTransfer) &&
  943.        (activeCmd->currentByteCount != 0)) {
  944.            ddm_thr("disconnect after partial DMA (max 0x%d curr 0x%x)\n",
  945.             activeCmd->scsiReq->maxTransfer, 
  946.             activeCmd->currentByteCount, 3,4,5);
  947.     }
  948.     #endif    DDM_DEBUG
  949.     /*
  950.      * Record this time so that activeCmd can be billed for
  951.      * disconnect latency at reselect time.
  952.      */
  953.     IOGetTimestamp(&activeCmd->disconnectTime);
  954.     activeCmd = NULL;
  955.     /* [self busFree]; NO! fsm does this at end of hwInterrupt! */
  956. }
  957.  
  958. /*
  959.  * Specified target, lun, and queueTag is trying to reselect. If we have 
  960.  * a commandBuf for this TLQ nexus on disconnectQ, remove it, make it the
  961.  * current activeCmd, and return YES. Else return NO.
  962.  * A value of zero for queueTag indicates a nontagged command (zero is never
  963.  * used as the queue tag value for a tagged command).
  964.  */
  965. - (BOOL)reselect : (unsigned char)target_id
  966.          lun : (unsigned char)lun
  967.         queueTag : (unsigned char)queueTag
  968. {
  969.     commandBuf *cmdBuf;
  970.     IOSCSIRequest *scsiReq;
  971.     ns_time_t currentTime;
  972.     
  973.     cmdBuf = (commandBuf *)queue_first(&disconnectQ);
  974.     while(!queue_end(&disconnectQ, (queue_t)cmdBuf)) {
  975.     
  976.         scsiReq = cmdBuf->scsiReq;
  977.         if((scsiReq->target == target_id) && 
  978.            (scsiReq->lun == lun) &&
  979.            (cmdBuf->queueTag == queueTag)) {
  980.             ddm_thr("RESELECT: target %d lun %d tag %d FOUND;"
  981.                 "cmdBuf 0x%x\n",
  982.                 target_id, lun, queueTag, cmdBuf, 5);
  983.             queue_remove(&disconnectQ,
  984.                 cmdBuf,
  985.                 commandBuf *,
  986.                 link);
  987.             activeCmd = cmdBuf;
  988.             
  989.             /*
  990.              * Bill this operation for latency time.
  991.              */
  992.             IOGetTimestamp(¤tTime);
  993.             scsiReq->latentTime += 
  994.                 (currentTime - activeCmd->disconnectTime);
  995.             return(YES);
  996.         }
  997.         /*
  998.          * Try next element in queue.
  999.          */
  1000.         cmdBuf = (commandBuf *)cmdBuf->link.next;
  1001.     }
  1002.  
  1003.     /*
  1004.      * Hmm...this is not good! We don't want to talk to this target.
  1005.      */    
  1006.     IOLog("%s: ILLEGAL RESELECT target %d lun %d tag %d\n",
  1007.                 [self name], target_id, lun, queueTag);
  1008.     return(NO);
  1009. }
  1010.  
  1011. /*
  1012.  * Determine if activeArray[][], maxQueue, cmdQueueEnable, and a 
  1013.  * command's target and lun show that it's OK to start processing cmdBuf.
  1014.  * Returns YES if copacetic.
  1015.  */
  1016. - (BOOL)cmdBufOK : (commandBuf *)cmdBuf
  1017. {
  1018.     IOSCSIRequest     *scsiReq = cmdBuf->scsiReq;
  1019.     unsigned     target   = scsiReq->target;
  1020.     unsigned     lun      = scsiReq->lun;
  1021.     unsigned char    active;
  1022.     unsigned char    maxQ;
  1023.     
  1024.     active = activeArray[target][lun];
  1025.     if(active == 0) {
  1026.         /*
  1027.          * Trivial quiescent case, always OK.
  1028.          */
  1029.         return YES;
  1030.     }
  1031.     if((cmdQueueEnable == 0) ||
  1032.        (perTarget[target].cmdQueueDisable)) {
  1033.         /*
  1034.          * No command queueing (either globally or for this target),
  1035.          * only one at a time.
  1036.          */
  1037.         return NO;
  1038.     }
  1039.     maxQ = perTarget[target].maxQueue;
  1040.     if(maxQ == 0) {
  1041.         /*
  1042.          * We don't know what the target's limit is; go for it.
  1043.          */
  1044.         return YES;
  1045.     }
  1046.     if(active >= maxQ) {
  1047.         /*
  1048.          * T/L's queue full; hold off.
  1049.          */ 
  1050.         return NO;
  1051.     }    
  1052.     else {
  1053.         return YES;
  1054.     }
  1055. }
  1056.  
  1057. /*
  1058.  * The bus has gone free. Start up a command from pendingQ, if any, and
  1059.  * if allowed by cmdQueueEnable and activeArray[][].
  1060.  */
  1061. - (void)busFree
  1062. {
  1063.     commandBuf *cmdBuf;
  1064.     
  1065.     ASSERT(activeCmd == NULL);
  1066.     if(queue_empty(&pendingQ)) {
  1067.         ddm_thr("busFree: pendingQ empty\n", 1,2,3,4,5);
  1068.         return;
  1069.     }
  1070.     
  1071.     /*
  1072.      * Attempt to find a commandBuf in pendingQ which we are in a position
  1073.      * to process.
  1074.      */
  1075.     cmdBuf = (commandBuf *)queue_first(&pendingQ);
  1076.     while(!queue_end(&pendingQ, (queue_entry_t)cmdBuf)) {
  1077.         if([self cmdBufOK:cmdBuf]) {
  1078.             queue_remove(&pendingQ, cmdBuf, commandBuf *, link);
  1079.             ddm_thr("busFree: starting pending cmd 0x%x\n", cmdBuf,
  1080.                 2,3,4,5);
  1081.             [self threadExecuteRequest:cmdBuf];
  1082.             return;    
  1083.         }
  1084.         else {
  1085.             cmdBuf = (commandBuf *)queue_next(&cmdBuf->link);
  1086.         }
  1087.     }
  1088.     ddm_thr("busFree: pendingQ non-empty, no commands available\n", 
  1089.         1,2,3,4,5);
  1090. }
  1091.  
  1092. /*
  1093.  * Abort activeCmd (if any) and any disconnected I/Os (if any) and reset 
  1094.  * the bus due to gross hardware failure.
  1095.  * If activeCmd is valid, its scsiReq->driverStatus will be set to 'status'.
  1096.  */
  1097. - (void)hwAbort         : (sc_status_t)status
  1098.               reason : (const char *)reason
  1099. {
  1100.     if(activeCmd) {
  1101.         activeCmd->scsiReq->driverStatus = status;
  1102.         [self ioComplete:activeCmd];
  1103.         activeCmd = NULL;
  1104.     }
  1105.     [self logRegs];
  1106.     [self threadResetBus:reason];    
  1107. }
  1108.  
  1109. /*
  1110.  * Called by chip level to indicate that a command has gone out to the 
  1111.  * hardware.
  1112.  */
  1113. - (void)activateCommand : (commandBuf *)cmdBuf
  1114. {
  1115.     unsigned char target;
  1116.     unsigned char lun;
  1117.     
  1118.     /*
  1119.      * Start timeout timer for this I/O. The timer request is cancelled
  1120.      * in ioComplete.
  1121.      */
  1122.     cmdBuf->timeoutPort = interruptPortKern;
  1123.     #if    LONG_TIMEOUT
  1124.     cmdBuf->scsiReq->timeoutLength = OUR_TIMEOUT;
  1125.     #endif    LONG_TIMEOUT
  1126.     IOScheduleFunc(AMDTimeout, cmdBuf, cmdBuf->scsiReq->timeoutLength);
  1127.     
  1128.     /*
  1129.      * This is the only place where an activeArray[][] counter is 
  1130.      * incremented (and, hence, the only place where cmdBuf->active is 
  1131.      * set). The only other place activeCmd is set to non-NULL
  1132.      * is in reselect:lun:queueTag.
  1133.      */
  1134.     activeCmd = cmdBuf;
  1135.     target = cmdBuf->scsiReq->target;
  1136.     lun = cmdBuf->scsiReq->lun;
  1137.     activeArray[target][lun]++;
  1138.     activeCount++;
  1139.     cmdBuf->active = 1;
  1140.  
  1141.     /*
  1142.      * Accumulate statistics.
  1143.      */
  1144.     maxQueueLen = MAX(maxQueueLen, activeCount);
  1145.     queueLenTotal += activeCount;
  1146.     totalCommands++;
  1147.     ddm_thr("activateCommand: cmdBuf 0x%x target %d lun %d\n",
  1148.         cmdBuf, target, lun, 4,5);
  1149. }
  1150.  
  1151. /*
  1152.  * Remove specified cmdBuf from "active" status. Update activeArray,
  1153.  * activeCount, and unschedule pending timer.
  1154.  */
  1155. - (void)deactivateCmd : (commandBuf *)cmdBuf
  1156. {
  1157.     IOSCSIRequest *scsiReq = cmdBuf->scsiReq;
  1158.     int target, lun;
  1159.     
  1160.     ASSERT(scsiReq != NULL);
  1161.     target = scsiReq->target;
  1162.     lun = scsiReq->lun;
  1163.     ddm_thr("deactivate cmdBuf 0x%x target %d lun %d activeArray %d\n",
  1164.         cmdBuf, target, lun, activeArray[target][lun], 5);
  1165.     ASSERT(activeArray[target][lun] != 0);
  1166.     activeArray[target][lun]--;
  1167.     ASSERT(activeCount != 0);
  1168.     activeCount--;
  1169.  
  1170.     /*
  1171.      * Cancel pending timeout request. Commands which timed out don't
  1172.      * have a timer request pending anymore.
  1173.      */
  1174.     if(scsiReq->driverStatus != SR_IOST_IOTO) {
  1175.         IOUnscheduleFunc(AMDTimeout, cmdBuf);
  1176.     }
  1177.     cmdBuf->active = 0;
  1178. }
  1179.  
  1180. @end    /* AMD_SCSI(Private) */
  1181.  
  1182. /*
  1183.  *  Handle timeouts.  We just send a timeout message to the I/O thread
  1184.  *  so it wakes up.
  1185.  */
  1186. static void AMDTimeout(void *arg)
  1187. {
  1188.     commandBuf     *cmdBuf = arg;
  1189.     msg_header_t    msg = timeoutMsgTemplate;
  1190.  
  1191.     ddm_err("AMDTimeout: cmdBuf 0x%x target %d\n", cmdBuf,
  1192.         cmdBuf->scsiReq->target, 3,4,5);
  1193.     if(!cmdBuf->active) {
  1194.         /*
  1195.          * Should never happen...
  1196.          */
  1197.         IOLog("AMD53C974: Timeout on non-active cmdBuf\n");
  1198.         return;
  1199.     }
  1200.     msg.msg_remote_port = cmdBuf->timeoutPort;
  1201.     IOLog("AMD53C974: SCSI Timeout\n");
  1202.     (void) msg_send_from_kernel(&msg, MSG_OPTION_NONE, 0);
  1203. }
  1204.  
  1205.