home *** CD-ROM | disk | FTP | other *** search
/ Openstep 4.2 (Developer) / Openstep Developer 4.2.iso / NextDeveloper / Examples / DriverKit / Adaptec1542B / Adaptec1542B_reloc.tproj / AHAThread.m < prev    next >
Encoding:
Text File  |  1996-04-05  |  14.4 KB  |  619 lines

  1. /*
  2.  * Copyright (c) 1993-1996 NeXT Software, Inc.
  3.  *
  4.  * AHAThread.m - I/O thread methods for Adaptec 1542 driver. 
  5.  *
  6.  * HISTORY
  7.  *
  8.  * 13 Apr 1993    Doug Mitchell at NeXT
  9.  *    Split off from AHAController.m.
  10.  */
  11.  
  12. #import "AHAThread.h"
  13. #import "AHATypes.h"
  14. #import "AHAInline.h"
  15. #import "AHAControllerPrivate.h"
  16. #import "scsivar.h"
  17. #import <driverkit/generalFuncs.h>
  18. #import <driverkit/kernelDriver.h>
  19. #import <kernserv/prototypes.h>
  20. #import <sys/param.h>
  21.  
  22. static void ahaTimeout(void *arg);
  23.  
  24. #define AUTO_SENSE_ENABLE    1
  25.  
  26. /*
  27.  * Template for timeout message.
  28.  */
  29. static msg_header_t timeoutMsgTemplate = {
  30.     0,                    // msg_unused 
  31.     1,                    // msg_simple 
  32.     sizeof(msg_header_t),            // msg_size 
  33.     MSG_TYPE_NORMAL,            // msg_type 
  34.     PORT_NULL,                // msg_local_port 
  35.     PORT_NULL,                // msg_remote_port - TO
  36.                         // BE FILLED IN 
  37.     IO_TIMEOUT_MSG                // msg_id 
  38. };
  39.  
  40. @implementation AHAController(IOThread)
  41.  
  42. /*
  43.  * I/O thread version of -executeRequest:buffer:client.
  44.  * The approximate logic is:
  45.  *    Build up an internal ccb describing this request
  46.  *    Put it on the queue of pending commands
  47.  *    Run as many pending commands as possible
  48.  *
  49.  * Returns non-zero if no ccb was available for the command. This case 
  50.  * must be handled gracefully by the caller by enqueueing the request on
  51.  * commandQ.
  52.  */
  53. - (int)threadExecuteRequest    : (AHACommandBuf *)cmdBuf
  54. {
  55.     struct ccb    *ccb;
  56.     IOSCSIRequest    *scsiReq = cmdBuf->scsiReq;
  57.     
  58.     ddm_thr("threadExecuteRequest cmdBuf 0x%x\n", cmdBuf, 2,3,4,5);
  59.     
  60.     ccb = [self allocCcb:(scsiReq->maxTransfer ? YES : NO)];
  61.     if(ccb == NULL) {
  62.         return 1;
  63.     }
  64.     if([self ccbFromCmd:cmdBuf ccb:ccb]) {
  65.         /*
  66.          * Command reject. Error status is in 
  67.          * cmdBuf->scsiReq->driverStatus. 
  68.          * Notify caller and clean up.
  69.          */
  70.         [self freeCcb:ccb];
  71.         [cmdBuf->cmdLock lock];
  72.         [cmdBuf->cmdLock unlockWith:CMD_COMPLETE];
  73.         return 0;
  74.     }
  75.  
  76.     /*
  77.      *  Make sure we'll be able to time this command out.  This should be
  78.      *  rare, so we don't particularly care about how efficient it is.
  79.      */
  80.     ccb->timeoutPort = interruptPortKern;
  81.     IOScheduleFunc(ahaTimeout, ccb, scsiReq->timeoutLength);
  82.  
  83.     /*
  84.      * Stick this command on the list of pending ones, and run them.
  85.      */
  86.     queue_enter(&pendingQ, ccb, struct ccb *, ccbQ);
  87.     [self runPendingCommands];
  88.     return 0;
  89.  
  90. }
  91.  
  92. /*
  93.  * I/O thread version of -resetSCSIBus.
  94.  * We also interpret this to mean we should reset the board.
  95.  * cmdBuf == NULL indicates a call from within the I/O thread for
  96.  * a reason other than -resetSCSIBus (e.g., timeout recovery). 
  97.  */
  98. - (void)threadResetBus : (AHACommandBuf *)cmdBuf
  99. {
  100.  
  101.     aha_ctrl_reg_t    ctrl = { 0 };
  102.         struct ccb *ccb;
  103.     queue_head_t *q;
  104.     
  105.     ddm_thr("threadResetBus\n", 1,2,3,4,5);
  106.     
  107.     /*
  108.      * Abort all outstanding and pending commands. 
  109.      */
  110.     for(q=&outstandingQ; q!=&pendingQ; q=&pendingQ)    {
  111.         while(!queue_empty(q)) {
  112.             ccb = (struct ccb *)queue_first(q);
  113.             queue_remove(q, ccb, struct ccb *, ccbQ);
  114.             if(q == &outstandingQ) {
  115.                 ASSERT(outstandingCount != 0);
  116.                 outstandingCount--;
  117.             }
  118.             [self commandCompleted:ccb reason:CS_Reset];
  119.         }
  120.     }
  121.     
  122.     /*
  123.      * Now reset the hardware.
  124.      */
  125.     aha_reset_board(ioBase, ahaBoardId);
  126.     aha_setup_mb_area(ioBase, ahaMbArea, ahaCcb);
  127.  
  128.     ctrl.scsi_rst = 1;
  129.     aha_put_ctrl(ioBase, ctrl);
  130.  
  131.     IOLog("Resetting SCSI Bus...\n");
  132.     IOSleep(10000);
  133.     
  134.     /*
  135.      * Notify caller of completion if appropriate.
  136.      */
  137.     if(cmdBuf) {
  138.         ddm_thr("threadResetBus: I/O complete on cmdBuf 0x%x\n",
  139.             cmdBuf, 2,3,4,5);
  140.         cmdBuf->result = SR_IOST_GOOD;
  141.         [cmdBuf->cmdLock lock];
  142.         [cmdBuf->cmdLock unlockWith:CMD_COMPLETE];
  143.     }
  144. }
  145.  
  146. /*
  147.  * Build a ccb from the specified AHACommandBuf. Returns non-zero on error
  148.  * (i.e., on command reject from this method). In that case, error status 
  149.  * is in cmdBuf->scsiReq->driverStatus.
  150.  */
  151. - (int) ccbFromCmd:(AHACommandBuf *)cmdBuf ccb:(struct ccb *)ccb
  152. {
  153.     IOSCSIRequest        *scsiReq = cmdBuf->scsiReq;
  154.     union cdb        *cdbp = &scsiReq->cdb;
  155.     int            cdb_ctrl;
  156.     vm_offset_t        addr, phys;
  157.     vm_size_t        len;
  158.     unsigned int        pages;
  159.     unsigned int        cmdlen;
  160.     
  161.     /*
  162.      * Figure out what kind of cdb we've been given
  163.      * and snag the ctrl byte
  164.      */
  165.     switch (SCSI_OPGROUP(cdbp->cdb_opcode)) {
  166.  
  167.         case OPGROUP_0:
  168.         cmdlen = sizeof (struct cdb_6);
  169.         cdb_ctrl = cdbp->cdb_c6.c6_ctrl;
  170.         break;
  171.     
  172.         case OPGROUP_1:
  173.         case OPGROUP_2:
  174.         cmdlen = sizeof (struct cdb_10);
  175.         cdb_ctrl = cdbp->cdb_c10.c10_ctrl;
  176.         break;
  177.     
  178.         case OPGROUP_5:
  179.         cmdlen = sizeof (struct cdb_12);
  180.         cdb_ctrl = cdbp->cdb_c12.c12_ctrl;
  181.         break;
  182.     
  183.             /*
  184.          * Group 6 and 7 commands allow a user-specified CDB length.
  185.          */
  186.         case OPGROUP_6:
  187.         if(scsiReq->cdbLength)
  188.              cmdlen = scsiReq->cdbLength;
  189.         else
  190.             cmdlen = sizeof (struct cdb_6);
  191.         cdb_ctrl = 0;
  192.         break;
  193.     
  194.         case OPGROUP_7:
  195.         if(scsiReq->cdbLength)
  196.              cmdlen = scsiReq->cdbLength;
  197.         else
  198.             cmdlen = sizeof (struct cdb_10);
  199.         cdb_ctrl = 0;
  200.         break;
  201.     
  202.         default:
  203.         scsiReq->driverStatus = SR_IOST_CMDREJ;
  204.         return 1;
  205.     }
  206.  
  207.     /*
  208.      * Make sure nothing unreasonable has been asked of us
  209.      */
  210.     if ((cdb_ctrl & CTRL_LINKFLAG) != CTRL_NOLINK) {
  211.         scsiReq->driverStatus = SR_IOST_CMDREJ;
  212.         return 1;
  213.     }
  214.  
  215.     addr = (vm_offset_t)cmdBuf->buffer;
  216.     len = scsiReq->maxTransfer;
  217.  
  218.     if (len > 0) 
  219.         pages = (round_page(addr+len) - trunc_page(addr)) / PAGE_SIZE;
  220.     else
  221.         pages = 0;
  222.         
  223.     ccb->cdb        = *cdbp;
  224.     ccb->cdb_len        = cmdlen;
  225.     
  226.     ccb->data_in        = scsiReq->read;
  227.     ccb->data_out        = !scsiReq->read;
  228.     ccb->target        = scsiReq->target;
  229.     ccb->lun        = scsiReq->lun;
  230.     #if    AUTO_SENSE_ENABLE
  231.     ccb->reqsense_len      = sizeof(esense_reply_t);
  232.     #else    AUTO_SENSE_ENABLE
  233.     ccb->reqsense_len    = 1;    /* no auto reqsense */
  234.     #endif    AUTO_SENSE_ENABLE
  235.  
  236.     /* 
  237.      * Note Adaptec does not support command queueing. Synchronous
  238.      * negotiation can only be disabled by jumper. Disconnects can
  239.      * not be disabled.
  240.      */
  241.  
  242.     ccb->cmdBuf = cmdBuf;
  243.     ccb->total_xfer_len = 0;
  244.     IOGetTimestamp(&ccb->startTime);
  245.  
  246.     /*
  247.      *  Set up the DMA address and length.  If we have more than one page,
  248.      *  then chances are that we'll have to use scatter/gather to collect
  249.      *  all the physical pages into a single transfer.
  250.      */
  251.     if (pages == 0) {
  252.         aha_put_24(0, ccb->data_addr);
  253.         aha_put_24(0, ccb->data_len);
  254.         ccb->oper = AHA_CCB_INITIATOR_RESID;
  255.     }
  256.     else if (pages == 1) {        
  257.     
  258.         if(IOPhysicalFromVirtual(cmdBuf->client, addr, &phys)) {
  259.             IOLog("%s: Can\'t get physical address\n",
  260.                 [self name]);
  261.             scsiReq->driverStatus = SR_IOST_INT;
  262.             return 1;
  263.         }
  264.  
  265.         ccb->dmaList[0] = [self createDMABufferFor:&phys
  266.                 length:len read:scsiReq->read
  267.                 needsLowMemory:YES limitSize:NO];
  268.  
  269.         if (ccb->dmaList[0] == NULL) {
  270.             [self abortDMA:ccb->dmaList length:len];
  271.             scsiReq->driverStatus = SR_IOST_INT;
  272.             return 1;
  273.         }
  274.  
  275.         aha_put_24(phys, ccb->data_addr);
  276.         aha_put_24(len, ccb->data_len);
  277.     
  278.         ccb->oper = AHA_CCB_INITIATOR_RESID;
  279.         ccb->total_xfer_len = len;
  280.     }
  281.     else {
  282.         vm_offset_t    lastPhys = 0;
  283.         unsigned int    sgEntry = 0;
  284.         unsigned int    maxEntries = MIN(pages, AHA_SG_COUNT);
  285.         IOEISADMABuffer    *dmaBuf = ccb->dmaList;
  286.  
  287.         for (sgEntry=0;  sgEntry < maxEntries;  sgEntry++) {
  288.             struct aha_sg    *sg = &ccb->sg_list[sgEntry];
  289.             unsigned int    thisLength;
  290.             
  291.             thisLength = MIN(len, round_page(addr+1) - addr);
  292.             
  293.                if(IOPhysicalFromVirtual(cmdBuf->client, 
  294.                     addr, &phys)) {
  295.                 IOLog("%s: Can\'t get physical address\n",
  296.                     [self name]);
  297.                 [self abortDMA:ccb->dmaList
  298.                     length:ccb->total_xfer_len];
  299.                 scsiReq->driverStatus = SR_IOST_INT;
  300.                 return 1;
  301.             }
  302.             *dmaBuf = [self createDMABufferFor:&phys
  303.                     length:thisLength
  304.                     read:scsiReq->read
  305.                     needsLowMemory:YES limitSize:NO];
  306.  
  307.             if (*dmaBuf == NULL) {
  308.                 [self abortDMA:ccb->dmaList
  309.                     length:ccb->total_xfer_len];
  310.                 scsiReq->driverStatus = SR_IOST_INT;
  311.                 return 1;
  312.             }
  313.  
  314.             aha_put_24(phys, sg->addr);
  315.             aha_put_24(thisLength, sg->len);
  316.     
  317.             ccb->total_xfer_len += thisLength;
  318.             
  319.             addr += thisLength;
  320.             len -= thisLength;
  321.             lastPhys = phys;
  322.             dmaBuf++;
  323.         }
  324.     
  325.         if(IOPhysicalFromVirtual(IOVmTaskSelf(), 
  326.                 (unsigned)ccb->sg_list, 
  327.                 &phys)) {
  328.             IOLog("%s: Can\'t get physical address of ccb\n", 
  329.                 [self name]);
  330.             IOPanic("AHAController");
  331.         }
  332.         aha_put_24(phys, ccb->data_addr);
  333.         aha_put_24(sgEntry * sizeof(struct aha_sg), ccb->data_len);
  334.  
  335.         ccb->oper = AHA_CCB_INITIATOR_RESID_SG;
  336.     }
  337.  
  338.     return 0;
  339. }
  340.  
  341. /*
  342.  * If any commands pending, and the controller's queue is not full,
  343.  * run the new commands. 
  344.  */
  345. - runPendingCommands
  346. {
  347.     unsigned int    cmdsToRun;
  348.     struct ccb    *ccb;
  349.  
  350.     cmdsToRun = AHA_QUEUE_SIZE - outstandingCount;
  351.  
  352.     while (cmdsToRun > 0 && !queue_empty(&pendingQ)) {
  353.  
  354.         /*
  355.          *  Dequeue pending command and add to the outstanding queue.
  356.          */
  357.         ccb = (struct ccb *) queue_first(&pendingQ);
  358.         queue_remove(&pendingQ, ccb, struct ccb *, ccbQ);
  359.         if (!ccb)
  360.             break;
  361.  
  362.         queue_enter(&outstandingQ, ccb, struct ccb *, ccbQ);
  363.         outstandingCount++;
  364.         
  365.         /*
  366.          *  Let 'er rip...
  367.          */
  368.         ccb->mb_out->mb_stat = AHA_MB_OUT_START;
  369.         aha_start_scsi(ioBase);
  370.     
  371.         /*
  372.          *  Accumulate some simple statistics: the max queue length
  373.          *  and enough info to compute a running average of the queue
  374.          *  length.
  375.          */
  376.         maxQueueLen = MAX(maxQueueLen, outstandingCount);
  377.         queueLenTotal += outstandingCount;
  378.         totalCommands++;
  379.  
  380.         cmdsToRun--;
  381.     }
  382.     return self;
  383. }
  384.  
  385. /*
  386.  * A command is done.  Figure out what happened, and notify the
  387.  * client appropriately. Called upon detection of I/O complete interrupt,
  388.  * timeout detection, or when we reset the bus and blow off pending 
  389.  * commands.
  390.  */
  391. - (void)commandCompleted : (struct ccb *) ccb 
  392.               reason : (completeStatus)reason
  393. {
  394.     ns_time_t        currentTime;
  395.     IOSCSIRequest        *scsiReq;
  396.     AHACommandBuf          *cmdBuf = ccb->cmdBuf;
  397.     
  398.     ASSERT(cmdBuf != NULL);
  399.     scsiReq = cmdBuf->scsiReq;
  400.     ASSERT(scsiReq != NULL);
  401.  
  402.     ddm_thr("commandCompleted: ccb 0x%x cmdBuf 0x%x reason %d\n", 
  403.         ccb, cmdBuf, reason, 4,5);
  404.         
  405.     scsiReq->scsiStatus = ccb->target_status;
  406.  
  407.     switch(reason) {
  408.         case CS_Timeout:
  409.            scsiReq->driverStatus = SR_IOST_IOTO;
  410.         break;
  411.         case CS_Reset:
  412.             scsiReq->driverStatus = SR_IOST_RESET;
  413.         break;
  414.         case CS_Complete:
  415.         switch (ccb->host_status) {
  416.     
  417.         /*
  418.          * Handle success and data overrun/underrun.  We can handle
  419.          * overrun/underrun as a normal case because the controller 
  420.          * sets the data_len field to be the actual number of bytes 
  421.          * transferred regardless of overrun.
  422.              */
  423.         case AHA_HOST_SUCCESS:
  424.         case AHA_HOST_DATA_OVRUN:
  425.             [self completeDMA:ccb->dmaList 
  426.                 length:scsiReq->maxTransfer];
  427.             scsiReq->bytesTransferred = ccb->total_xfer_len -  
  428.                     aha_get_24(ccb->data_len);
  429.     
  430.             /*
  431.              *  Everything looks good.  Make sure the SCSI status byte
  432.              *  is cool before we really say everything is hunky-dory.
  433.              */
  434.             if (scsiReq->scsiStatus == STAT_GOOD)
  435.                 scsiReq->driverStatus = SR_IOST_GOOD;
  436.             else if (scsiReq->scsiStatus == STAT_CHECK) {
  437.                 if(AUTO_SENSE_ENABLE) {
  438.             
  439.                 esense_reply_t *sensePtr;
  440.                 
  441.                 scsiReq->driverStatus = SR_IOST_CHKSV;
  442.                 
  443.                 /*
  444.                  * Sense data starts immediately after the actual
  445.                  * cdb area we use, not an entire union cdb.
  446.                  */
  447.                 sensePtr = (esense_reply_t *)
  448.                     (((char *)&ccb->cdb) + ccb->cdb_len);
  449.                 scsiReq->senseData = *sensePtr;
  450.             }
  451.             else {
  452.                 scsiReq->driverStatus = SR_IOST_CHKSNV;
  453.             }
  454.             }
  455.             else
  456.                 scsiReq->driverStatus = ST_IOST_BADST;
  457.             break;
  458.     
  459.         case AHA_HOST_SEL_TIMEOUT:
  460.             [self abortDMA:ccb->dmaList length:scsiReq->maxTransfer];
  461.             scsiReq->driverStatus = SR_IOST_SELTO;
  462.             break;
  463.         
  464.         default:
  465.             IOLog("AHA interrupt: bad status %x\n", ccb->host_status);
  466.             [self abortDMA:ccb->dmaList length:scsiReq->maxTransfer];
  467.             scsiReq->driverStatus = SR_IOST_INVALID;
  468.             break;
  469.         }   /*  switch host_status */
  470.     }       /*  switch status */
  471.  
  472.     IOGetTimestamp(¤tTime);
  473.     scsiReq->totalTime = currentTime - ccb->startTime;
  474.     cmdBuf->result = scsiReq->driverStatus;
  475.     
  476.     /*
  477.      * Wake up client.
  478.      */
  479.     ddm_thr("commandCompleted: I/O complete on cmdBuf 0x%x\n",
  480.             cmdBuf, 2,3,4,5);
  481.     [cmdBuf->cmdLock lock];
  482.     [cmdBuf->cmdLock unlockWith:CMD_COMPLETE];
  483.  
  484.     /*
  485.      * Free the CCB and clean up possible pending timeout.
  486.      */    
  487.     (void) IOUnscheduleFunc(ahaTimeout, ccb);
  488.     [self freeCcb:ccb];
  489. }
  490.  
  491. /*
  492.  * Alloc/free ccb's. These only come from the array ahaCcb[]. 
  493.  * If we can't find one, return NULL - caller will have to try 
  494.  * again later.
  495.  */
  496. - (struct ccb *)allocCcb : (BOOL)doDMA
  497. {
  498.     struct ccb *ccb;
  499.     int i;
  500.     
  501.     if(numFreeCcbs == 0) {
  502.         ddm_thr("allocCcb: numFreeCcbs = 0\n", 1,2,3,4,5);
  503.         return NULL;
  504.     }
  505.  
  506.     /*
  507.      * Since numFreeCcbs is non-zero, there has to be one available 
  508.      * in ahaCcb[].
  509.      */
  510.     ccb = ahaCcb;
  511.     while (ccb <= &ahaCcb[AHA_QUEUE_SIZE - 1] && ccb->in_use) {
  512.         ccb++;
  513.     }
  514.     if (ccb > &ahaCcb[AHA_QUEUE_SIZE - 1]) {
  515.         IOPanic("AHAController: out of ccbs");
  516.     }
  517.     numFreeCcbs--;
  518.     ccb->in_use = TRUE;
  519.     
  520.     /*
  521.      * Null out dmaList.
  522.      */
  523.     for(i=0; i<AHA_SG_COUNT; i++) {
  524.         ccb->dmaList[i] = NULL;
  525.     }
  526.     
  527.     /*
  528.      * Acquire the reentrant DMA lock. This is a nop on EISA machines.
  529.      * 
  530.      * Although -reserveDMALock is reentrant for multiple threads on
  531.      * one device, it is *not* reentrant for one thread. Thus we should 
  532.      * only call it if we don't already hold the lock.
  533.      * Also, avoid this if we're not going to do any DMA. 
  534.      */
  535.     if(doDMA && (++dmaLockCount == 1)) {
  536.         ddm_thr("allocCcb: calling reserveDMALock\n", 1,2,3,4,5);
  537.         [super reserveDMALock];
  538.     }
  539.     ddm_thr("allocCcb: returning 0x%x\n", ccb, 2,3,4,5);
  540.     
  541.     return ccb;    
  542. }
  543.  
  544. - (void)freeCcb : (struct ccb *)ccb
  545. {
  546.     BOOL    didDMA = (ccb->total_xfer_len ? YES : NO);
  547.  
  548.     ddm_thr("freeCcb: ccb 0x%x\n", ccb, 2,3,4,5);
  549.     ccb->in_use = FALSE;
  550.     numFreeCcbs++;
  551.     if(didDMA && (--dmaLockCount == 0)) {
  552.         ddm_thr("freeCcb: calling releaseDMALock\n", 
  553.             1,2,3,4,5);
  554.         [super releaseDMALock];
  555.     }
  556. }
  557.  
  558. - (void) completeDMA:(IOEISADMABuffer *) dmaList length:(unsigned int) xferLen
  559. {
  560.     IOEISADMABuffer    *buf = &dmaList[0];
  561.     int        i;
  562.     
  563.     for (i = 0; i < AHA_SG_COUNT; i++, buf++) {
  564.         if(*buf) {
  565.             [self freeDMABuffer:*buf];
  566.         }
  567.         else {
  568.             return;
  569.         }
  570.     }
  571. }
  572.  
  573.  
  574. - (void) abortDMA:(IOEISADMABuffer *) dmaList length:(unsigned int) xferLen
  575. {
  576.     IOEISADMABuffer    *buf = &dmaList[0];
  577.     int        i;
  578.     
  579.     for (i = 0; i < AHA_SG_COUNT; i++, buf++) {
  580.         if(*buf) {
  581.             [self abortDMABuffer:*buf];
  582.         }
  583.         else {
  584.             return;
  585.         }
  586.     }
  587. }
  588.  
  589. @end
  590.  
  591.  
  592. /*
  593.  *  Handle timeouts.  We just send a timeout message to the I/O thread
  594.  *  so it wakes up.
  595.  */
  596. static void
  597. ahaTimeout(void *arg)
  598. {
  599.     
  600.     struct ccb    *ccb = arg;
  601.     msg_header_t    msg = timeoutMsgTemplate;
  602.     msg_return_t    mrtn;
  603.     
  604.     if(!ccb->in_use) {
  605.         /* 
  606.          * Race condition - this CCB got completed another way. 
  607.          * No problem.
  608.          */
  609.         return;
  610.     }    
  611.     msg.msg_remote_port = ccb->timeoutPort;
  612.     IOLog("AHA timeout\n");
  613.     if(mrtn = msg_send_from_kernel(&msg, MSG_OPTION_NONE, 0)) {
  614.         IOLog("ahaTimeout: msg_send_from_kernel() returned %d\n",
  615.             mrtn);
  616.     }
  617. }
  618.  
  619.