Src/ContinueTesting.c

/*                                  ContinueTesting.c                           */
/*
 * ContinueTesting.c
 * Copyright © 93 Apple Computer Inc. All Rights Reserved.
 *
 * Run the dialog until the user has had enough.
 */
#include "SCSIAsyncSample.h"
#include "SCSIDefinitions.h"
pascal void
PerformTestIOCompletion(
        void                        *ioPtr
    );
 
#define INFO        (*infoPtr)
 
void                                ComputeTestParameters(
        register InfoPtr                infoPtr
    );
void                                PerformTest(
        register InfoPtr                infoPtr
    );
    
 
 
void
StartTesting(void)
{
        InfoPtr                         infoPtr;
 
        for (infoPtr = (InfoPtr) infoPtrQueue.qHead;
                infoPtr != NULL;
                infoPtr = (InfoPtr) infoPtr->link) {
            if (INFO.validDevice) {
                INFO.sampleSum = INFO.sampleSumSquare = 0.0;
                INFO.deviceActive = TRUE;
                Microseconds(&INFO.testStartTime);
                if (INFO.enableAsync) {
                    /*
                     * This loop is entered once when we start the actual test.
                     * It is only called for asychronous threads.
                     */
                    ComputeTestParameters(infoPtr);
                    PerformTest(infoPtr);
                }
            }
        }
}
 
void
ContinueTesting(void)
{
        InfoPtr                         infoPtr;
        Boolean                         somethingIsBusy;
        static Boolean                  stoppedYet = FALSE;
        
        somethingIsBusy = FALSE;
        for (infoPtr = (InfoPtr) infoPtrQueue.qHead;
                infoPtr != NULL;
                infoPtr = (InfoPtr) infoPtr->link) {
            if (INFO.deviceActive && INFO.testCompleted == FALSE) {
                somethingIsBusy = TRUE;
                if (INFO.enableAsync == FALSE) {
                    /*
                     * This loop is entered each time through the event loop to
                     * read one request from a synchronous test thread. It is not
                     * called for asychronous threads.
                     */
                    ComputeTestParameters(infoPtr);
                    PerformTest(infoPtr);
                }
            }
        }
        if (somethingIsBusy == FALSE) {
            InitCursor();
            if (stoppedYet == FALSE) {  
                stoppedYet = TRUE;
                DisplayTestResults();
            }
        }
}
 
void VMStartMarker(void) { }
 
/*
 * These may be called from an I/O completion routine
 */
