Src/GetDevicesToTest.c

/*                              GetDevicesToTest.c                              */
/*
 * GetDevicesToTest.c
 * Copyright © 93 Apple Computer Inc. All Rights Reserved.
 *
 * Run the dialog until the user has had enough.
 */
#include "SCSIAsyncSample.h"
#include "DialogUtilities.h"
#include "SCSIDefinitions.h"
 
 
void                        SetupDialog(void);
void                        MakeNewInfoRecord(void);
void                        RefreshParamBlock(void);
void                        RefreshDeviceInfo(void);
Boolean                     CheckInfoRecord(void);
void                        SaveInfoRecord(void);
OSErr                       SetupGetSCSIParamBlock(void);
OSErr                       SetupDeviceInquiry(void);
OSErr                       SetupReadCapacity(void);
void                        StoreDeviceString(
        unsigned char           *dst,
        unsigned char           *src,
        short                   srcLength
    );
unsigned long               GetFourBytes(
        unsigned char           *src
    );
 
/*
 * These are state flags that are valid only for the current
 * dialog.
 */
DialogPtr                   gDialog;
InfoPtr                     gCurrentInfoPtr;
#define INFO                (*gCurrentInfoPtr)
short                       gThreadIndex;
 
void
GetDevicesToTest(void)
{
        GrafPtr                         savePort;
        short                           itemHit;
        Boolean                         needNewInfoRecord;
        Boolean                         needToRefreshDeviceInfo;
        Boolean                         needToRefreshParamBlock;
        Boolean                         needToUpdateDialogValues;
        
        GetPort(&savePort);
        gDialog = GetNewDialog(DLOG_Query, NULL, (WindowPtr) -1L);
        SetCheckBox(gDialog, kQueryEnableAsync, FALSE);
        SetCheckBox(gDialog, kQueryEnableDisconnect, FALSE);
        SetDialogValue(gDialog, kQueryTotalRequests, 100);
        /*
         * For my system: prime the device to point to the CD-Rom
         */
        SetDialogValue(gDialog, kQueryHostBus, 1);
        SetDialogValue(gDialog, kQueryTargetID, 3);
        /* */
        ShowWindow(gDialog);
        SetPort(gDialog);
        gThreadIndex = 0;
        needNewInfoRecord = TRUE;
        InitCursor();
        for (;;) {
            if (needNewInfoRecord) {
                MakeNewInfoRecord();
                needNewInfoRecord = FALSE;
                needToRefreshParamBlock = TRUE;
                needToUpdateDialogValues = TRUE;
            }
            if (needToRefreshParamBlock) {
                RefreshParamBlock();
                needToRefreshParamBlock = FALSE;
                needToRefreshDeviceInfo = (INFO.pb != NULL);
                needToUpdateDialogValues = TRUE;
            }
            if (needToRefreshDeviceInfo) {
                RefreshDeviceInfo();
                needToRefreshDeviceInfo = FALSE;
                needToUpdateDialogValues = TRUE;
            }
            if (needToUpdateDialogValues) {
                SetupDialog();
                needToUpdateDialogValues = FALSE;
            }
            ModalDialog(NumericFilter, &itemHit);
            switch (itemHit) {
            case kQueryTest:
                if (CheckInfoRecord()) {
                    SaveInfoRecord();
                    needNewInfoRecord = TRUE;
                }
                break;
            case kQueryFinished:
                if (gCurrentInfoPtr != NULL)
                    DisposePtr((Ptr) gCurrentInfoPtr);
                goto exit;
            case kQueryHostBus:
                INFO.deviceIdent.bus = GetDialogValue(gDialog, kQueryHostBus);
                needToRefreshParamBlock = TRUE;
                break;
            case kQueryTargetID:
                INFO.deviceIdent.targetID = GetDialogValue(gDialog, kQueryTargetID);
                needToRefreshDeviceInfo = TRUE;
                break;
            case kQueryEnableAsync:
                SelectCheckBox(gDialog, kQueryEnableAsync, &INFO.enableAsync);
                break;
            case kQueryEnableDisconnect:
                SelectCheckBox(gDialog, kQueryEnableDisconnect, &INFO.enableDisconnect);
                break;
            case kQueryRandomSeek:
                SelectCheckBox(gDialog, kQueryRandomSeek, &INFO.enableRandomSeek);
                break;
            case kQueryTotalRequests:
                INFO.totalTransfers = GetDialogValue(gDialog, kQueryTotalRequests);
                break;
            case kQueryBlocksPerTransfer:
                INFO.transferSizeBlocks = GetDialogValue(gDialog, kQueryBlocksPerTransfer);
                break;
            case kQueryTimeout:
                INFO.completionTimeout = GetDialogValue(gDialog, kQueryTimeout);
            default:
                break;
            }
        }
exit:   ;
        DisposDialog(gDialog);
        SetPort(savePort);
}
 
