Src/DoSCSISynchronousIO.c

/*                              DoSCSISynchronousIO.c                           */
/*
 * DoSCSIExecIO.c
 * Copyright © 1992-93 Apple Computer Inc. All Rights Reserved.
 *
 * Talk to the Macintosh SCSI Manager 4.3 using the "new" interface. This executes
 * a single SCSI Command on a device using parameters from the INFO record. Note:
 * this command is only used for dialog setup and always executes synchronously
 * with polled I/O and no disconnect.
 *
 * Calling Sequence:
 *      OSErr               DoSCSISynchronousIO(
 *              InfoPtr         infoPtr,
 *              SCSI_CommandPtr commandPtr,
 *              unsigned long   scsiFlags,
 *              Ptr             bufferPtr,
 *              unsigned long   transferLength
 *          );
 * The following information is taken from the InfoRecord:
 *              ScsiCmdBlock    *pb             -- the Scsi Command block
 *              unsigned long   pbSize          -- the size of the command block
 *              ProcPtr         ioCompletion    -- NULL for synch, else asynch
 *              DeviceIdent     deviceIdent     -- bus/target/LUN
 * After completion, the following information is stored in the InfoRecord
 *              unsigned short  stsByte         -- from Status phase
 *              unsigned short  msgByte         -- Command Complete message
 * Return codes:
 *  noErr           normal
 *  statusErr       Device returned "Check condition" and SCSI Manager was able
 *                  to successfully issue Request Sense. There is data in the
 *                  Sense Record, but you cannot assume that the original request
 *                  succeeded.
 *  paramErr        Could not determine the command length.
 *  sc...           Other error
 */
#include "SCSIAsyncSample.h"
#include <GestaltEqu.h>
#include <Memory.h>
#include <Events.h>
 
static void                     NextFunction(void);     /* For HoldMemory size  */
static Boolean                  IsVirtualMemoryRunning(void);
 
OSErr
DoSCSISynchronousIO(
        InfoPtr                 infoPtr,
        SCSI_CommandPtr         commandPtr,
        unsigned long           scsiFlags,
        Ptr                     bufferPtr,
        unsigned long           transferLength
    )
{
        OSErr                       status;
        register short              i;
        union {
            SCSIAbortCommandPB      abortCommandPB;
            SCSIResetBusPB          resetBusPB;
            SCSIReleaseQPB          releaseQPB;
        } pb;
        auto void                   *vmProtectedStackBase;  /* Last local var   */
#define INFO    (*infoPtr)
#define PB      (*INFO.pb)
/*
 * These values are used to compute the size of the stack that we must hold in
 * protected (non-virtual) memory. kSCSIManagerStackEstimate is an estimate.
 */
#define kSCSILocalVariableSize  ( \
        (unsigned long) (((Ptr) &infoPtr) - ((Ptr) &vmProtectedStackBase))  \
    )