void
PerformTest(
        register InfoPtr            infoPtr
    )
{
        short                       commandLength;
        OSErr                       status;
        register short              i;
        register SCSIExecIOPB       *pb;
        register RequestMemoryPtr   requestMemoryPtr;
#define PB (*pb)
 
        ClearMemory((Ptr) INFO.vmHoldInfo, sizeof INFO.vmHoldInfo);
        CLEAR(INFO.command);
        if (INFO.transferSizeBlocks <= 256
         && INFO.blockCount <= 0x001FFFFFL) {
            INFO.command.scsi6.opcode = kScsiCmdRead6;
            INFO.command.scsi6.lbn3 = (INFO.blockNumber >> 16) & 0xFF;
            INFO.command.scsi6.lbn2 = (INFO.blockNumber >>  8) & 0xFF;
            INFO.command.scsi6.lbn1 = (INFO.blockNumber      ) & 0xFF;
            INFO.command.scsi6.len  = INFO.transferSizeBlocks & 0xFF;
            INFO.command.scsi6.lbn3 |= (INFO.deviceIdent.LUN << 5) & 0xE0;
            commandLength = sizeof INFO.command.scsi6;
        }
        else {
            INFO.command.scsi10.opcode = kScsiCmdRead6;
            INFO.command.scsi10.lbn4 = (INFO.blockNumber    >> 24) & 0xFF;
            INFO.command.scsi10.lbn3 = (INFO.blockNumber    >> 16) & 0xFF;
            INFO.command.scsi10.lbn2 = (INFO.blockNumber    >>  8) & 0xFF;
            INFO.command.scsi10.lbn1 = (INFO.blockNumber         ) & 0xFF;
            INFO.command.scsi10.len2 = (INFO.transferSizeBlocks >> 8) & 0xFF;
            INFO.command.scsi10.len1 = (INFO.transferSizeBlocks     ) & 0xFF;
            INFO.command.scsi10.lun |= (INFO.deviceIdent.LUN << 5) & 0xE0;
            commandLength = sizeof INFO.command.scsi10;
        }
        /*
         * Plug the parameters into the SCSI command block.
         */
        pb = INFO.pb;
        PB.scsiPBLength = INFO.pbSize;
        PB.scsiFunctionCode = SCSIExecIO;
        /*
         * Use the private pointer in the SCSI command block to link the
         * SCSI parameter block to the InfoRecord.
         */
        PB.scsiDriverStorage = (unsigned char *) infoPtr;
        PB.scsiCompletion = (INFO.enableAsync) ? PerformTestIOCompletion : NULL;
        PB.scsiTimeout = INFO.completionTimeout;
        PB.scsiSelectTimeout = INFO.completionTimeout;
        PB.scsiDevice = INFO.deviceIdent;
        PB.scsiCDBLength = commandLength;
        PB.scsiCDB.cdbPtr = (unsigned char *) &INFO.command;
        PB.scsiFlags = (scsiCDBIsPointer | scsiSIMQNoFreeze);
        PB.scsiFlags |= (INFO.enableDisconnect)
                    ? scsiDoDisconnect
                    : scsiDontDisconnect;
        if (INFO.bufferPtr == NULL || INFO.bufferLength == 0)
            PB.scsiFlags |= scsiDirectionNone;
        else {
            /*
             * If the user specified the transfer quantum == 1, select "polled"
             * transfers, otherwise, select "blind."
             */
            PB.scsiTransferType = (INFO.transferQuantum == 1)
                        ? scsiTransferPolled
                        : scsiTransferBlind;
            PB.scsiDataPtr = (unsigned char *) INFO.bufferPtr;
            PB.scsiDataLength = INFO.bufferLength;
            PB.scsiDataType = scsiDataBuffer;
            PB.scsiFlags |= scsiDirectionIn;
            PB.scsiHandshake[0] = 512;
            PB.scsiHandshake[1] = 0;
 
        }
        INFO.senseData.errorCode = 0;
        PB.scsiSensePtr = (unsigned char *) &INFO.senseData;
        PB.scsiSenseLength = sizeof INFO.senseData;
        if (gVirtualMemoryEnabled) {
            /* Function */
            /* Param block */
            INFO.vmHoldInfo[kVMParam].ptr = INFO.pb;
            INFO.vmHoldInfo[kVMParam].size = INFO.pbSize;
            /* Data buffer */
            INFO.vmHoldInfo[kVMBuffer].ptr = PB.scsiDataPtr;
            INFO.vmHoldInfo[kVMBuffer].size = PB.scsiDataLength;
            /* Info record (has command and sense buffer) */
            INFO.vmHoldInfo[kVMSense].ptr = PB.scsiSensePtr;
            INFO.vmHoldInfo[kVMSense].size = PB.scsiSenseLength;
            for (i = 0; i < kVMSize; i++) {
                if (INFO.vmHoldInfo[i].ptr != NULL) {
                    status = HoldMemory(
                                INFO.vmHoldInfo[i].ptr,
                                INFO.vmHoldInfo[i].size
                            );
                    if (status != noErr) {
                        while (i < kVMSize)
                            INFO.vmHoldInfo[i++].ptr = NULL;
                        break;
                    }
                }
            }
            if (status != noErr) {
                /*
                 * Something failed. Unwind before exiting.
                 */
                for (i = 0; i < kVMSize; i++) {
                    if (INFO.vmHoldInfo[i].ptr != NULL) {
                        (void) UnholdMemory(
                                INFO.vmHoldInfo[i].ptr,
                                INFO.vmHoldInfo[i].size
                            );
                    }
                }
            }
        }
        /*
         * What ho, here we go!
         */
        requestMemoryPtr = &INFO.requestMemory[INFO.transfersAttempted % kRequestMemory];
        INFO.requestMemoryPtr = requestMemoryPtr;
        requestMemoryPtr->blockNumber = INFO.blockNumber;
        ++INFO.transfersAttempted;
        Microseconds(&requestMemoryPtr->startTime);
        status = SCSIAction((SCSI_PB *) pb);
        if (PB.scsiResult == scsiRequestInProgress)
            ++INFO.asynchRequests;
        if (status != noErr) {
            if (PB.scsiCompletion == NULL && PB.scsiResult == noErr)
                PB.scsiResult = status;
            if (INFO.finalStatus == noErr)
                INFO.finalStatus = status;
        }
        /*
         * If we're asychronous, just exit - the I/O completion routine will
         * clean up the mess. If we're synchronous, we're done, so call the
         * I/O completion routine ourself.
         */
        if (PB.scsiCompletion == NULL)
            PerformTestIOCompletion(pb);            
}
 