/*
 * MakeNewInfoRecord creates a new, empty, InfoRecord and sets state flags
 * so that the first call to SetupDialog fills in the information.
 */
void
MakeNewInfoRecord(void)
{
        Str255                      work;
        
        gCurrentInfoPtr = (InfoPtr) NewPtrClear(sizeof (InfoRecord));
        if (gCurrentInfoPtr == NULL)
            FatalError(MemError(), "\pCan't make new InfoRecord");
        ++gThreadIndex;
        INFO.threadIndex = gThreadIndex;
        INFO.deviceActive = FALSE;
        INFO.testCompleted = FALSE;
        INFO.completionTimeout = (60L * 15L);           /* 15 seconds           */
        /*
         * Copy the current dialog parameters into the new info record.
         */
        INFO.deviceIdent.bus = GetDialogValue(gDialog, kQueryHostBus);
        INFO.deviceIdent.targetID = GetDialogValue(gDialog, kQueryTargetID);
        INFO.totalTransfers = GetDialogValue(gDialog, kQueryTotalRequests);
        INFO.transferSizeBlocks = GetDialogValue(gDialog, kQueryBlocksPerTransfer);
        INFO.completionTimeout = GetDialogValue(gDialog, kQueryTimeout);
        INFO.enableAsync = GetDialogButton(gDialog, kQueryEnableAsync) == kCheckedButton;
        INFO.enableDisconnect = GetDialogButton(gDialog, kQueryEnableDisconnect) == kCheckedButton;
        INFO.enableRandomSeek = GetDialogButton(gDialog, kQueryRandomSeek) == kCheckedButton;
        GetDateTime(&INFO.randomSeed);
        INFO.randomSeed ^= TickCount();
        NumToString(INFO.threadIndex, work);
        pstrcat(work, "\p Thread index");
        SetDialogText(gDialog, kQueryThreadNumber, work);
}
 
/*
 * SetupDialog is executed each time through the dialog loop to enable and
 * disable editable items and to get information about the active device.
 */
void
SetupDialog(void)
{       
        EnableDialogItem(gDialog, kQueryTest, INFO.validDevice);
        EnableDialogItem(gDialog, kQueryTotalRequests, INFO.validDevice);
        EnableDialogItem(gDialog, kQueryBlocksPerTransfer, INFO.validDevice);
        EnableDialogItem(gDialog, kQueryTimeout, INFO.validDevice);
        EnableDialogItem(gDialog, kQueryEnableAsync, INFO.validDevice);
        EnableDialogItem(gDialog, kQueryEnableDisconnect, INFO.validDevice);
        if (INFO.validDevice) {
            SetDialogValue(gDialog, kQueryBlocksPerTransfer, INFO.transferSizeBlocks);
            SetDialogValue(gDialog, kQueryTimeout, INFO.completionTimeout);
            SetDialogText(gDialog, kQueryVendorID, (StringPtr) INFO.vendor);
            SetDialogText(gDialog, kQueryProduct, (StringPtr) INFO.product);
            SetDialogValue(gDialog, kQueryLogicalBlockLength, INFO.logicalBlockLength);
            SetDialogValue(gDialog, kQueryBlocksOnDevice, INFO.totalLogicalBlocks);
        }
        else {
            SetDialogText(gDialog, kQueryBlocksPerTransfer, "\p");
            SetDialogText(gDialog, kQueryTimeout, "\p");
            SetDialogText(gDialog, kQueryVendorID, "\p");
            SetDialogText(gDialog, kQueryProduct, "\p");
            SetDialogText(gDialog, kQueryLogicalBlockLength, "\p");
            SetDialogText(gDialog, kQueryBlocksOnDevice, "\p");
        }
}
 
void
RefreshDeviceInfo(void)
{
        OSErr               status;
        
        if (INFO.pb != NULL) {
            status = SetupDeviceInquiry();
            if (status == noErr)
                status = SetupReadCapacity();
            INFO.validDevice = (status == noErr);
        }
}
 
/*
 * Return TRUE if the InfoRecord is valid - this allocates the data buffer.
 */
Boolean
CheckInfoRecord(void)
{
        Str15                   work;
        
        if (INFO.validDevice == FALSE)
            return (FALSE);
        else {
            INFO.totalTransfers = GetDialogValue(gDialog, kQueryTotalRequests);
            INFO.transferSizeBlocks = GetDialogValue(gDialog, kQueryBlocksPerTransfer);
            INFO.completionTimeout = GetDialogValue(gDialog, kQueryTimeout);
            INFO.bufferLength = (INFO.transferSizeBlocks * INFO.logicalBlockLength);
            INFO.bufferPtr = NewPtr(INFO.bufferLength);
            if (INFO.bufferPtr == NULL) {
                NumToString(INFO.bufferLength, work);
                ParamText(work, "\p", "\p", "\p");
                StopAlert(ALRT_NoMemory, NULL);
                return (FALSE);
            }
            /* To do: read one block to verify that everything is in place */
            return (TRUE);
        }
}
 