#define kSCSIManagerStackEstimate 512
#define kSCSIProtectedStackSize (kSCSIManagerStackEstimate + kSCSILocalVariableSize)
 
        status = noErr;
        ClearMemory((Ptr) INFO.pb, INFO.pbSize);
        ClearMemory((Ptr) INFO.vmHoldInfo, sizeof INFO.vmHoldInfo);
        /*
         * Setup the parameter block for the user's request.
         */
        PB.scsiPBLength = INFO.pbSize;
        PB.scsiFunctionCode = SCSIExecIO;
        PB.scsiCompletion = NULL;
        PB.scsiDriverStorage = (unsigned char *) infoPtr; /* for IOCompletion   */
        INFO.statusByte = 0xFF;                 /* Illegal value for debugging  */
        PB.scsiTimeout = INFO.completionTimeout;
        PB.scsiDevice = INFO.deviceIdent;
        PB.scsiCDBLength = GetSCSICDBLength(commandPtr);
        if (PB.scsiCDBLength == 0) {
            status = paramErr;
            goto exit;
        }
        /*
         * Copy the command block into the SCSI PB to simplify vm lockdown.
         */
        BlockMove(commandPtr, &PB.scsiCDB, PB.scsiCDBLength);
        /*
         * Specify the transfer direction, if any. scsiFlags should be one of
         *  scsiDirectionIn, scsiDirectionOut, or scsiDirectionNone
         */
        PB.scsiFlags = scsiFlags;       /* scsiDirectionIn or scsiDirectionOut  */
        if (bufferPtr == NULL || transferLength == 0)
            PB.scsiFlags = scsiDirectionNone;
        else {
            /*
             * If the user specified the transfer quantum == 1, select "polled"
             * transfers, otherwise, select "blind."
             */
            PB.scsiTransferType = scsiTransferPolled;
            PB.scsiDataPtr = (unsigned char *) bufferPtr;
            PB.scsiDataLength = transferLength;
            PB.scsiDataType = scsiDataBuffer;
            PB.scsiHandshake[0] = 1;
            PB.scsiHandshake[1] = 0;
        }
        PB.scsiSensePtr = (unsigned char *) &INFO.senseData;
        PB.scsiSenseLength = sizeof INFO.senseData;
        /*
         * The SCSI Manager "freezes" the device queue after a Check Condition
         * (even if AutoSense was enabled). Disable this to prevent hangs.
         */
        PB.scsiFlags |= (scsiDontDisconnect | scsiSIMQNoFreeze);
        /*
         * We are now ready to perform the operation. If virtual memory is active
         * however, we must lock down all memory segments that can be potentially
         * "touched" while the SCSI request is being executed. All of this is
         * needed for applications. For drivers, most of the following can be
         * ignored as the driver code and driver-specific resources are stored in
         * the System Heap, which is always "held" in physical memory.
         */
        if (gVirtualMemoryEnabled) {
            /*
             * Virtual memory is active. Lock all of the memory segments that we
             * need in "real" memory (i.e. non-paged pool) for the duration of the
             * call.
             */
            INFO.vmHoldInfo[kVMFunction].ptr = DoSCSISynchronousIO;
            INFO.vmHoldInfo[kVMFunction].size =
                (unsigned long) NextFunction - (unsigned long) DoSCSISynchronousIO;
            INFO.vmHoldInfo[kVMStack].ptr = (void *)
                (((unsigned long) &vmProtectedStackBase)
                - kSCSIManagerStackEstimate);
            INFO.vmHoldInfo[kVMStack].size = kSCSIProtectedStackSize;
            INFO.vmHoldInfo[kVMParam].ptr = INFO.pb;
            INFO.vmHoldInfo[kVMParam].size = INFO.pbSize;
            if (bufferPtr != NULL) {
                INFO.vmHoldInfo[kVMBuffer].ptr = PB.scsiDataPtr;
                INFO.vmHoldInfo[kVMBuffer].size = PB.scsiDataLength;
            }
            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
                            );
                    }
                }
            }
        }
        /*
         * Now, just call the New SCSI Manager.
         */
        if (status == noErr) {
            status = SCSIAction((SCSI_PB *) &PB);
            if (PB.scsiCompletion == NULL) {
                /*
                 * Synchronous request: The following should be
                 * moved to a "default" I/O Completion routine.
                 */
                if (status == noErr)
                    status = PB.scsiResult;
                /*
                 * These should never happen.
                 */
                if (status == scsiRequestInProgress) {
                    CLEAR(pb.abortCommandPB);
                    pb.abortCommandPB.scsiPBLength = sizeof pb.abortCommandPB;
                    pb.abortCommandPB.scsiFunctionCode = SCSIAbortCommand;
                    pb.abortCommandPB.scsiIOptr = INFO.pb;
                    (void) SCSIAction((SCSI_PB *) &pb.abortCommandPB);
                }
                if ((PB.scsiResultFlags & scsiBusNotFree) != 0) {
                    CLEAR(pb.resetBusPB);
                    pb.resetBusPB.scsiPBLength = sizeof pb.resetBusPB;
                    pb.resetBusPB.scsiFunctionCode = SCSIResetBus;
                    (void) SCSIAction((SCSI_PB *) &pb.resetBusPB);
                }
                if ((PB.scsiResultFlags & scsiSIMQFrozen) != 0) {
                    CLEAR(pb.releaseQPB);
                    pb.releaseQPB.scsiPBLength = sizeof pb.releaseQPB;
                    pb.releaseQPB.scsiFunctionCode = SCSIReleaseQ;
                    (void) SCSIAction((SCSI_PB *) &pb.releaseQPB);
                }
                for (i = 0; i < kVMSize; i++) {
                    if (INFO.vmHoldInfo[i].ptr != NULL) {
                        (void) UnholdMemory(
                                INFO.vmHoldInfo[i].ptr,
                                INFO.vmHoldInfo[i].size
                            );
                    }
                }
                INFO.actualTransferCount =
                    PB.scsiDataLength - PB.scsiDataResidual;
                if (status == scsiDataRunError
                 && PB.scsiDataResidual != PB.scsiDataLength)
                    status = noErr;
            }
        }
exit:   return (status);
#undef PB
}
 
static void NextFunction(void) { }  /* Dummy function for MacSCSICommand size   */