Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
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 */ |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14