/*
 * SaveInfoRecord links this InfoRecord into the active queue.
 */
void
SaveInfoRecord(void)
{
        Enqueue((QElemPtr) gCurrentInfoPtr, &infoPtrQueue);
        gCurrentInfoPtr = NULL;     
}
 
/*
 * Get the SCSI 4.3 Exec I/O parameter block. If this succeeds,
 * INFO.pb points to the parameter block, and INFO.pbSize has
 * its length. On failure, INFO.pb is NULL and INFO.pbSize zero.
 */
void
RefreshParamBlock(void)
{
        OSErr                   status;
        SCSIBusInquiryPB        pb;
 
        CLEAR(pb);
        pb.scsiPBLength = sizeof pb;
        pb.scsiFunctionCode = SCSIBusInquiry;
        pb.scsiCompletion = NULL;
        pb.scsiFlags = 0;
        pb.scsiDevice = INFO.deviceIdent;
        SCSIAction((SCSI_PB *) &pb);
        status = pb.scsiResult;
        if (status == noErr) {
            if (INFO.pb != NULL && INFO.pbSize != pb.scsiIOpbSize) {
                DisposePtr((Ptr) INFO.pb);
                INFO.pb = NULL;
                INFO.pbSize = 0;
            }
            if (INFO.pb == NULL) {
                INFO.pb = (SCSIExecIOPB *) NewPtr(pb.scsiIOpbSize);
                if (INFO.pb == NULL)
                    status = MemError();
                else {
                    INFO.pbSize = pb.scsiIOpbSize;
                }
            }
        }
        if (status != noErr) {
            NonFatalError(status, "\pCan't allocate SCSI Parameter Block");
            if (INFO.pb != NULL) {
                DisposePtr((Ptr) INFO.pb);
                INFO.pb = NULL;
                INFO.pbSize = 0;
            }
        }
}
 
/*
 * Get the Device Inquiry (vendor, product) data. Return error status.
 */
OSErr
SetupDeviceInquiry(void)
{
        OSErr                       status;
        SCSI_Inquiry_Data           inquiryData;
        const SCSI_6_Byte_Command   inquiryCmd = {
            kScsiCmdInquiry, 0, 0, 0, sizeof inquiryData, 0
        };
 
        status = DoSCSISynchronousIO(
                    gCurrentInfoPtr,                    /* -> INFO              */
                    (SCSI_CommandPtr) &inquiryCmd,      /* Command              */
                    scsiDirectionIn,                    /* Reading from device  */
                    (Ptr) &inquiryData,                 /* -> data buffer       */
                    sizeof inquiryData                  /* data buffer size     */
                );
        if (status == noErr) {
            StoreDeviceString(INFO.vendor, inquiryData.vendor, sizeof inquiryData.vendor);
            StoreDeviceString(INFO.product, inquiryData.product, sizeof inquiryData.product);
        } 
        return (status);
}
 
/*
 * Get the drive capacity (blocks, logical block length) for the device.
 * Return error status.
 */
OSErr
SetupReadCapacity(void)
{
        OSErr                       status;
        SCSI_Capacity_Data          capacityData;
        const SCSI_10_Byte_Command  capacityCmd = {
            kScsiCmdReadCapacity, 0, 0, 0, 0, 0, 0, 0, 0, 0
        };
 
        status = DoSCSISynchronousIO(
                    gCurrentInfoPtr,                    /* -> INFO              */
                    (SCSI_CommandPtr) &capacityCmd,     /* Command              */
                    scsiDirectionIn,                    /* Reading from device  */
                    (Ptr) &capacityData,                /* -> data buffer       */
                    sizeof capacityData                 /* data buffer size     */
                );
        if (status == noErr) {
            INFO.totalLogicalBlocks = GetFourBytes(&capacityData.lbn4);
            INFO.logicalBlockLength = GetFourBytes(&capacityData.len4);
        } 
        return (status);
}
 
void
StoreDeviceString(
        unsigned char               *dst,
        unsigned char               *src,
        short                       srcLength
    )
{
        short                       i;
 
        for (i = srcLength; i > 0 && src[i - 1] == ' '; --i) 
            ;
        BlockMove(src, &dst[1], i);
        dst[0] = i;
}
 
unsigned long
GetFourBytes(
        unsigned char               *src
    )
{
        register unsigned long      result;
        
        result = src[0] & 0xFF;
        result <<= 8;
        result |= (src[1] & 0xFF);
        result <<= 8;
        result |= (src[2] & 0xFF);
        result <<= 8;
        result |= (src[3] & 0xFF);
        return (result);
}