home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection 1998 Fall: Game Toolkit / Disc.iso / SDKs / PCI Driver Development Kit / • Samples / Driver Samples / SCSI samples / SCSI 950629 / NCR_DriverProject / Src / NCRRunScript.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-08-20  |  38.1 KB  |  1,090 lines  |  [TEXT/MPCC]

  1. /*                                        NCRRunScript.c                                */
  2. /*
  3.  * NCRRunScript.c
  4.  * Copyright © 1994 Apple Computer Inc. All rights reserved.
  5.  */
  6. /*    .___________________________________________________________________________________.
  7.       | These low-level routines access the NCR 53C825 chip registers. They start an        |
  8.       | asynchrous operation (either a general SCSI I/O request or a Bus Reset operation)    |
  9.       | and follow its operation to the bitter end. While the logic is extremely specific    |
  10.       | to the NCR chip, you should at least read through this file to understand the        |
  11.       | relation between the hardware-specific code and the Driver Services code.            |
  12.     |                                                                                    |
  13.     | Almost everything here is specific to the NCR chip. The watchdog timeout will        |
  14.     | be generally useful. However, it has not been tested yet.                            |
  15.     .___________________________________________________________________________________.
  16. */
  17.  
  18. #include "NCRDriverPrivate.h"
  19. /*
  20.  * stddef defines offsetof()
  21.  */
  22. #include <stddef.h>
  23. /*
  24.  * WARNING: the bus reset script hangs if you turn on single-stepping. I suspect that
  25.  * I'm not handling multiple interrupt conditions correctly. I "fixed" this by using
  26.  * the DriverServices DelayForHardware routine to do bus reset delays. However, in
  27.  * general, you should not expect single-step to work correctly.
  28.  */
  29. #define SINGLE_STEP                0    /* Used for debugging the script */
  30. #ifndef SINGLE_STEP_DEFAULT
  31. #define SINGLE_STEP_DEFAULT        0
  32. #endif
  33.  
  34. void                        StartWatchdogTimeout(
  35.         register PerRequestDataPtr perRequestDataPtr
  36.     );
  37. OSStatus                    WatchdogTimerCompletion(
  38.         void                    *p1,
  39.         void                    *p2
  40.     );
  41. void                        StartScript(
  42.         register PerRequestDataPtr perRequestDataPtr,
  43.         UInt32                    scriptPtr
  44.     );
  45.  
  46. #define REQUEST        (*perRequestDataPtr)
  47. #define SCRIPT        (REQUEST.scriptData)
  48. #define SHADOW        (REQUEST.shadow)
  49.  
  50. OSErr                        StoreDMAParameters(
  51.         PerRequestDataPtr        perRequestDataPtr
  52.     );
  53.  
  54. #if 0 && USE_LOG_LIBRARY
  55. void                        LogScriptInterrupt(
  56.         register PerRequestDataPtr perRequestDataPtr
  57.     );
  58. #else
  59. #define LogScriptInterrupt(perRequestDataPtr) /* Nothing */
  60. #endif
  61.  
  62. #if USE_LOG_LIBRARY
  63. void                        DumpRegisters(
  64.         register PerRequestDataPtr perRequestDataPtr
  65.     );
  66. #else
  67. #define DumpRegisters(perRequestDataPtr)    /* Nothing */
  68. #endif
  69.  
  70.  
  71. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  72.  * This is the primary interrupt service routine that is called by the system. Input
  73.  * parameters are ignored.
  74.  *
  75.  * This subroutine handles the interrupt from the NCR chip. The logic is from the
  76.  * NCR Family Programmers Guide, page 8-30 ff.
  77.  *
  78.  * Note that we must handle phase-mismatch errors here. In particular, we must handle
  79.  * residual byte count problems (Request Sense with a larger buffer than the
  80.  * device plans to deliver) as they are not fatal errors.
  81.  */
  82. InterruptMemberNumber
  83. PCIInterruptServiceRoutine(
  84.         InterruptSetMember        member,
  85.         void                    *refCon,
  86.         UInt32                    theIntCount
  87.     )
  88. {
  89.         OSErr                    status;
  90.         register PerRequestDataPtr perRequestDataPtr;
  91.         unsigned short            oldPhase;
  92.         InterruptMemberNumber    result;
  93.         IOParam                    *pb;
  94.         NCRSCSIParamPtr            scsiParamPtr;
  95.         short                    stallLoop;
  96.         static const Nanoseconds gTenMsec = { 0, 10000000 };
  97. #define PB        (*pb)
  98. #define SCSI    (*scsiParamPtr)
  99.  
  100.         Timestamp('DMA-');
  101.         Trace(PCIInterruptServiceRoutine);
  102.         UNUSED(member);
  103.         UNUSED(refCon);
  104.         UNUSED(theIntCount);
  105.         /*
  106.          * We should try to establish which "per-request" interrupted the processor.
  107.          * Currently, we support only a single transfer, but this routine will have to
  108.          * get more complex (by querying the NCR chip) when we support suspend-resume
  109.          * or tagged commands. For example, we could use the data storage address.
  110.          * The actual interrupt is processed by a primary interrupt routine. When the
  111.          * I/O operation is completed, it queues a secondary interrupt routine that
  112.          * frees resources and calls IOCommandIsComplete. 
  113.          */
  114.         perRequestDataPtr = GLOBAL.perRequestDataPtr;
  115.         /*
  116.          * These are only needed for re-preparation.
  117.          */
  118.         pb = (IOParam *) REQUEST.pb;                    /* pb can be NULL            */
  119.         scsiParamPtr = (pb == NULL) ? NULL : (NCRSCSIParamPtr) PB.ioMisc;
  120.         status = kIOBusyStatus;                        /* Changed by completed I/O        */
  121.         SHADOW.istat = ReadByte(ISTAT);                /* 0x14 Interrupt Status        */
  122.         /*
  123.          * We must check that at least one interrupt bit is set -- if none are set,
  124.          * this is a spurious interrupt -- we then return kIsrIsNotComplete
  125.          */
  126.         result = ((SHADOW.istat & (bit2 | bit1 | bit0)) != 0)
  127.                     ? kIsrIsComplete : kIsrIsNotComplete;
  128.         if ((SHADOW.istat & bit2) != 0) {            /* INTF: Interrupt on the fly?    */
  129.             /*
  130.              * This bit must be written to 1 to clear it. This interrupt must be
  131.              * handled before handling other interrupt conditions. We don't
  132.              * currently use the INTF interrupt.
  133.              */
  134.             WriteByte(ISTAT, SHADOW.istat | bit2);
  135.             SynchronizeIO();                            /* eieio                    */
  136.             SHADOW.istat = ReadByte(ISTAT);                /* 0x14 Int. Status            */
  137.         }
  138.         if ((SHADOW.istat & (bit1 | bit0)) != 0) {
  139.             /*
  140.              * If the SIP and DIP interrupts are clear, this is a "true" interrupt on
  141.              * the fly. Exit the interrupt service routine to let the script continue
  142.              * in peace -- we're not allowed to touch other NCR chip registers.
  143.              * If either bit is set, this is a normal interrupt. Save the interesting
  144.              * registers in the per-request block: this is for debugging convenience.
  145.              *
  146.              * Note: we might need to fix this algorithm. See the discussion in the
  147.              * NCR 53C825 manual for the reason behind clearing ISTAT.
  148.              */
  149. #if 0
  150.             if ((SHADOW.istat & (bit1 | bit0)) == bit0)
  151.                 WriteByte(ISTAT, 0);
  152. #endif
  153.             SHADOW.dbc = ReadLong(DBC);                    /* 0x24 byte count            */
  154.             SHADOW.dsp = ReadLong(DSP);                    /* 0x2C Script pointer        */
  155.             SHADOW.scriptPCOffset = SHADOW.dsp - REQUEST.scriptBaseAddress - 8;
  156. #if 0 && USE_LOG_LIBRARY
  157.             WriteLogEntry(GLOBAL.logRecordPtr, 'PISR',
  158.                 LogFormat4(kLogFormatAddress, kLogFormatAddress, kLogFormatSigned, kLogFormatString),
  159.                 (UInt32) SHADOW.dsp,
  160.                 (UInt32) REQUEST.scriptBaseAddress,
  161.                 (SInt32) SHADOW.scriptPCOffset,
  162.                 "\pScript @ isr"
  163.             );
  164. #endif
  165.             SHADOW.dsps = ReadLong(DSPS);                /* 0x30 Script 2nd long    */
  166.             /*
  167.              * Read sist0 and sist1 before reading dstat. The calls to
  168.              * DelayForHardware enforce a 12 clock tick delay between reads.
  169.              */
  170.             SHADOW.sist0 = ReadByte(SIST0);                /* 0x42 SCSI Int. Sts 0        */
  171.             DelayForHardware(GLOBAL.clockTick12);
  172.             SHADOW.sist1 = ReadByte(SIST1);                /* 0x43 SCSI Int. Sts 1        */
  173.             DelayForHardware(GLOBAL.clockTick12);
  174.             SHADOW.dstat = ReadByte(DSTAT);                /* 0x0C DMA status            */
  175.             SHADOW.scntl1 = ReadByte(SCNTL1);            /* 0x01 SCSI control 1        */
  176.             SHADOW.socl = ReadByte(SOCL);                /* 0x09 SCSI output ctl        */
  177.             SHADOW.sbcl = ReadByte(SBCL);                /* 0x0B Bus ctl signals        */
  178.             SHADOW.sstat0 = ReadByte(SSTAT0);            /* 0x0D core status            */
  179.             SHADOW.sstat1 = ReadByte(SSTAT1);            /* 0x0E core status            */
  180.             SHADOW.sstat2 = ReadByte(SSTAT2);            /* 0x0F core status            */
  181.             SHADOW.dfifo = ReadByte(DFIFO);                /* 0x20    fifo count            */
  182.             SHADOW.dbc &= 0x00FFFFFF;                    /* Only 24 bits                */
  183.             LogScriptInterrupt(perRequestDataPtr);        /* Debug: log registers        */
  184.             if ((SHADOW.istat & bit0) != 0) {            /* DMA interrupt pending?    */
  185.                 if ((SHADOW.dstat & bit7) == 0) {        /* DMA fifo not empty?        */
  186.                     LogHex(SHADOW.dstat, "\pDMA fifo not empty");
  187.                     /*
  188.                      * Clear the dma fifo non-empty condition by resetting the dma
  189.                      * and scsi fifos. Hmm, how does this deal with partial (short)
  190.                      * preparation?
  191.                      */
  192.                     SHADOW.stest3 = ReadByte(STEST3);
  193.                     WriteByte(STEST3, SHADOW.stest3 | bit1);
  194.                     SynchronizeIO();                    /* eieio                    */
  195.                     SHADOW.ctest3 = ReadByte(CTEST3);
  196.                     WriteByte(CTEST3, SHADOW.ctest3 | bit2);
  197.                 }
  198.                 if ((SHADOW.dstat & (bit5 | bit6)) != 0) {    /* PCI bus fault?        */
  199.                     LogHex(SHADOW.dstat, "\pPCIBusFault");
  200.                     LogHex(ReadLong(DBC), "\pDBC 0x24-26, DNAD 0x27 - count, cmd");
  201.                     LogHex(ReadLong(DNAD), "\pDNAD 0x28 - DMA next address");
  202.                     LogHex(ReadLong(DSPS), "\pDSPS 0x30 - Scripts pointer save");
  203.                     status = scsiTerminated;
  204.                 }
  205.                 else if ((SHADOW.dstat & bit0) != 0) {    /* Illegal Script opcode?    */
  206.                     LogHex(SHADOW.scriptPCOffset, "\pIllegal Script @ pc offset");
  207.                     DumpRegisters(perRequestDataPtr);
  208.                     status = ioErr;
  209.                 }
  210.                 else if ((SHADOW.dstat & bit4) != 0) {    /* Abort error (IOKill)        */
  211.                     /*
  212.                      * Note: we'll get one of these if the non-interrupt variant
  213.                      * times out. A better error message mechanism is probably needed.
  214.                      * A KillIO request also causes one of these.
  215.                      */
  216.                     LogString("\pChip-detected abort");
  217.                     WriteByte(ISTAT, 0);                /* Clear ISTAT again        */
  218.                     /*
  219.                      * To do: look at the current bus status: if we are connected
  220.                      * and REQ is set, the target is trying to do something and we
  221.                      * aren't responding. We should restart the script at the
  222.                      * rundown address. If we are connected and the target is
  223.                      * not in REQ, the device is, how should we put it, dead. About
  224.                      * all we can do is exit the script and hope that the caller
  225.                      * tosses a Bus Reset or I/O Rundown Control call at us.
  226.                      */
  227.                     status = scsiCommandTimeout;
  228.                 }
  229.                 else if ((SHADOW.dstat & bit2) != 0) {    /* Script INT instruction    */
  230.                     //** LogDecimal(SHADOW.dsps, "\pScript Interrupt");
  231.                     WriteByte(STIME0, 0);                /* Cancel timers            */
  232.                     WriteByte(STIME1, 0);                /* Cancel both of them        */
  233.                     status = SHADOW.dsps;
  234.                     if (status == kIntNeedAnotherPreparation) {    /* Normal DMA?        */
  235.                         if (scsiParamPtr != NULL
  236.                           && SCSI.driverAction != kNCRDriverNoDataPhase) {
  237.                             /*
  238.                              * We are in a data phase, but don't have any prepared I/O.
  239.                              * Prepare the next chunk of DMA and restart the script.
  240.                              * If PrepareNextDMA returns scsiDataRunError, queue the
  241.                              * Secondary Interrupt Handler to schedule our Software
  242.                              * Task that will call PrepareMemoryForIO, setup the next
  243.                              * chunk of the transfer, and then queue the Secondary
  244.                              * Interrupt routine again to restart the device.
  245.                              */
  246. #if 0
  247.                             LogDecimal(
  248.                                 (SInt32) ReadByte(CTEST0),
  249.                                 "\pCalling Prepare[Next Area]"
  250.                             );
  251. #endif
  252.                             status = StoreDMAParameters(perRequestDataPtr);
  253.                             if (status != noErr)
  254.                                 status = kPrepareMemoryStartTask;
  255.                             else {
  256.                                 WriteByte(CTEST0, SCSI.driverAction);
  257.                                 status = kIOBusyStatus;
  258.                             }
  259.                         }
  260.                         else {
  261.                             status = scsiTransferTypeInvalid;    /* Oops            */
  262.                         }
  263.                     }
  264.                     else if (status == kIntMemTestNeedsAnotherPreparation) {
  265.                         status = StoreDMAParameters(perRequestDataPtr);
  266.                         if (status != noErr)
  267.                             status = kPrepareMemoryStartTask;
  268.                         else {
  269.                             status = kIOBusyStatus;
  270.                             StartScript(perRequestDataPtr, REQUEST.scriptPtr);
  271.                             goto exit;
  272.                         }
  273.                     }
  274.                     else {
  275.                         /*
  276.                          * This is some other "final" SCSI interrupt status.
  277.                          */
  278.                     }
  279.                 }
  280.                 if ((SHADOW.dstat & bit3) != 0) {
  281.                     /*
  282.                      * Single-step interrupt. We have presumably logged the interrupt
  283.                      * so we need only restart the process. This may be insufficient:
  284.                      * we might need to re-read the registers to see if other interrupt
  285.                      * conditions need to be cleared.
  286.                      *
  287.                      * Warning: the single-step stuff doesn't work correctly as the
  288.                      * chip apparently gives us multiple interrupt conditions which
  289.                      * the interrupt service routine doesn't handle properly.
  290.                      */
  291.                 }
  292.             } /* If DMA interrupt or script complete */
  293.             if ((SHADOW.istat & bit1) != 0) {        /* SIP: SCSI interrupt pending?    */
  294.                 /*
  295.                  * Process a SCSI interrupt.
  296.                  */
  297.                 if ((SHADOW.sist1 & bit2) != 0) {        /* Selection timeout?        */
  298.                     WriteByte(STIME0, 0);                /* Clear the timer            */
  299.                     status = scsiSelectTimeout;
  300.                 }
  301.                 else if ((SHADOW.sist1 & bit1) != 0) {    /* General timeout?            */
  302.                     WriteByte(STIME1, 0);                /* Clear the timer            */
  303.                 }
  304.                 else if ((SHADOW.sist0 & (bit3 | bit2 | bit1 | bit0)) == bit1) {
  305.                     /*
  306.                      * Bus Reset. If SCSI Control 1 has RST (bit 3) set, indicating
  307.                      * that we asserted bus Reset, generate a private status that
  308.                      * the secondary interrupt routine will use to stall the script
  309.                      * for 250 msec. Then, it restarts the script to clear the
  310.                      * bus reset condition.
  311.                      */
  312.                     if ((SHADOW.scntl1 & bit3) != 0) {
  313.                         status = kBusResetRestart;        /* Private status            */
  314.                     }
  315.                     else {
  316.                         status = scsiSCSIBusReset;
  317.                         LogString("\pUnexpected bus reset");
  318.                     }
  319.                 }
  320.                 else if ((SHADOW.sist0 & (bit3 | bit2 | bit1 | bit0)) != 0) {
  321.                     /*
  322.                      * bit3: SCSI gross error (data under/overflow) 
  323.                      * bit2: Unexpected disconnect
  324.                      * bit1: Bus Reset (and some other SCSI interrupt)
  325.                      * bit0: Bus parity error.
  326.                      */
  327.                     if (REQUEST.scriptSelector == kSCSICommandScript) {
  328.                         switch (SHADOW.sist0 & (bit3 | bit2 | bit1 | bit0)) {
  329.                         case bit0:    status = scsiParityError;        break;
  330.                         case bit1:    status = scsiSCSIBusReset;        break;
  331.                         case bit2:    status = scsiUnexpectedBusFree;    break;
  332.                         case bit3:    status = scsiDataRunError;        break;
  333.                         default:    status = scsiTerminated;        break;
  334.                         }
  335.                     }
  336.                     if (status == scsiSCSIBusReset                    /* Bus reset?    */
  337.                      && REQUEST.scriptSelector == kBusResetScript)    /* Ignore ours    */
  338.                         status = noErr;
  339.                     else {
  340.                         LogHex(SHADOW.sist0, "\pStrange SCSI error");
  341.                     }
  342.                 }
  343.                 else if ((SHADOW.sist0 & bit7) != 0) {        /* Phase mismatch?        */
  344.                     /*
  345.                      * Phase mismatch - this is often an error, but we do allow
  346.                      * short transfers, where the user requested a longer buffer
  347.                      * than the target intends to provide. We check this by
  348.                      *    (1) checking for "old" phase in SHADOW.socl. Look for DATI.
  349.                      *    (2) checking for "new" phase -- it should be STATUS, but this
  350.                      *        is only for debugging: the script will eventually fail if
  351.                      *        this is not the case.
  352.                      *    (3) checking the DMA fifo -- it should be empty
  353.                      *    (4) checking the DBC transfer count -- it should be nonzero.
  354.                      * Other cases are errors. The script continues after phase-
  355.                      * mismatch errors: it will eventually finish with a bus-free
  356.                      * status and an Int instruction.
  357.                      *
  358.                      * To handle partial preparationn, scatter-gather, or other
  359.                      * non-contiguous physical memory situations, we queue a secondary
  360.                      * interrupt handler that recovers the residual dma count (the
  361.                      * following code will be copied there), it then calls
  362.                      * PrepareMemoryForIO to update the physical mapping pointers,
  363.                      * updates the data table, and restarts the operation. Too much
  364.                      * work for a sample.
  365.                      */
  366.                     oldPhase = SHADOW.socl & 0x07;
  367.                     if (oldPhase == DATO || oldPhase == DATI) {
  368.                         /*
  369.                          * Try to recover the residual dma count. The algorithm is
  370.                          * from page 5-26 of the  NCR data manual:
  371.                          *    1. Subtract the seven least significant bits of the dbc
  372.                          *        register from the 7-bit value of the dfifo register.
  373.                          *    2. And the result with 0x7F for a byte count between
  374.                          *        zero and 64.
  375.                          */
  376.                         SHADOW.residualTransferCount = SHADOW.dbc
  377.                             + (((SHADOW.dfifo & 0x7F) - (SHADOW.dbc & 0x7F)) & 0x7F);
  378.                         /*
  379.                          * Byte count will be a value between zero and 64.
  380.                          * If this was a send (DATO, MSGO, CMD) operation, look
  381.                          * at the SSTAT0 and SSTAT2 registers to see if bytes remain
  382.                          * in the SODL register. This probably needs work.
  383.                          */
  384.                         if (oldPhase == DATO) {
  385.                             if ((SHADOW.sstat0 & bit5) != 0)
  386.                                 ++SHADOW.residualTransferCount;
  387.                             if ((SHADOW.sstat2 & bit5) != 0)
  388.                                 ++SHADOW.residualTransferCount;
  389.                         }
  390.                         else {
  391.                             if ((SHADOW.sstat0 & bit7) != 0)
  392.                                 ++SHADOW.residualTransferCount;
  393.                             if ((SHADOW.sstat2 & bit7) != 0)
  394.                                 ++SHADOW.residualTransferCount;
  395.                         }
  396.                         if (pb != NULL)
  397.                             PB.ioActCount -= SHADOW.residualTransferCount;
  398.                     } /* If DATI or DATO phase */
  399.                 } /* If this is a phase mismatch error */
  400.             }
  401.         }
  402.         if (status != kIOBusyStatus) {
  403.             /*
  404.              * Turn off the interrupts and chip timers to prevent an unexpected
  405.              * interrupt: our global per-request record risks reentrancy bugs.
  406.              * If we restart I/O we will re-enable timers and interrupts.
  407.              */
  408.             WriteByte(DIEN, 0);
  409.             WriteByte(SIEN0, 0);
  410.             WriteByte(SIEN1, 0);
  411.             WriteByte(STIME0, 0);
  412.             WriteByte(STIME1, 0);
  413.             /*
  414.              * Note that actual completion is done from a secondary interrupt
  415.              * handler as we must Checkpoint the I/O buffers.
  416.              */
  417.             for (stallLoop = 0; stallLoop < 100; stallLoop++) {
  418.                 status = NCRQueueSecondaryInterrupt(perRequestDataPtr, status);
  419.                 if (status == noErr)
  420.                     break;
  421.                 /*
  422.                  * For some reason, we couldn't queue a secondary interrupt handler
  423.                  * Try after a brief stall. This probably won't work and certainly
  424.                  * won't be tested adaquately.
  425.                  */
  426.                 LogStatusString(status, "\pQueueSecondaryInterruptHandler fail");
  427.                 DelayForHardware(gTenMsec);
  428.             }
  429.         }
  430.         else {
  431.             StartScript(perRequestDataPtr, SHADOW.dsp);
  432.         }
  433.         /*
  434.          * Jump directly to exit to leave the interrupt service routine without
  435.          * restarting the script or scheduling a secondary interrupt routine.
  436.          */
  437. exit:    return (result);
  438. #undef PB
  439. #undef SCSI
  440. }
  441.  
  442. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  443.  * NCRQueueSecondaryInterrupt
  444.  *
  445.  * This routine queues the secondary interrupt handler. It was centralized to help
  446.  * debug the system ROMs.
  447.  */
  448. OSErr
  449. NCRQueueSecondaryInterrupt(
  450.         PerRequestDataPtr        perRequestDataPtr,
  451.         OSErr                    statusOrSignal
  452.     )
  453. {
  454.         OSStatus                osStatus;
  455.         
  456.         Trace(NCRQueueSecondaryInterrupt);
  457.         Timestamp('Q2I+');
  458.         osStatus = QueueSecondaryInterruptHandler(
  459.                     NCRSecondaryInterruptHandler,
  460.                     NULL,
  461.                     (void *) perRequestDataPtr,
  462.                     (void *) statusOrSignal
  463.                 );
  464.         CheckStatus(osStatus, "\pQueueSecondaryInterruptHandler2");
  465.         return (osStatus);
  466. }
  467.  
  468. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  469.  * NCRSecondaryInterruptHandler
  470.  *
  471.  * This is a secondary interrupt handler that is called by the primary interrupt
  472.  * service routine to complete an asynchronous I/O request. It is also called
  473.  * directly by the KillIO handler. It uses the atomic test-and-set operation to
  474.  * ensure that each request is completed exactly once.
  475.  *
  476.  * This is also called to restart I/O from the Software Interrupt Task
  477.  */
  478. OSStatus
  479. NCRSecondaryInterruptHandler(
  480.         void                    *p1,
  481.         void                    *p2
  482.     )
  483. {
  484.         register PerRequestDataPtr    perRequestDataPtr;
  485.         OSErr                    ioResult;
  486.         /*
  487.          * Note that status is used here only for adminstrative status from the
  488.          * Driver Services Library calls -- it is never passed back to the caller.
  489.          */
  490.         OSErr                    status;
  491.         OSStatus                osStatus;
  492.         ParmBlkPtr                pb;
  493.         NCRSCSIParamPtr            scsiParamPtr;
  494.         UInt32                    scriptPtr;
  495. #define IOPB    (pb->ioParam)
  496. #define SCSI    (*scsiParamPtr)
  497.  
  498.         Trace(NCRSecondaryInterruptHandler);
  499.         Timestamp('Q2I-');
  500.         perRequestDataPtr = (PerRequestDataPtr) p1;
  501.         pb = REQUEST.pb;
  502.         ioResult = (OSErr) p2;
  503.         /*
  504.          * There are a couple of private ioResult values that are used for
  505.          * intermediate script operations.
  506.          */
  507.         switch (ioResult) {
  508.         case kIORequestStart:
  509.             SHADOW.residualTransferCount = 0;
  510.             switch (REQUEST.scriptSelector) {
  511.             case kSCSICommandScript:
  512.                 if (SCSI.driverAction != 0
  513.                  && StoreDMAParameters(perRequestDataPtr) != noErr) {
  514.                     ioResult = scsiDataRunError;
  515.                     goto allDone;
  516.                 }
  517.                 WriteByte(SCID, GLOBAL.initiatorID);
  518.                 /*
  519.                  * Timeout if selection fails after 409.6 msec. This is the smallest
  520.                  * value larger than the SCSI Standard 250 msec. timeout value.
  521.                  */
  522.                 WriteByte(STIME0, 13);                            /* Selection timer    */
  523.                 WriteLong(DSA, REQUEST.scriptDataPtr);
  524.                 SynchronizeIO();                                /* eieio            */
  525.                 break;
  526.             case kBusResetScript:
  527.                 /*
  528.                  * No initiator ID, selection timeout or DSA.
  529.                  */
  530.                 WriteByte(SCID, GLOBAL.initiatorID);
  531.                 WriteByte(STIME0, 0);
  532.                 WriteLong(DSA, kInvalidPageAddress);
  533.                 break;
  534.             case kSCSITestISRScript:
  535.             case kSCSITestMemoryScript:
  536.                 if (StoreDMAParameters(perRequestDataPtr) != noErr) {
  537.                     ioResult = scsiDataRunError;
  538.                     goto allDone;
  539.                 }
  540.                 WriteLong(DSA, kInvalidPageAddress);
  541.                 break;
  542.             }
  543.             StartWatchdogTimeout(perRequestDataPtr);
  544.             StartScript(perRequestDataPtr, REQUEST.scriptPtr);
  545.             break;
  546.         case kPrepareMemoryStartTask:    /* Primary Interrupt needs PrepareMemory    */
  547.             /*
  548.              * We've run off the end of the preparation. Queue a Software Interrupt
  549.              * that will call PrepareMemoryForIO to handle the partial preparation.
  550.              * If this fails, restart I/O to rundown the SCSI device. Because this
  551.              * will run "task level" and other I/O may intervene, we may want to
  552.              * cancel the timer (for now) and restart it when the driver resumes
  553.              * operation. This requires thought, but I don't know the right answer;
  554.              * but I'll try it for now.
  555.              *
  556.              * Hmm, to do this right, CancelWatchdogTimer ought to return "time
  557.              * remaining" which will be used to restart the timer, rather than
  558.              * always starting at the original timeout.
  559.              */
  560. needAnotherPrepareMemory:
  561.             Timestamp('SSI+');
  562.             CancelWatchdogTimer(perRequestDataPtr);
  563.             CheckpointIOTable(&REQUEST.scsiIOTable);
  564.             osStatus = SendSoftwareInterrupt(REQUEST.nextDMAInterruptID, 0);
  565.             CheckStatus(osStatus, "\pSendSoftwareInterrupt");
  566.             if (osStatus != noErr) {
  567.                 /*
  568.                  * We couldn't queue a software interrupt. Force the
  569.                  * device into its failure script. The device will, eventually,
  570.                  * exit and return an error ioResult to the caller.
  571.                  */
  572.                 WriteByte(CTEST0, 0);    /* This will cause rundown    */
  573.                 scriptPtr = ((UInt32) GLOBAL.scriptIOTable.physicalMapping[0])
  574.                             + kSCSIRundownScript;
  575.                 StartScript(perRequestDataPtr, scriptPtr);
  576.             }
  577.             break;        
  578.         case kPrepareMemoryRestart:        /* PrepareMemoryForIO called                */
  579.             if (StoreDMAParameters(perRequestDataPtr) != noErr) {
  580.                 if (REQUEST.scriptSelector == kSCSITestMemoryScript) {
  581.                     ioResult = scsiDataRunError;
  582.                     goto allDone;
  583.                 }
  584.                 else {
  585.                     WriteByte(CTEST0, 0);    /* This will cause rundown    */
  586.                     scriptPtr = ((UInt32) GLOBAL.scriptIOTable.physicalMapping[0])
  587.                             + kSCSIRundownScript;
  588.                     StartScript(perRequestDataPtr, scriptPtr);
  589.                 }
  590.             }
  591.             else {
  592.                 StartWatchdogTimeout(perRequestDataPtr);
  593.                 if (REQUEST.scriptSelector == kSCSITestMemoryScript) {
  594.                     scriptPtr = ((UInt32) REQUEST.perRequestIOTable.physicalMapping[0])
  595.                             + offsetof(PerRequestData, memoryMoveScript);
  596.                 }
  597.                 else {
  598.                     scriptPtr = ((UInt32) GLOBAL.scriptIOTable.physicalMapping[0])
  599.                             + kSCSIRestartScript;
  600.                 }
  601.                 StartScript(perRequestDataPtr, scriptPtr);
  602.             }
  603.             break;
  604.         case kBusResetRestart:            /* Bus reset: stall and restart the script    */
  605.             DelayForHardware(GLOBAL.msec250);
  606.             scriptPtr = ((UInt32) GLOBAL.scriptIOTable.physicalMapping[0])
  607.                         + kBusResetScriptRestart;
  608.             StartScript(perRequestDataPtr, scriptPtr);
  609.             break;
  610.         /*
  611.          * These are intermediate error values: restart at 'Fail'
  612.          */
  613.         case kIntFailStrangePhase:        /* Bug: unknown phase at phase loop        */
  614.         case kIntDataPhaseExpected:        /* At 'Data', but not in data phase        */
  615.         case kIntPreparationFailed:        /* CTEST == 0 after prep restart        */
  616.         case kIntDataOutNoData:            /* CTEST not == 2 at DATO phase            */
  617.         case kIntDataInNoData:            /* CTEST not == 1 at DATI phase            */
  618.         case kIntNotMsgInAfterStatus:    /* STS phase must be followed by MSGI    */
  619.             LogStatusString(ioResult, "\pIntermediate Script Error");
  620.             scriptPtr = ((UInt32) GLOBAL.scriptIOTable.physicalMapping[0])
  621.                             + kSCSIRundownScript;
  622.             StartScript(perRequestDataPtr, scriptPtr);
  623.             break;
  624.         case noErr:                        /* Successful completion                */
  625.             /*
  626.              * But wait, there's more -- the memory test will succeed after each
  627.              * "chunk" of DMA -- we catch this here and re-direct (to use a legal
  628.              * term) the secondary interrupt to the Software Task.
  629.              */
  630.             if (REQUEST.scriptSelector == kSCSITestMemoryScript
  631.              && IOPB.ioActCount < IOPB.ioReqCount)
  632.                  goto needAnotherPrepareMemory;
  633.              /* Else, continue at I/O completion */
  634.         default:
  635. allDone:
  636.             /*
  637.              * Note that we use an atomic memory sequence to retrieve the old value of
  638.              * the parameter block, then set it to NULL. This prevents two asynchronous
  639.              * routines from calling IOCommandIsComplete on the same parameter block.
  640.              * Ignoring atomic considerations, the while/if sequence is equivalent to
  641.              *        pb = REQUEST.pb;
  642.              *        if (pb != NULL) {
  643.              *            REQUEST.pb = NULL;
  644.              *            ... IOCommandIsComplete(...);
  645.              *        }
  646.              */
  647. #if 0 && USE_LOG_LIBRARY
  648.             WriteLogEntry(GLOBAL.logRecordPtr, 'SecC',
  649.                 LogFormat4(
  650.                     kLogFormatSigned, kLogFormatAddress,
  651.                     kLogFormatAddress, kLogFormatString
  652.                 ),
  653.                 (signed long) ioResult, REQUEST.pb,
  654.                 (((unsigned long) SCRIPT.commandCompleteByte) << 16) | SCRIPT.statusByte,
  655.                 "\pI/O complete"
  656.             );
  657. #endif
  658.             /*
  659.              * Watch out for re-entrancy problems here. Once we call IOCommandIsComplete,
  660.              * we must not continue the while loop as the REQUEST and REQUEST.pb may be
  661.              * re-used because of an asynchronous request started from the completion
  662.              * routine.
  663.              *
  664.              * Since we're done with this I/O request, checkpoint the per-request
  665.              * table, but don't release system resources.
  666.              *
  667.              * Note: status values are not passed back to the caller -- ioResult has
  668.              * the final operation status.
  669.              */
  670.             status = CheckpointIO(
  671.                         REQUEST.perRequestIOTable.preparationID,
  672.                         kMoreIOTransfers
  673.                     );
  674.             CheckStatus(status, "\pCheckpointIO perRequest table");
  675.             while ((pb = REQUEST.pb) != NULL) {
  676.                 if (IOPB.ioResult != kIOBusyStatus) {
  677.                     /*
  678.                      * We can be re-entered after KillIO. This needs improvement.
  679.                      */
  680.                     LogStatusString(IOPB.ioResult, "\pCompletion re-entered!!!");
  681.                     break;
  682.                 }
  683.                 if (CompareAndSwap((UInt32) pb, NULL, (UInt32 *) &REQUEST.pb)) {
  684.                     CancelWatchdogTimer(perRequestDataPtr);
  685.                     if (REQUEST.scriptSelector == kSCSICommandScript) {
  686.                         scsiParamPtr = (NCRSCSIParamPtr) IOPB.ioMisc;
  687.                         SCSI.statusByte = SCRIPT.statusByte;
  688.                         SCSI.messageByte = SCRIPT.commandCompleteByte;
  689.                         if (ioResult == noErr && SCSI.statusByte != kScsiCommandStatusGood) {
  690.                             ioResult = scsiNonZeroStatus;
  691.                             LogHex(SCSI.statusByte, "\pNon-zero ioResult");
  692.                         }
  693.                     }
  694.                     /*
  695.                      * Release the user I/O request physical memory allocations.
  696.                      */
  697.                     CheckpointIOTable(&REQUEST.scsiIOTable);
  698.                     /*
  699.                      * Here's where we should checkpoint the PerRequest record. Do not
  700.                      * touch pb after calling IOCommandIsComplete as it may be use
  701.                      * again. This means that we must break out of the while loop.
  702.                      */
  703.                     Timestamp('IOC+');        /* IOC- is in the test application        */
  704.                     status = IOCommandIsComplete(REQUEST.ioCommandID, ioResult);
  705.                     break;
  706.                 }
  707.             }
  708.             break;
  709.         }
  710.         return (noErr);
  711. #undef SCSI
  712. #undef PB
  713. }
  714.  
  715. OSErr
  716. StoreDMAParameters(
  717.         PerRequestDataPtr        perRequestDataPtr
  718.     )
  719. {
  720.         OSErr                    status;
  721.         UInt32                    ioBufferPhysAddress;
  722.         UInt32                    callerPhysAddress;
  723.         UInt32                    temp;
  724. #define PB        (*((IOParam *) REQUEST.pb)) 
  725. #define SCSI    (* ((NCRSCSIParamPtr) PB.ioMisc))
  726.  
  727.         Trace(StoreDMAParameters);
  728.         status = PrepareNextDMA(perRequestDataPtr);
  729.         if (status == noErr) {
  730.             switch (REQUEST.scriptSelector) {
  731.             case kSCSITestISRScript:
  732.                 REQUEST.memoryMoveScript[0] = kIntOpcode;    /* Already swapped        */
  733.                 REQUEST.memoryMoveScript[1] = EndianSwap32Bit(noErr);
  734.                 break;
  735.             case kSCSITestMemoryScript:
  736.                 ioBufferPhysAddress = EndianSwap32Bit(
  737.                         (UInt32) REQUEST.thisDMATransfer.base);
  738.                 callerPhysAddress = EndianSwap32Bit(
  739.                             ((UInt32) SCSI.memTestPhysAddress)
  740.                             + REQUEST.thisDMATransfer.length
  741.                     );
  742.                 REQUEST.memoryMoveScript[0] =
  743.                     EndianSwap32Bit(0xC0000000 | REQUEST.thisDMATransfer.length);
  744.                 switch (SCSI.driverAction) {
  745.                 case kNCRDriverInputAllowed:    /* PBRead (physAddress->ioBuffer)    */
  746.                     REQUEST.memoryMoveScript[1] = callerPhysAddress;
  747.                     REQUEST.memoryMoveScript[2] = ioBufferPhysAddress;
  748.                     break;
  749.                 case kNCRDriverOutputAllowed:    /* PBWrite (ioBuffer->physAddress)    */
  750.                     REQUEST.memoryMoveScript[1] = ioBufferPhysAddress;
  751.                     REQUEST.memoryMoveScript[2] = callerPhysAddress;
  752.                     break;
  753.                 }
  754.                 PB.ioActCount += REQUEST.thisDMATransfer.length;
  755.                 /*
  756.                  * If this I/O burst will complete the operation, store a "normal"
  757.                  * status; if we'll need further preparation, store a special status.
  758.                  */
  759.                 REQUEST.memoryMoveScript[3] = kIntOpcode;
  760.                 temp = (PB.ioActCount >= PB.ioReqCount)
  761.                         ? noErr
  762.                         : kIntMemTestNeedsAnotherPreparation;
  763.                 REQUEST.memoryMoveScript[4] = EndianSwap32Bit(temp);
  764.                 WriteByte(DMODE, SCSI.memTestBurstLength);
  765. #if 0 && USE_LOG_LIBRARY
  766.                 WriteLogEntry(GLOBAL.logRecordPtr, 'GoIo',
  767.                     LogFormat(kLogFormatUnsigned, kLogFormatUnsigned, kLogFormatString),
  768.                     REQUEST.thisDMATransfer.length,
  769.                     SCSI.memTestBurstLength,
  770.                     "\pLen Burst"
  771.                 );
  772. #endif
  773.                 break;
  774.             default: /* SCSI command */
  775.                 WriteByte(CTEST0, SCSI.driverAction);
  776.                 SCRIPT.dataTable.address = EndianSwap32Bit((UInt32)
  777.                             (UInt32) REQUEST.thisDMATransfer.base);
  778.                 SCRIPT.dataTable.byteCount =
  779.                             EndianSwap32Bit(REQUEST.thisDMATransfer.length);
  780.                 /*
  781.                  * Increment the actual transfer count -- this is not quite correct,
  782.                  * as we haven't actually transferred anything yet. It would be better
  783.                  * to increment the count using the actual DMA values, but this would
  784.                  * require an extra interrupt or some other fiddling in the script.
  785.                  */
  786.                 PB.ioActCount += REQUEST.thisDMATransfer.length;
  787. #if 1 && LOG_LIBRARY
  788.                 WriteLogEntry(GLOBAL.logRecordPtr, 'GoIO',
  789.                     LogFormat4(kLogFormatUnsigned, kLogFormatAddress,
  790.                         kLogFormatUnsigned, kLogFormatString),
  791.                     (UInt32) SCSI.driverAction,
  792.                     EndianSwap32Bit(REQUEST.scriptData.dataTable.address),
  793.                     EndianSwap32Bit(REQUEST.scriptData.dataTable.byteCount),
  794.                     "\paction, addr, count"
  795.                 );
  796. #endif
  797.             }
  798.         }
  799.         /*
  800.          * We just stored something into the perRequest table that the NCR chip
  801.          * will read. Since we can be called from primary interrupt, we can't
  802.          * call CheckpointIO. SynchronizeIO will do what we need, or so I hope.
  803.          */
  804.         SynchronizeIO();
  805.         return (status);
  806. #undef PB
  807. #undef SCSI
  808. }
  809.  
  810. /*
  811.  * This function starts, or re-starts, the NCR card. The parameter is the script
  812.  * physical address. To restart the script, use the value in the DSP register.
  813.  */
  814. void
  815. StartScript(
  816.         register PerRequestDataPtr perRequestDataPtr,
  817.         UInt32                    scriptPtr
  818.     )
  819. {
  820.         OSErr                    status;
  821.         UInt8                    dmode;                /* 0x38 (for manual restart)    */
  822.         UInt8                    dcntl;                /* 0x3B (for manual restart)    */
  823.  
  824.         //* Trace(StartScript);
  825.         /*
  826.          * Before calling this routine, the driver has messed with the per-request
  827.          * record. Checkpoint it to ensure that the cacheing is coherent.
  828.          */
  829.         status = CheckpointIO(
  830.                     REQUEST.perRequestIOTable.preparationID,
  831.                     kNextIOIsInput | kNextIOIsOutput
  832.                 );
  833.         CheckStatus(status, "\pCheckpoint perRequest restart");
  834.         /*
  835.          * The NCR chip is unhappy if we try to set these bits inside a script.
  836.          *
  837.          * Enable DMA interrupts (register 0x39 0xB9):
  838.          *    bit6        Master Data Parity Error
  839.          *    bit5        Bus Fault
  840.          *    bit4        Aborted
  841.          *    bit3        Single-step interrupt.
  842.          *    bit2        Script interrupt
  843.          *    bit0        Illegal script instruction
  844.          */
  845.         WriteByte(DIEN, bit6 | bit5 | bit4 | bit3 | bit2 | bit0);
  846.         /*
  847.          * SCSI interrupts (register 0x40 0xC0)
  848.          *    bit7        Phase mismatch
  849.          *    bit3        SCSI gross error
  850.          *    bit2        Unexpected disconnect
  851.          *    bit1        Bus reset (from an external device)
  852.          *    bit0        Parity
  853.          * Do not interrupt on function complete, selected, or reselected.
  854.          */
  855.         WriteByte(SIEN0, bit7 | bit3 | bit2 | bit1 | bit0);
  856.         /*
  857.          * SCSI interrupts (register 0x41 0xC1)
  858.          *    bit2        Selection timeout
  859.          *    bit1        General purpose timer (used only for bus reset)
  860.          */
  861.         WriteByte(SIEN1, bit2 | bit1);
  862.         /*
  863.          * Restart the script - it will continue by jumping to one of the loops.
  864.          * -- at least, this is the theory
  865.          */
  866.         Timestamp('DMA+');
  867.         WriteLong(DSP, scriptPtr);
  868.         SynchronizeIO();                            /* eieio                        */
  869.         if (SINGLE_STEP) {
  870.             /*
  871.              * If manual-start is turned on, or we are in "single-step" mode,
  872.              * we must restart the script by setting dcntl bit2. It is unlikely that
  873.              * this still works correctly.
  874.              */
  875.             dmode = ReadByte(DMODE);                /* Has manual start bit            */
  876.             dcntl = ReadByte(DCNTL);                /* Has single step bit            */
  877.             if ((dmode & bit0) != 0 || (dcntl & bit4) != 0) {
  878.                 WriteByte(DCNTL, dcntl | bit2);
  879.                 SynchronizeIO();                    /* eieio                        */
  880.             }
  881.         }
  882.         //** LogHex(scriptPtr - ((UInt32) REQUEST.scriptBaseAddress), "\pStartScript");
  883. }
  884.  
  885. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  886.  * StartWatchdogTimeout
  887.  *
  888.  * Start an independent timer that will let us abort a run-away NCR SCSI operation.
  889.  * We ignore any errors.
  890.  *
  891.  * This must be called from Secondary Interrupt Level for now.
  892.  */
  893. void
  894. StartWatchdogTimeout(
  895.         register PerRequestDataPtr perRequestDataPtr
  896.     )
  897. {
  898.         OSStatus                osStatus;
  899.         AbsoluteTime            completionTime;
  900.         NCRSCSIParamPtr            scsiParamPtr;
  901.         volatile AbsoluteTime    now;        /* This must be volatile: see below        */
  902. #define SCSI    (*scsiParamPtr)
  903.  
  904.         Trace(StartWatchdogTimeout);
  905.         Timestamp('SWT+');
  906.         scsiParamPtr = (NCRSCSIParamPtr) (REQUEST.pb)->ioParam.ioMisc;
  907.         if (REQUEST.watchdogTimeout == kNoSCSITimeout
  908.          || REQUEST.watchdogTimeout == durationForever)
  909.              REQUEST.timerID = kInvalidID;
  910.         else {
  911.             /*
  912.              * These two statements must not be combined as some compilers
  913.              * cannot optimize functions returning struct's.
  914.              */
  915.             now = UpTime();
  916.             completionTime = AddDurationToAbsolute(REQUEST.watchdogTimeout, now);
  917.             osStatus = SetInterruptTimer(
  918.                         &completionTime,
  919.                         WatchdogTimerCompletion,
  920.                         perRequestDataPtr,
  921.                         &REQUEST.timerID
  922.                     );
  923.             CheckStatus(osStatus, "\pSetInterruptTimer");
  924.             if (osStatus != noErr)
  925.                 REQUEST.timerID = kInvalidID;
  926.         }
  927.         Timestamp('SWT-');
  928. #undef SCSI
  929. }
  930.  
  931. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  932.  * WatchdogTimerCompletion is a timer completion routine that stops a runaway
  933.  * script.
  934.  */
  935. OSStatus
  936. WatchdogTimerCompletion(
  937.         void                    *p1,    /* PerRequestDataPtr                        */
  938.         void                    *p2        /* Current program counter -- unused        */
  939.     )
  940. {
  941.         UInt8                    istat;
  942.         register PerRequestDataPtr perRequestDataPtr;
  943.  
  944.         Trace(WatchdogTimerCompletion);
  945.         UNUSED(p2);                        /* Current program counter -- unused        */
  946.         perRequestDataPtr = (PerRequestDataPtr) p1;
  947. #if 0 && USE_LOG_LIBRARY
  948.         WriteLogEntry(GLOBAL.logRecordPtr, 'Tout',
  949.             LogFormat5(kLogFormatAddress, kLogFormatAddress,
  950.                 kLogFormatAddress, kLogFormatAddress, kLogFormatString),
  951.             p1, p2, REQUEST.timerID, REQUEST.pb,
  952.             "\pp1, p2, id, pb @ TimerComplete"
  953.         );
  954. #endif
  955.         if (REQUEST.pb != NULL) {
  956.             LogString("\pI/O still running");
  957.             /*
  958.              * The timer fired but I/O is still running. Stop the ckip. Umm, this
  959.              * doesn't really work: we can finish the Macintosh side, but but
  960.              * the SCSI bus will be stuck until we force bus-reset. I don't know
  961.              * the right solution to this problem -- it might require cooperation
  962.              * from the application using this driver. In any case, it is specific
  963.              * to the particular hardware device.
  964.              */
  965.             istat = ReadByte(ISTAT);
  966.             WriteByte(ISTAT, istat | 0x80);            /* Abort NCR Chip                */
  967.             istat = ReadByte(ISTAT);
  968.         }
  969.         return (noErr);
  970. }
  971.  
  972. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  973.  * Cancel a pending timer. This must be called from SecondaryInterrupt level.
  974.  * An atomic sequence is used to prevent the timer from being cancelled twice.
  975.  */
  976. void
  977. CancelWatchdogTimer(
  978.         PerRequestDataPtr        perRequestDataPtr
  979.     )
  980. {
  981.         TimerID                    timerID;
  982.         AbsoluteTime            timeRemaining;
  983.         OSStatus                osStatus;
  984.         
  985.         Trace(CancelWatchdogTimer);
  986.         Timestamp('CWT+');
  987.         while ((timerID = REQUEST.timerID) != kInvalidID) {
  988.             if (CompareAndSwap(
  989.                     (UInt32) timerID, kInvalidID, (UInt32 *) &REQUEST.timerID)) {
  990.                 osStatus = CancelTimer(timerID, &timeRemaining);
  991.                 CheckStatus(osStatus, "\pCancelTimer");
  992. #if 0 && LOG_LIBRARY
  993.                 {
  994.                     Nanoseconds                nanosecondsRemaining;
  995.                 
  996.                     nanosecondsRemaining = AbsoluteToNanoseconds(timeRemaining);
  997.                     LogDecimal(nanosecondsRemaining.lo, "\pAfter CancelTimer");
  998.                 }
  999. #endif
  1000.                 break;
  1001.             }
  1002.         }
  1003.         Timestamp('CWT-');
  1004. }
  1005.  
  1006. #ifndef LogScriptInterrupt
  1007. void
  1008. LogScriptInterrupt(
  1009.         register PerRequestDataPtr perRequestDataPtr
  1010.     )
  1011. {
  1012.         Str255                work;
  1013.         const StringPtr        gBusPhase[] = {            /* In sbcl and socl                 */
  1014.             "\p DATO", "\p DATI", "\p CMD", "\p STS",
  1015.             "\p ResO", "\p ResI", "\p MSGO", "\p MSGI"
  1016.         };
  1017.  
  1018.         //** Trace(LogScriptInterrupt);
  1019.         work[0] = 0;
  1020.         AppendHexLeadingZeros(work, SHADOW.scriptPCOffset, 3);
  1021.         AppendChar(work, ' '); 
  1022.         AppendHexLeadingZeros(work, SHADOW.istat, 2);        /* 14    Interrupt status    */
  1023.         AppendChar(work, ' '); 
  1024.         AppendHexLeadingZeros(work, SHADOW.sbcl, 2);        /* 0B    Active bus status    */
  1025.         AppendChar(work, ' '); 
  1026.         AppendHexLeadingZeros(work, SHADOW.dstat, 2);        /* 0C    DMA status            */
  1027.         AppendChar(work, ' '); 
  1028.         AppendHexLeadingZeros(work, SHADOW.sist0, 2);        /* 42    SCSI interrupt stat    */
  1029.         AppendChar(work, ' '); 
  1030.         AppendHexLeadingZeros(work, SHADOW.sist1, 2);        /* 43    SCSI interrupt stat    */
  1031.         if ((SHADOW.sbcl & bit5) == 0)
  1032.             PStrCat(work, "\p ~BSY");
  1033.         else {
  1034.             if ((SHADOW.sbcl & bit7) != 0)
  1035.                 PStrCat(work, "\p REQ");
  1036.             if ((SHADOW.sbcl & bit6) != 0)
  1037.                 PStrCat(work, "\p ACK");
  1038.             PStrCat(work, gBusPhase[SHADOW.sbcl & 0x07]);
  1039.         }
  1040.         WriteLogEntry(GLOBAL.logRecordPtr, ' ISR', LogStringFormat, work);
  1041. }
  1042. #endif
  1043.  
  1044. #if USE_LOG_LIBRARY
  1045. /*
  1046.  * Dump the entire register set.
  1047.  */
  1048. void
  1049. DumpRegisters(
  1050.         register PerRequestDataPtr perRequestDataPtr
  1051.     )
  1052. {
  1053.         int                    i;
  1054.         Str255                work;
  1055.  
  1056.         //** Trace(DumpRegisters);
  1057. #define ncrRegisters    ((UInt32 *) GLOBAL.pciCardBaseAddress)
  1058.         for (i = kRegisterBase; i < kIORegisterMax; i += 16) {
  1059.             work[0] = 0;
  1060.             AppendHexLeadingZeros(work, i, 2);
  1061.             WriteLogEntry(GLOBAL.logRecordPtr, 'Dump',
  1062.                 LogFormat5(kLogFormatAddress, kLogFormatAddress,
  1063.                         kLogFormatAddress, kLogFormatAddress, kLogFormatString),
  1064.                 ncrRegisters[(i / sizeof (UInt32)) + 0],
  1065.                 ncrRegisters[(i / sizeof (UInt32)) + 1],
  1066.                 ncrRegisters[(i / sizeof (UInt32)) + 2],
  1067.                 ncrRegisters[(i / sizeof (UInt32)) + 3],
  1068.                 work                
  1069.             );
  1070.         }
  1071.         WriteLogEntry(GLOBAL.logRecordPtr, 'Dump',
  1072.             LogFormat3(kLogFormatAddress, kLogFormatAddress, kLogFormatString),
  1073.             SCRIPT.deviceIDTable.byteCount, SCRIPT.deviceIDTable.address, "\pTarget ID"
  1074.         );
  1075.         WriteLogEntry(GLOBAL.logRecordPtr, 'Dump',
  1076.             LogFormat3(kLogFormatAddress, kLogFormatAddress, kLogFormatString),
  1077.             SCRIPT.idMsgTable.byteCount, SCRIPT.idMsgTable.address, "\pID MSG"
  1078.         );
  1079.         WriteLogEntry(GLOBAL.logRecordPtr, 'Dump',
  1080.             LogFormat3(kLogFormatAddress, kLogFormatAddress, kLogFormatString),
  1081.             SCRIPT.commandTable.byteCount, SCRIPT.commandTable.address, "\pCommand"
  1082.         );
  1083.         WriteLogEntry(GLOBAL.logRecordPtr, 'Dump',
  1084.             LogFormat3(kLogFormatAddress, kLogFormatAddress, kLogFormatString),
  1085.             SCRIPT.dataTable.byteCount, SCRIPT.dataTable.address, "\pData"
  1086.         );
  1087. }
  1088. #endif
  1089.  
  1090.