/*
 * This is called by I/O completion.
 */
pascal void
PerformTestIOCompletion(
        void                        *ioPtr
    )
{
        register SCSIExecIOPB       *pb;
        register InfoPtr            infoPtr;
        short                       i;
        double                      sampleTime;
        UnsignedWide                difference;
        
        pb = (SCSIExecIOPB *) ioPtr;
        infoPtr = (InfoPtr) PB.scsiDriverStorage;
        Microseconds(&INFO.requestMemoryPtr->endTime);
        MicrosecondDelta(
            &INFO.requestMemoryPtr->startTime,
            &INFO.requestMemoryPtr->endTime,
            &difference
        );
        sampleTime = MicrosecondToDouble(&difference) / 1000000.0;
        INFO.sampleSum += sampleTime;
        INFO.sampleSumSquare += (sampleTime * sampleTime);
        /*
         * Release virtual memory
         */
        for (i = 0; i < kVMSize; i++) {
            if (INFO.vmHoldInfo[i].ptr != NULL) {
                (void) UnholdMemory(
                        INFO.vmHoldInfo[i].ptr,
                        INFO.vmHoldInfo[i].size
                    );
            }
        }
        ++INFO.transfersCompleted;
        /*
         * If an error occurred, set the global "stop" flag to prevent
         * errors from cascading.
         */
        if (PB.scsiResult != noErr && INFO.finalStatus == noErr)
            INFO.finalStatus = PB.scsiResult;
        if (INFO.finalStatus != noErr)
            INFO.testCompleted = TRUE;
        else if (INFO.totalTransfers != 0
              && INFO.transfersCompleted >= INFO.totalTransfers)
            INFO.testCompleted = TRUE;
        /*
         * If we're running asychronously and are still alive, start the
         * next transfer.
         */
        if (INFO.testCompleted) {
            INFO.testEndTime = INFO.requestMemoryPtr->endTime;
            INFO.deviceActive = FALSE;
        }
        else if (INFO.enableAsync) {
            ComputeTestParameters(infoPtr);
            PerformTest(infoPtr);
        }
}
 
void VMEndMarker(void) { }
 
void
ComputeTestParameters(
        register InfoPtr                infoPtr
    )
{
        if (INFO.finalStatus != noErr
         || (INFO.totalTransfers != 0
          && INFO.transfersCompleted >= INFO.totalTransfers))
            INFO.testCompleted = TRUE;
        else if (INFO.enableRandomSeek) {
            INFO.randomSeed = (INFO.randomSeed * 1103515245L) + 12345L;
            INFO.nextBlockNumber = INFO.randomSeed % INFO.totalLogicalBlocks;
            goto computeByteCount;
        }
        else {
            if (INFO.nextBlockNumber >= INFO.totalLogicalBlocks)
                INFO.nextBlockNumber = 0;
computeByteCount:
            INFO.blockNumber = INFO.nextBlockNumber;
            INFO.blockCount = INFO.transferSizeBlocks;
            if (INFO.blockNumber + INFO.blockCount >= INFO.totalLogicalBlocks)
                INFO.blockCount = INFO.totalLogicalBlocks - INFO.blockNumber;
            INFO.byteCount = INFO.transferSizeBlocks * INFO.logicalBlockLength;
            INFO.nextBlockNumber = INFO.blockNumber + INFO.blockCount;
        }
}