home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 July: Mac OS SDK / Dev.CD Jul 00 SDK2.toast / Development Kits / Hardware / Mac OS USB DDK / Mac OS USB DDK 1.4.1 / Examples / USBSampleStorageDriver / UnitTableDriver / UnitTableFunctions.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-04-25  |  35.3 KB  |  1,275 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        UnitTableFunctions.c
  3.  
  4.     Contains:    This contains the function entry points for each function
  5.                 code passed to DoDriverIO.
  6.  
  7.     Version:    1.0
  8.  
  9.     Copyright:    © 1998-2000 by Apple Computer, Inc., all rights reserved.
  10.  
  11.  
  12. */
  13.  
  14. #include <Disks.h>
  15. #include <DriverGestalt.h>
  16. #include <Gestalt.h>
  17. #include <NameRegistry.h>
  18. #include <Power.h>
  19. #include <Resources.h>
  20. #include <Strings.h>
  21. #include <Devices.h>
  22.  
  23. // Unit Table Driver specifc headers
  24. #include "UnitTableDoDriverIO.h"
  25. #include "UnitTableFunctions.h"
  26. #include "UnitTableDriveQSupport.h"
  27. #include "UnitTableFloppySupport.h"
  28. #include "UnitTablePCExchangeSupport.h"
  29. #include "UnitTableReadWriteSupport.h"
  30. #include "UnitTableDriverIcons.h"
  31. #include "UnitTableDeviceAccess.h"
  32. #include "UnitTableCWDefines.h"
  33.  
  34. typedef struct DriveRequestPB
  35. {
  36.     volatile ParmBlkPtr                theIOPB;
  37.     IOCommandID                     ioCommandID;
  38.     volatile OSStatus                status;
  39.     UInt8                            currentExecutionState;
  40. } DriveRequestPB, *DriveRequestPBPtr;
  41.  
  42. typedef struct DriverGlobalsStructure
  43. {
  44.     DriveRequestPB                drivePB;
  45.     DriveRequestPB                ejectPB;            // Use this so that t
  46.     DriverRefNum                drvrRefNum;            // Our driver reference number
  47.     RegEntryID                    drvrRegEntryID;
  48. } DriverGlobalsStructure;
  49.  
  50. // The values for the states of the state machines
  51. enum
  52. {
  53.     // Mount State Machine
  54.     kMountStartState = 1,
  55.     kMountPreventRemovalState,
  56.     kMountInstallDriveState,
  57.  
  58.     // Eject State Machine
  59.     kEjectStartState,
  60.     kEjectAllowRemovalDone,
  61.     kEjectCartridgeDone
  62. };
  63.  
  64. // These supporting functions are in this file…
  65. static void     StandardControlCallCompletion(UInt32 userData, OSStatus status );
  66. static void     ReadWriteFinishCommand ( UInt32 userData, OSStatus status );
  67. static void     InitializeDriveAccessDone(UInt32 userData, OSStatus status );
  68.  
  69. static OSStatus SetMountInterruptTimer( void );
  70. static OSStatus ClearMountInterruptTimer( void );
  71. static OSStatus MountSecondaryInterrupt( void *p1, void *p2);
  72.  
  73. static void     MountTheCartridge( UInt32 userData, OSStatus status );
  74. static void        DriveInstallCompletion(Boolean wasSuccessful);
  75. static void     EjectTheCartridge( UInt32 userData, OSStatus status );
  76.  
  77. //----------------------------------------------------------------------------------
  78. //                                    Globals
  79. //----------------------------------------------------------------------------------
  80. static MediaIconStruct                    gDriveIcon;                            // static structure for control calls
  81. static TimerID                            gInterruptTimer = 0;
  82. static DriverGlobalsStructure            gTheDriverGlobals;
  83.  
  84. //----------------------------------------------------------------------------------
  85. //                                    Driver calls
  86. //----------------------------------------------------------------------------------
  87.  
  88. //    Always run at task level, can allocate and move memory.
  89. OSStatus DriverInitializeCmd ( DriverRefNum theRefNum, RegEntryIDPtr theRegEntryPtr )
  90. {
  91.     OSStatus    err = noErr;
  92.  
  93.     gTheDriverGlobals.drvrRefNum = theRefNum;                // Our driver reference number
  94.     
  95.     if ( theRegEntryPtr != nil )
  96.     {
  97.         RegistryEntryIDCopy( theRegEntryPtr, &gTheDriverGlobals.drvrRegEntryID );    // Our driver Reg Entry ID Ptr
  98.     }
  99.     else
  100.     {
  101.          // No RegEntryID was passed in
  102.         RegistryEntryIDInit( &gTheDriverGlobals.drvrRegEntryID );
  103.     }
  104.  
  105.     return (err);
  106. }
  107.  
  108. //    Always run at task level, can allocate and move memory.
  109. OSStatus DriverFinalizeCmd ( DriverRefNum theRefNum, RegEntryIDPtr theRegEntryPtr )
  110. {
  111. #pragma unused ( theRefNum, theRegEntryPtr )
  112.     OSStatus    err = noErr;
  113.  
  114.     return (err);
  115. }
  116.  
  117. //    Always run at task level, can allocate and move memory.
  118. OSStatus DriverSupersededCmd ( DriverRefNum theRefNum, RegEntryIDPtr theRegEntryPtr )
  119. {
  120. #pragma unused ( theRefNum, theRegEntryPtr )
  121.  
  122.     OSErr    err = noErr;
  123.  
  124.     return (err);
  125. }
  126.  
  127. //    Always run at task level, can allocate and move memory.
  128. OSStatus DriverReplaceCmd ( DriverRefNum theRefNum, RegEntryIDPtr theRegEntryPtr )
  129. {
  130. #pragma unused ( theRefNum, theRegEntryPtr )
  131.     OSStatus    err = noErr;
  132.  
  133.     return (err);
  134. }
  135.  
  136. //    Always run at task level, can allocate and move memory.
  137. OSStatus DriverOpenCmd (ParmBlkPtr pb)
  138. {
  139. #pragma unused (pb)
  140.     OSStatus    err         = noErr;
  141.     
  142.     err = InitializeDeviceAccess( gTheDriverGlobals.drvrRefNum, &gTheDriverGlobals.drvrRegEntryID );
  143.     
  144.     if( err == noErr )
  145.     {
  146.         gTheDriverGlobals.drivePB.theIOPB = nil;
  147.         gTheDriverGlobals.drivePB.currentExecutionState = kMountStartState;
  148.     
  149.         // Set up our color icon family
  150.         BuildMediaIconFamily();
  151.         
  152.         // Let the world know we can do DriverGestalt calls
  153.         DriverGestaltOn(gTheDriverGlobals.drvrRefNum);
  154.  
  155.         if( IsDeviceAccessEnabled() == false )
  156.         {
  157.             return noErr;
  158.         }
  159.         
  160.         err = InstallDriveQueueElement( gTheDriverGlobals.drvrRefNum );
  161.  
  162.         // See if we can mount the media
  163.         err = MountSecondaryInterrupt( nil, nil );    
  164.     }
  165.     
  166.     return(err);
  167. }
  168.  
  169. //    Always run at task level, can allocate and move memory.
  170. OSStatus DriverCloseCmd (ParmBlkPtr pb)
  171. {
  172. #pragma unused (pb)
  173.     OSStatus        err = noErr;
  174.  
  175.     // If an interrupt timer is set, cancel it!
  176.     ClearMountInterruptTimer();
  177.         
  178.     // If a command is pending, wait for it to finish.  Since the close
  179.     // call is made at Task Time, this polling will not cause a deadlock.
  180.     while(    gTheDriverGlobals.drivePB.theIOPB != nil )
  181.     {
  182.         // Do Nothing till the command is done
  183.     }
  184.  
  185.     // Let the world know we can no longer do DriverGestalt calls
  186.     DriverGestaltOff(gTheDriverGlobals.drvrRefNum);
  187.  
  188.     // Dequeue all drive volumes 
  189.     RemoveDriveQueueElement();
  190.  
  191.     // tell the DAM to terminate
  192.     err = TerminateDeviceAccess( gTheDriverGlobals.drvrRefNum, &gTheDriverGlobals.drvrRegEntryID );
  193.                                                             
  194.     DestroyMediaIconFamily();
  195.  
  196.     return (err);
  197. }
  198.  
  199. //    May run at interrupt level, CANNOT allocate or move memory.
  200. OSStatus DriverControlCmd (    IOCommandID ioCommandID, ParmBlkPtr pb )
  201. {
  202.     OSStatus        err            = noErr;
  203.     CntrlParamPtr    pbPtr;
  204.     SInt16            driveNum;
  205.  
  206.     pbPtr = (CntrlParamPtr) pb;
  207.     driveNum = pbPtr->ioVRefNum;
  208.     
  209.     // Parse the control codes…
  210.     switch(pbPtr->csCode) 
  211.     {
  212.         case killCode:
  213.         {
  214.             // The Device Manager will convert this control code to the
  215.             // equivalent function code and call DoDriverIO
  216.             err = controlErr;
  217.         }
  218.         break;
  219.         
  220.         case kVerify:
  221.         {
  222.             // Verify the media, Currently, only done for floppy disks.
  223.             if( IsDriveAFloppy( driveNum ) == true )
  224.             {
  225.                 // Since this call does not complete immediatley for floppy disks,
  226.                 // set up the information so that the StandardControlCallCompletion
  227.                 // routine can finish the IO when called.
  228.                 BlockZero((Ptr) &gTheDriverGlobals.drivePB, sizeof(DriveRequestPB));
  229.                 
  230.                 gTheDriverGlobals.drivePB.theIOPB            = nil;
  231.                 gTheDriverGlobals.drivePB.ioCommandID         = ioCommandID;
  232.                 err = FloppyControlCallSupport( (UInt32) &gTheDriverGlobals.drivePB, pbPtr, &StandardControlCallCompletion );
  233.             }
  234.             else if( IsDriveNumberValid( driveNum ) == true )
  235.             {
  236.                 // For non-floppy disks, report noErr to make the system
  237.                 // think that the driver formatted the media.  This may
  238.                 // need to be changed to be drive dependent if there is 
  239.                 // a device that the driver supports that has no low-level
  240.                 // format already on the media.
  241.                 err = noErr;
  242.             }
  243.             else
  244.             {
  245.                 // The requested drive doesn't belong to this driver,
  246.                 // return a no such drive error.
  247.                 err = nsDrvErr;
  248.             }
  249.         }
  250.         break;
  251.  
  252.         case kFormat:
  253.         {
  254.             // Format the media. Currently, only done for floppy disks.
  255.             if( AreThereMountedDrives() == false )
  256.             {
  257.                 // The requested drive doesn't have media inserted,
  258.                 // return a no such drive error.
  259.                 err = nsDrvErr;
  260.                 break;
  261.             }
  262.  
  263.             if ( IsDriveNumWriteProtected(driveNum) == true )
  264.             {
  265.                 // The media is write protected, return a write protect error.
  266.                 err = wPrErr;
  267.                 break;
  268.             }
  269.             
  270.             if( IsDriveAFloppy( driveNum ) == true )
  271.             {
  272.                 // Since this call does not complete immediately for floppy disks,
  273.                 // set up the information so that the StandardControlCallCompletion
  274.                 // routine can finish the IO when called.
  275.                 BlockZero((Ptr) &gTheDriverGlobals.drivePB, sizeof(DriveRequestPB));
  276.                 
  277.                 gTheDriverGlobals.drivePB.theIOPB            = nil;
  278.                 gTheDriverGlobals.drivePB.ioCommandID         = ioCommandID;
  279.                 err = FloppyControlCallSupport( (UInt32) &gTheDriverGlobals.drivePB, pbPtr, &StandardControlCallCompletion );
  280.             }
  281.             else if( IsDriveNumberValid( driveNum ) == true )
  282.             {
  283.                 // For non-floppy disks, report noErr to make the system
  284.                 // think that the driver formatted the media.  This may
  285.                 // need to be changed to be drive dependent if there is 
  286.                 // a device that the driver supports that has no low-level
  287.                 // format already on the media.
  288.                 err = noErr;
  289.             }
  290.             else
  291.             {
  292.                 // The requested drive doesn't belong to this driver,
  293.                 // return a no such drive error.
  294.                 err = nsDrvErr;
  295.             }
  296.         }
  297.         break;
  298.  
  299.         case kEject:
  300.         {
  301.             // We get this call whenever a volume is put away (dragged to trash).  
  302.             if ( IsDriveNumberValid( driveNum ) == false )    
  303.             {
  304.                 // If we did not find a drive and volume, report drive not found error.
  305.                 err = nsDrvErr;
  306.                 break;
  307.             }
  308.  
  309.             // This is probably not needed because there could be media in the drive without
  310.             // having a partition marked as mounted.
  311. /*            if( AreThereMountedDrives() == false )*/
  312. /*            {*/
  313. /*                err = nsDrvErr;                // no Media is inserted, return an error*/
  314. /*                break;*/
  315. /*            }*/
  316.             
  317.             EjectDriveNum( driveNum );
  318.             
  319.             if ( AreThereMountedDrives() == false )    // If no more mounted volumes…
  320.             {
  321.                 // Since this call does not complete immediately, set up the information 
  322.                 // so that the StandardControlCallCompletion routine can finish the IO when called.
  323.                 BlockZero((Ptr) &gTheDriverGlobals.drivePB, sizeof(DriveRequestPB));
  324.                 
  325.                 gTheDriverGlobals.drivePB.currentExecutionState = kEjectStartState;
  326.                  EjectTheCartridge( (UInt32)&gTheDriverGlobals, noErr );
  327.                 err = noErr;
  328.             }
  329.         }
  330.         break;
  331.  
  332.         case kDriveIcon:                    // Return icon displayed during media initialization
  333.         case kMediaIcon:                    // Return icon displayed on desktop for media
  334.         {
  335.             if( AreThereMountedDrives() == false )
  336.             {
  337.                 err = nsDrvErr;            // no Media is inserted, return an error
  338.                 break;
  339.             }
  340.             else
  341.             {
  342.                 if ( IsDriveNumberValid( driveNum ) == false )    // Check if drive number is invalid
  343.                 {
  344.                     err = nsDrvErr;                                // report drive not found error
  345.                     break;
  346.                 }
  347.                     
  348.                 GetMediaIconForDriveNum( driveNum, (DiskIcon *) &gDriveIcon.mediaIcon);
  349.     
  350.                 // Copy over the DriveInfo string from the global param block        
  351.                 PStrCopy(gDriveIcon.mediaWhereString, GetDeviceWhereString() );
  352.     
  353.                 // Finally, return the pointer to the icon in csParam
  354.                 *(MediaIconStruct**)pbPtr->csParam = &gDriveIcon;    
  355.     
  356.                 err = noErr;                // clear any error from above
  357.             }
  358.         }
  359.         break;
  360.  
  361.         case kDriveInfo:
  362.         {
  363.             pbPtr->csParam[0] = 0;            // Upper Word is always 0;
  364.             pbPtr->csParam[1] = 0;            // Clear Lower Word
  365.             
  366.             if( IsDriveAFloppy( driveNum ) == true )
  367.             {
  368.                 err = FloppyControlCallSupport( (UInt32) &gTheDriverGlobals.drivePB, pbPtr, &StandardControlCallCompletion );
  369.             }
  370.             else if( IsDriveNumberValid( driveNum ) == true )
  371.             {
  372.                 // If we currently have a LS-120 loaded, return the following info
  373.                 pbPtr->csParam[1] =     ( 1 << 11 )     // Drive cardinality (0 - primary, 1 - secondary)
  374.                                     |    ( 0 << 10 )        // Media removability ( 0 - removable, 1 - fixed )
  375.                                     |     ( 1 << 9 )         // Interface ( 0 - floppy, 1 - SCSI )        
  376.                                     |     ( 1 << 8 )         // Location ( 0 - internal, 1 - external )
  377.                                                         // Bits 4,5,6,7 are reserved
  378.                                     |     1;                // Drive Type ( use 1 for Unknown drive (Not a floppy) )
  379.             }
  380.             else
  381.             {
  382.                 err = nsDrvErr;                // requested drive doesn't belong to this driver
  383.             }
  384.         }
  385.         break;
  386.  
  387.         
  388.         case kDriverConfigureCode:
  389.         {
  390.             DriverConfigParam             *configPtr;            // local pointer to drive config structure
  391.                         
  392.             configPtr = (DriverConfigParam *) pbPtr;
  393.             switch(configPtr->driverConfigureSelector) 
  394.             {
  395.                 case kdgFlush:
  396.                 {
  397.                     // Issue a Flush cache Command to the drive
  398.                     //err = FlushDriveWriteCache(&gTheDriverGlobals.drivePB.executePB, nil);
  399.                     err = noErr;
  400.                 }
  401.                 break;
  402.                 
  403.                 default:
  404.                 {
  405.                     err = controlErr;
  406.                 }
  407.                 break;
  408.             }
  409.         }
  410.         break;
  411.  
  412.         case kcsSetBootPartitionCode:                // Set Startup Partition
  413.         {
  414.             // If the media is partitioned, this should update the boot partition
  415.             // status information in the partition map on the drive.
  416.             err = noErr;
  417.         }
  418.         break;
  419.  
  420. #if 0        
  421.         case kcsMountVolume:                // Mount Volume
  422.         {
  423.             // Who uses this call, and what exactly is its purpose?
  424.             if (!vol->hasPMapEntry)            // volume must have a partition map entry on media
  425.                 err = controlErr;
  426.             else 
  427.             {
  428.                 if (vol->inDriveQ == false)
  429.                 {
  430.                     // Added following two lines. Bug#1607768 & 1630408.
  431.                     vol->vRefNum = NextQDrive();        // assign a logical drive number
  432.                     AddDrive(gDrvrRefNum, vol->vRefNum, &vol->driveStatus.drvQEl);
  433.                     vol->inDriveQ = true;
  434.                 }
  435.             }
  436.             
  437.             if (PostEvent(diskEvt, vol->vRefNum) == noErr)     // Post event for system to mount
  438.                 vol->partmounted = true;
  439.         }
  440.         break;
  441. #endif
  442.  
  443.         case kMediaPowerCSCode:                // Set Power Mode
  444.         {
  445.             err = controlErr;
  446.         }
  447.         break;
  448.         
  449.         // Drive Queue support control calls
  450.         case kcsSetDriverMode:                // Set Driver Mode
  451.         case kcsManageReadOnlyMediaQueue:
  452.         {
  453.             BlockZero((Ptr) &gTheDriverGlobals.drivePB, sizeof(DriveRequestPB));
  454.             
  455.             gTheDriverGlobals.drivePB.theIOPB            = nil;
  456.             gTheDriverGlobals.drivePB.ioCommandID         = ioCommandID;
  457.             
  458.             // There are no mounted volumes, it is safe to change driver modes
  459.             err = DriveQueueControlCallSupport( (UInt32) &gTheDriverGlobals.drivePB, pbPtr, &StandardControlCallCompletion );
  460.         }
  461.         break;
  462.  
  463.         // These are the floppy only control calls
  464.         case kSetTagBuffer:                    // This is a floppy specific control call
  465.         case kTrackCache:                    // This is a floppy specific control call
  466.         case kDiskCopy:                        // Floppy specific call to support the DiskCopy Format and Write Image function
  467.         {
  468.             if( IsDriveAFloppy( driveNum ) == true )
  469.             {
  470.                 err = FloppyControlCallSupport( (UInt32) &gTheDriverGlobals.drivePB, pbPtr, &StandardControlCallCompletion );
  471.             }
  472.             else
  473.             {
  474.                 err = controlErr;            // If not a floppy, this call is not supported.
  475.             }
  476.         }
  477.         break;
  478.         
  479.         // This is where the CD/DVD specific control calls should go
  480.         
  481.         // All PC Exchange specific control calls
  482.         case kRegisterPartition:            // Register New Partition
  483.         case kGetADrive:                    // Get A Drive (Create New Partition)
  484.         case kProhibitMounting:
  485.         {
  486.             err = PCExchangeControlCallSupport( (UInt32) &gTheDriverGlobals.drivePB, pbPtr, &StandardControlCallCompletion );
  487.         }
  488.         break;
  489.  
  490.         // If it hasn't been handle, maybe it is a Device Access Module specific control call.
  491.         default:
  492.         {
  493.             BlockZero((Ptr) &gTheDriverGlobals.drivePB, sizeof(DriveRequestPB));
  494.             
  495.             gTheDriverGlobals.drivePB.theIOPB            = nil;
  496.             gTheDriverGlobals.drivePB.ioCommandID         = ioCommandID;
  497.             err = HandleControlRequest((UInt32) &gTheDriverGlobals.drivePB, pbPtr, &InitializeDriveAccessDone);
  498.             if (err != kRequestPending )
  499.             {
  500.                 gTheDriverGlobals.drivePB.theIOPB            = nil;
  501.                 gTheDriverGlobals.drivePB.ioCommandID         = 0;
  502.             }
  503.         }
  504.         break;
  505.     }
  506.         
  507.     return err;
  508. }
  509.  
  510. void StandardControlCallCompletion(UInt32 userData, OSStatus status )
  511. {
  512.     OSStatus         err = status;
  513.     DriveRequestPB    *ourPB;
  514.  
  515.     ourPB = ((DriveRequestPB *) userData);
  516.  
  517.     // Signal completion of the command to the operating system
  518.     ourPB->theIOPB = nil;
  519.     FinishCommandProcessing(ourPB->ioCommandID, err);
  520. }
  521.  
  522. void InitializeDriveAccessDone(UInt32 userData, OSStatus status )
  523. {
  524.     OSStatus         err = status;
  525.     DriveRequestPB    *ourPB;
  526.     
  527.     ourPB = ((DriveRequestPB *) userData);
  528.  
  529.     if( err == noErr )
  530.     {
  531.         err = InstallDriveQueueElement( gTheDriverGlobals.drvrRefNum );
  532.         
  533.         if (err == noErr)
  534.         {
  535.             // See if we can mount the media
  536.             err = MountSecondaryInterrupt(nil,nil);
  537.             //err = SetMountInterruptTimer();
  538.         }
  539.         else
  540.         {
  541.             err = kClassNotConfiguredError;
  542.         }
  543.     }
  544.  
  545.     // Signal completion of the command to the operating system
  546.     ourPB->theIOPB = nil;
  547.     FinishCommandProcessing( ourPB->ioCommandID, err);
  548. }
  549.  
  550. //    May run at interrupt level, CANNOT allocate or move memory.
  551. OSStatus DriverStatusCmd ( IOCommandID ioCommandID, ParmBlkPtr pb)
  552. {
  553. #pragma unused ( ioCommandID )
  554.  
  555.     OSStatus            err            = noErr;
  556.     CntrlParamPtr         pbPtr;
  557.     SInt16                driveNum;
  558.  
  559.     // Cast the pb pointer to a CntrlParamPtr for easier access to the fields 
  560.     pbPtr = (CntrlParamPtr) pb;
  561.     driveNum = pbPtr->ioVRefNum;
  562.  
  563.     // Handle the status call
  564.     switch(pbPtr->csCode)
  565.     {
  566.         case kDriveStatus:
  567.         {
  568.             if ( IsDriveNumberValid( driveNum ) == false )
  569.             {
  570.                 err = nsDrvErr;                // requested drive doesn't belong to this driver
  571.             }
  572.             else
  573.             {
  574.                 GetDriveStatusForDriveNum( driveNum, (DrvSts *) &pbPtr->csParam[0]);
  575.             }
  576.         }
  577.         break;
  578.     
  579.         case kDriverGestaltCode:
  580.         {
  581.             DriverGestaltParam             *gestaltPtr;            // local pointer to driver gestalt structure
  582.  
  583.             gestaltPtr = (DriverGestaltParam *) pbPtr;
  584.             switch(gestaltPtr->driverGestaltSelector) 
  585.             {
  586.                 case kdgVersion:
  587.                 {
  588.                     // Return information on the driver version
  589.                     // Get this from the DAM because the driver version may be 
  590.                     // different for each attachment interface.
  591.                     NumVersion* numVersion;
  592.                     
  593.                     numVersion = GetDriverGestaltVersionResponse(gestaltPtr);
  594.                     GetDriverVersionNumber(    numVersion );            
  595.                 }
  596.                 break;
  597.  
  598.                 case kdgSupportedMediaTypes:
  599.                 {
  600.                     // Let this be processed by the DAM since it has knowledge 
  601.                     // of all possible media types for this device.
  602.                     DriverGestaltSupportedMediaTypesResponse** supptMediaResponse = GetDriverGestaltSupportedMediaTypesResponse (gestaltPtr);
  603.                     
  604.                     *supptMediaResponse = GetSupportedMediaTypesPtr();
  605.                 }
  606.                 break;
  607.                 
  608.                 case kdgDeviceType:
  609.                 {
  610.                     // Return the type of media for this drive number.
  611.                     DriverGestaltDevTResponse*     deviceTypeResponse = GetDriverGestaltDevTResponse(gestaltPtr);
  612.  
  613.                     if( IsDriveNumberValid( driveNum ) == true )
  614.                     {
  615.                         deviceTypeResponse->deviceType = GetMediaTypeForDriveNum( driveNum );
  616.                     }
  617.                     else
  618.                     {
  619.                         // requested drive doesn't belong to this driver
  620.                         err = nsDrvErr;
  621.                     }
  622.                 }
  623.                 break;
  624.                 
  625.                 case kdgInterface:
  626.                 {
  627.                     // Return the interface of the drive in ioVRefNum.
  628.                     DriverGestaltIntfResponse* interfaceResponse = GetDriverGestaltIntfResponse(gestaltPtr);
  629.                     
  630.                     interfaceResponse->interfaceType = GetDriverGestaltIntfForInterface() ;
  631.                 }
  632.                 break;
  633.  
  634.                 case kdgSync:
  635.                 {
  636.                     // Return true if the driver supports only synchronous behavior
  637.                     // Since the native drivers will always be able to handle any type of call, 
  638.                     // this will always return false.
  639.                     DriverGestaltSyncResponse*    syncResponse = GetDriverGestaltSyncResponse(gestaltPtr);
  640.  
  641.                     syncResponse->behavesSynchronously = false;
  642.                 }
  643.                 break;
  644.                     
  645.                 case kdgBoot:
  646.                 {
  647.                     // Return the ID of the boot device for PRAM storage
  648.                     // Since NewWorld Native drivers will have a new DriverGestalt for
  649.                     // returning the boot device path, this should never be required to
  650.                     // be supported by 'ndrv's.
  651.                     err = statusErr;
  652.                 }
  653.                 break;
  654.  
  655.                 case kdgWide:
  656.                 {
  657.                     // Return whether driver supports large volume addressing (> 4GByte)
  658.                     // Since the UnitTable Read/Write handles this automatically, all
  659.                     // Fusion drivers support large volume or wide addressing.
  660.                     *(GetDriverGestaltBooleanResponse(gestaltPtr)) = true;
  661.                 }
  662.                 break;
  663.  
  664.                 case kdgPurge:        // Return if we can be closed and purged from memory.
  665.                 {
  666.                     DriverGestaltPurgeResponse* response = GetDriverGestaltPurgeResponse(gestaltPtr);
  667.                                         
  668.                     // This will be handled by the appropriate expert.
  669.                     // All 'ndrv's should correctly handle a close request, and once closed 
  670.                     // and finalized, be removed by the appropriate Expert/Loader/Manager
  671.                     response->purgePermission = kmNoCloseNoPurge;
  672.                     response->purgeDriverPointer = (Ptr) nil;
  673.                 }
  674.                 break;
  675.                     
  676.                 case kdgSupportsSwitching:
  677.                 {
  678.                     // Return whether driver supports low power control call (csCode = 70h)
  679.                     *(GetDriverGestaltBooleanResponse(gestaltPtr)) = false;
  680.                 }
  681.                 break;
  682.                     
  683.                 case kdgSupportsPowerCtl:
  684.                 {
  685.                     // Return whether driver supports low power control call (csCode = 70h)
  686.                     *(GetDriverGestaltBooleanResponse(gestaltPtr)) = false;
  687.                 }
  688.                 break;
  689.                     
  690.                 case kdgAPI:
  691.                 {
  692.                     // Return whether driver supports PC-Exchange Control and Status calls
  693.                     // related to partitioning.
  694.                     DriverGestaltAPIResponse* apiResponse = GetDriverGestaltAPIResponse(gestaltPtr);
  695.                     
  696.                     apiResponse->partitionCmds = 1;
  697.                 }
  698.                 break;
  699.                 
  700.                 case kdgFlush:
  701.                 {
  702.                     // Return whether driver supports the Cache flush Control call,
  703.                     // and whether the finder should tell us to flush our cache
  704.                     DriverGestaltFlushResponse* response = GetDriverGestaltFlushResponse(gestaltPtr);
  705.                     
  706.                     response->canFlush = true;
  707.                     if( AreThereMountedDrives() == false )
  708.                     {
  709.                         response->needsFlush = false;     // Don't need a flush call because there is no media
  710.                     }
  711.                     else
  712.                     {
  713.                         response->needsFlush = true;     // Needs a flush call because media is present
  714.                     }
  715.                     response->canFlush = false;
  716.                     response->needsFlush = false;
  717.                 }
  718.                 break;
  719.  
  720.                 case kdgEject:
  721.                 {
  722.                     // Return whether driver wants eject call for shutdown and restart
  723.                     // Eject on restart or shutdown
  724.                     DriverGestaltEjectResponse* response = GetDriverGestaltEjectResponse(gestaltPtr);
  725.                     
  726.                     response->ejectFeatures = 0L; // Clear both Dont Eject Bits, kShutDownDontEject and kRestartDontEject.
  727.                 }
  728.                 break;
  729.  
  730.                 case kdgVMOptions:
  731.                 {
  732.                     // Return whether drive can be used for Virtual Memory.
  733.                     // Determine the return value by whether the interface can
  734.                     // support VM and whether the media type is suitable for VM.
  735.                     DriverGestaltVMOptionsResponse* response = GetDriverGestaltVMOptionsResponse(gestaltPtr);
  736.  
  737.                     // Since this is a per drive call and not a per driver call
  738.                     // make sure that the information for a valid drive is being requested.
  739.                     if( IsDriveNumberValid( driveNum ) == false )
  740.                     {
  741.                         // requested drive doesn't belong to this driver
  742.                         err = nsDrvErr;
  743.                     }
  744.                     else
  745.                     {
  746.                         if (( DoesInterfaceHaveVirtualMemoryCapabilities() == false ) || (IsDriveAFloppy( driveNum ) == true))
  747.                         {
  748.                             // The current interface does not support VM page mapping, or
  749.                             // the media is floppy disk media and we do not want to put
  750.                             // this into the VM Page path.
  751.                             // Let the system know by returning the Allow None value.
  752.                             response->vmOptions = kAllowVMNoneMask;
  753.                         }
  754.                         else
  755.                         {
  756.                             if ( IsDriveNumWriteProtected(driveNum) == true )
  757.                             {
  758.                                 // The disk is write protect, let the system know that
  759.                                 // this drive can only be mapped for reading.
  760.                                 response->vmOptions = kAllowVMReadOnlyMask;
  761.                             }
  762.                             else
  763.                             {
  764.                                 // The disk can be used for both reading and writing.
  765.                                 response->vmOptions = kAllowVMReadWriteMask;
  766.                             }
  767.                         }
  768.                     }
  769.                 }
  770.                 break;
  771.  
  772.                 case kdgMediaInfo:
  773.                 {
  774.                     // Return back specific information about our media
  775.                     // This is a driver level call and does not require that
  776.                     // a drive queue number is specified.
  777.                     DriverGestaltMediaInfoResponse* response = GetDriverGestaltMediaInfoResponse(gestaltPtr);
  778.                     UInt32                            currentCapacity, currentBlockSize;
  779.                     Boolean                            isWriteProtected;
  780.                     OSType                            currentMedia;
  781.                     
  782.                     GetMediaProperties( ¤tCapacity,  ¤tBlockSize, &isWriteProtected, ¤tMedia); 
  783.  
  784.                     response->numberBlocks     = currentCapacity;
  785.                     response->blockSize     = currentBlockSize;
  786.                     if ( currentCapacity == 0 )
  787.                     {
  788.                         // If the capacity is 0, media is not in the drive.
  789.                         response->mediaType = kMediaTypeNoMedia;
  790.                     }
  791.                     else
  792.                     {
  793.                         response->mediaType = kMediaTypeUnknown;
  794.                     }
  795.                 }
  796.                 break;
  797.  
  798.                 /* Return a pointer to a IconFamily ('icns') data structure for */
  799.                 /* Disk Driver physical drive (formerly in csCode 22) in driverGestaltResponse. */
  800.                 case kdgPhysDriveIconSuite:
  801.  
  802.                 /* Return a pointer to a IconFamily ('icns') data structure for */
  803.                 /* Disk Driver media (formerly in csCode 21) in driverGestaltResponse. */
  804.                 case kdgMediaIconSuite:
  805.                 {
  806.                     // Check to see if this is a drive that own
  807.                     if( IsDriveNumberValid( driveNum ) == true )
  808.                     {
  809.                         IconFamilyPtr theMediaFamilyPtr = nil;
  810.                         
  811.                         if( IsDriveAFloppy( driveNum ) == true )
  812.                         {
  813.                             theMediaFamilyPtr = GetFloppyIconFamilyPtr();
  814.                         }
  815.                         else
  816.                         {
  817.                             theMediaFamilyPtr = GetMediaIconFamilyPtr();
  818.                         }
  819.                         
  820.                         if ( theMediaFamilyPtr != nil )
  821.                         {
  822.                             // We were able to get the Icon Family ptr, return it in the GestaltParam Struct
  823.                             gestaltPtr->driverGestaltResponse = (UInt32) theMediaFamilyPtr;
  824.                         }
  825.                         else
  826.                         {
  827.                             // We were not able to get the Icon Family pointer, return a status error and
  828.                             // let the system use the default icons.
  829.                             err = statusErr;
  830.                         }
  831.                     }
  832.                     else
  833.                     {
  834.                         err = nsDrvErr;                // requested drive doesn't belong to this driver
  835.                     }
  836.                 }
  837.                 break;
  838.  
  839.                 case kdgMediaName:
  840.                 {
  841.                     /* Return a pointer to a pascal string describing the Disk Driver (formerly in csCode 21) in driverGestaltResponse. */
  842.                     gestaltPtr->driverGestaltResponse = (UInt32) GetDeviceWhereString();
  843.                 }
  844.                 break;
  845.                 
  846.                 case kdgDeviceReference:
  847.                 {
  848.                     GetDriverGestaltDeviceReferenceResponse(gestaltPtr)->devRef = GetDeviceReferenceNumber();
  849.                 }
  850.                 break;
  851.  
  852.                 case kdgNameRegistryEntry:
  853.                 {
  854.                     GetDriverGestaltNameRegistryResponse(gestaltPtr)->entryID = &gTheDriverGlobals.drvrRegEntryID;
  855.                 }
  856.                 break;
  857.                 
  858.                 case kdgDeviceModelInfo:
  859.                 {
  860.                     *(GetDriverGestaltDeviceModelInfoResponse(gestaltPtr)) = GetDeviceInfoPtr();
  861.                 }
  862.                 break;
  863.                 
  864.                 case kdgOpenFirmwareBootSupport:
  865.                 case kdgOpenFirmwareBootingSupport:
  866.                 {
  867.                     DriveQRecPtr     theDriveRec;
  868.                     
  869.                     theDriveRec = FindDriveQRecForDriveNum( driveNum );
  870.                     
  871.                     if (theDriveRec == nil)
  872.                     {
  873.                         // Invalid drive number, return an error
  874.                         err = nsDrvErr;
  875.                     }
  876.                     else
  877.                     {
  878.                         if ( theDriveRec->partMapEntryNum == 0 )
  879.                         {
  880.                             // If the PMap entry number is zero, the media is not paritioned
  881.                             (GetDriverGestaltOFBootSupportResponse(gestaltPtr))->bootPartitionQualifier = kOFBootNotPartitioned;
  882.                         }
  883.                         else
  884.                         {
  885.                             (GetDriverGestaltOFBootSupportResponse(gestaltPtr))->bootPartitionQualifier = kOFBootSpecifiedPartition;
  886.                             (GetDriverGestaltOFBootSupportResponse(gestaltPtr))->bootPartitionMapEntry = theDriveRec->partMapEntryNum;
  887.                         }
  888.                     }
  889.                 }
  890.                 break;
  891.  
  892.                 case kdgDriverUtilitySupport:
  893.                 {
  894.                     *(GetDriverGestaltBooleanResponse(gestaltPtr)) = true;
  895.                 }
  896.                 break;
  897.  
  898.                 default:
  899.                 {
  900.                     // This call was not handled here, pass to the DAM to see if it knows what to do.
  901.                     err = HandleStatusRequest((UInt32) nil, pbPtr, nil);
  902.                 }
  903.             }
  904.         }
  905.         break;
  906.         
  907.         case kcsGetBootPartitionStatus:     // Is this the boot partition?
  908.         {
  909.             // Return 1 if this drive is the startup partition,
  910.             // else return 0
  911.             pbPtr->csParam[0] = 1;
  912.             err = noErr;
  913.         }
  914.         break;
  915.  
  916.         case kMediaPowerCSCode:
  917.         {
  918.             // Return the current power mode of the drive.  NOTE: The power modes (kMediaModeOn,
  919.             // kMediaModeStandBy, kMediaModeSuspend, and kMediaModeOff) are not the same as the ATA modes.
  920.             // The primary difference is kMediaModeSuspend = Standby and kMediaModeStandBy = Idle
  921.             if( AreThereMountedDrives() == false )
  922.             {
  923.                 err = nsDrvErr;            // no Media is inserted, return an error
  924.                 break;
  925.             }
  926.             else
  927.             {
  928.                 UInt8         powerMode = kMediaModeOn;        // Always report that the drive is active
  929.                 
  930.                 // Return the power mode in the upper byte of csParam[0], lower byte is cleared
  931.                 pbPtr->csParam[0] = powerMode << 8;
  932.             }
  933.         }
  934.         break;
  935.  
  936.         // Drive Queue support status calls
  937.         case kcsGetDriverMode:                // Get Driver Mode
  938.         case kcsManageReadOnlyMediaQueue:
  939.         {
  940.             err = DriveQueueStatusCallSupport( (UInt32) &gTheDriverGlobals.drivePB, pbPtr, nil );
  941.         }
  942.         break;
  943.  
  944.         // All Floppy disk specific status calls
  945.         case kReturnFormatList:
  946.         case kMFMStatus:
  947.         case kDuplicatorVersion:                            // DiskCopy version supported
  948.         {
  949.             if( IsDriveAFloppy( driveNum ) == true )
  950.             {
  951.                 err = FloppyStatusCallSupport( (UInt32) &gTheDriverGlobals.drivePB, pbPtr, nil );
  952.             }
  953.             else
  954.             {
  955.                 err = statusErr;
  956.             }
  957.         }
  958.         break;
  959.     
  960.         // All PC Exchange specific status calls
  961.         case kGetPartitionStatus:
  962.         case kGetPartInfo:
  963.         {
  964.             err = PCExchangeStatusCallSupport( (UInt32) &gTheDriverGlobals.drivePB, pbPtr, nil );
  965.         }
  966.         break;
  967.         
  968.         // Unrecognized status call, should add call to the DAM's status routine.
  969.         default:
  970.         {
  971.             err = statusErr;
  972.         }
  973.         break;
  974.     }
  975.  
  976.     return(err);
  977. }
  978.  
  979. //    May run at interrupt level, CANNOT allocate or move memory.
  980. OSStatus DriverReadCmd (    IOCommandID        ioCommandID,
  981.                             ParmBlkPtr        pb)
  982. {
  983.     OSStatus        err = ioErr;
  984.  
  985.     if(  AreThereMountedDrives() == false )
  986.     {
  987.         err = ioErr;
  988.     }
  989.     else
  990.     {
  991.         // Clear out the Drive request PB
  992.         BlockZero((Ptr) &gTheDriverGlobals.drivePB, sizeof(DriveRequestPB));
  993.         
  994.         gTheDriverGlobals.drivePB.theIOPB            = pb;
  995.         gTheDriverGlobals.drivePB.ioCommandID         = ioCommandID;
  996.     
  997.         err = DoReadWriteCommand( (UInt32) &gTheDriverGlobals.drivePB, (IOParamPtr) pb, ((IOParamPtr) pb)->ioVRefNum, false, &ReadWriteFinishCommand );
  998.         if ( err != kRequestPending )
  999.         {
  1000.             // The request is not pending, This means that it is either done, or an error
  1001.             // occurred.  Either way, we need to clear out the Drive request PB.
  1002.             BlockZero((Ptr) &gTheDriverGlobals.drivePB, sizeof(DriveRequestPB));
  1003.         }
  1004.     }
  1005.     
  1006.     return(err);
  1007. }
  1008.  
  1009. //    May run at interrupt level, CANNOT allocate or move memory.
  1010. OSStatus DriverWriteCmd (    IOCommandID        ioCommandID,
  1011.                             ParmBlkPtr        pb)
  1012. {
  1013.     OSStatus        err = ioErr;
  1014.  
  1015.     if( AreThereMountedDrives() == false )
  1016.     {
  1017.         err = ioErr;
  1018.     }
  1019.     else
  1020.     {
  1021.         // Clear out the Drive request PB
  1022.         BlockZero((Ptr) &gTheDriverGlobals.drivePB, sizeof(DriveRequestPB));
  1023.     
  1024.         gTheDriverGlobals.drivePB.theIOPB            = pb;
  1025.         gTheDriverGlobals.drivePB.ioCommandID         = ioCommandID;
  1026.             
  1027.         err = DoReadWriteCommand( (UInt32) &gTheDriverGlobals.drivePB, (IOParamPtr) pb, ((IOParamPtr) pb)->ioVRefNum, true, &ReadWriteFinishCommand );
  1028.         if ( err != kRequestPending )
  1029.         {
  1030.             // The request is not pending, This means that it is either done, or an error
  1031.             // occurred.  Either way, we need to clear out the Drive request PB.
  1032.             BlockZero((Ptr) &gTheDriverGlobals.drivePB, sizeof(DriveRequestPB));
  1033.         }
  1034.     }
  1035.     
  1036.     return(err);
  1037. }
  1038.  
  1039. void ReadWriteFinishCommand ( UInt32 userData, OSStatus status )
  1040. {
  1041.     DriveRequestPBPtr    ourPB;
  1042.     IOParamPtr         iopb;
  1043.  
  1044.     ourPB = (DriveRequestPBPtr) userData;
  1045.     iopb = (IOParamPtr) ourPB->theIOPB;
  1046.     
  1047.     if ( status == noErr )
  1048.     {
  1049.         // All data was transferred, update actCount field
  1050.         iopb->ioActCount = iopb->ioReqCount;
  1051.     }
  1052.     else
  1053.     {
  1054.         // An error occurred, make sure actCount is zeroed
  1055.         iopb->ioActCount = 0;
  1056.     }
  1057.     
  1058.     ourPB->theIOPB = nil;
  1059.     FinishCommandProcessing(ourPB->ioCommandID, status);
  1060. }
  1061.  
  1062.  
  1063. //    May run at interrupt level, CANNOT allocate or move memory.
  1064. OSStatus DriverKillIOCmd (ParmBlkPtr pb)
  1065. {
  1066. #pragma unused (pb)
  1067.     OSStatus    err = noErr;
  1068.  
  1069.     return err;
  1070. }
  1071.  
  1072. #pragma mark -
  1073. #pragma mark Media Mounting Routines
  1074. // **************************************************************************
  1075. // All Mount Timer interrupt routines
  1076. // **************************************************************************
  1077. OSStatus SetMountInterruptTimer( void )
  1078. {
  1079.     OSStatus             status;
  1080.     AbsoluteTime        theWait;
  1081.  
  1082.     // If there is an outstanding timer, cancel it
  1083.     ClearMountInterruptTimer();
  1084.     
  1085.     theWait = DurationToAbsolute(durationSecond);
  1086.     theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  1087.     status = SetInterruptTimer( &theWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1088.     
  1089.     return status;
  1090. }
  1091.  
  1092. OSStatus ClearMountInterruptTimer( void )
  1093. {
  1094.     OSStatus    status = noErr;
  1095.     
  1096.     if( gInterruptTimer != nil )
  1097.     {
  1098.         AbsoluteTime    timeLeft;
  1099.         
  1100.         // Cancel any pending timers
  1101.         status = CancelTimer( gInterruptTimer, &timeLeft);
  1102.         gInterruptTimer = 0;
  1103.     }
  1104.     
  1105.     return status;
  1106. }
  1107.  
  1108.  
  1109. OSStatus MountSecondaryInterrupt( void *p1, void *p2)
  1110. {
  1111. #pragma unused ( p1, p2 )
  1112.     OSStatus             status;
  1113.  
  1114.     gInterruptTimer = 0;
  1115.     
  1116.     if( IsDeviceAccessEnabled() == false )
  1117.     {
  1118.         status = SetMountInterruptTimer();        
  1119.     } 
  1120.     else
  1121.     {
  1122.         gTheDriverGlobals.drivePB.currentExecutionState = kMountStartState;
  1123.         MountTheCartridge( (UInt32) &gTheDriverGlobals.drivePB, noErr );
  1124.     }
  1125.     
  1126.     return noErr;
  1127. }
  1128.  
  1129.  
  1130. // **************************************************************************
  1131. // Mount State Machine and associated completion routines
  1132. // **************************************************************************
  1133. void MountTheCartridge( UInt32 userData, OSStatus status )
  1134. {
  1135.     OSStatus         err = status;
  1136.     DriveRequestPB    *ourPB;
  1137.  
  1138.     ourPB = (DriveRequestPB *) userData;
  1139.     
  1140.     switch (ourPB->currentExecutionState )
  1141.     {
  1142.         case kMountStartState:
  1143.         {            
  1144.             ourPB->currentExecutionState = kMountPreventRemovalState;
  1145.             err = CheckIfMediaIsPresent( (UInt32) ourPB,
  1146.                                         (CheckIfMediaIsPresentCompletionProcPtr) MountTheCartridge );
  1147.         }
  1148.         break;
  1149.  
  1150.         case kMountPreventRemovalState:
  1151.         {            
  1152.             if( status == noErr )
  1153.             {
  1154.                 // Media was detected, lock it in place.
  1155.                 ourPB->currentExecutionState = kMountInstallDriveState;
  1156.                 err = PreventMediaRemoval ((UInt32) ourPB, MountTheCartridge );
  1157.             }
  1158.             else
  1159.             {
  1160.                 err = status;
  1161.             }
  1162.         }
  1163.         break;
  1164.         
  1165.         case kMountInstallDriveState:
  1166.         {            
  1167.             // The device is now mounted, and the Media is locked in place.
  1168.             // There is nothing left for us to do, so just break.
  1169.             InstallDrive( DriveInstallCompletion );
  1170.             err = kRequestPending;
  1171.         }
  1172.         break;
  1173.     }
  1174.     
  1175.     if (( err != kRequestPending ) && ( err != noErr ))
  1176.     {
  1177.         if ( err == kDeviceAccessMediaUnusable )
  1178.         {
  1179.             // We can not use this cartridge, go ahead and eject it
  1180.             BlockZero((Ptr) &gTheDriverGlobals.drivePB, sizeof(DriveRequestPB));
  1181.             
  1182.             gTheDriverGlobals.drivePB.currentExecutionState = kEjectStartState;
  1183.             EjectTheCartridge( (UInt32)&gTheDriverGlobals, noErr );
  1184.         }
  1185.         else
  1186.         {
  1187.             // Reset the interrupt and try again
  1188.             ourPB->currentExecutionState = kMountStartState;
  1189.             err = SetMountInterruptTimer();
  1190.         }
  1191.     }
  1192. }
  1193.  
  1194. void DriveInstallCompletion(Boolean wasSuccessful)
  1195. {
  1196.     if((wasSuccessful == false ) || (GetNumberOfVolumes() == 0))
  1197.     {
  1198.         BlockZero((Ptr) &gTheDriverGlobals.drivePB, sizeof(DriveRequestPB));
  1199.         
  1200.         gTheDriverGlobals.drivePB.currentExecutionState = kEjectStartState;
  1201.         EjectTheCartridge( (UInt32)&gTheDriverGlobals, noErr );
  1202.     }
  1203. }
  1204.  
  1205.  
  1206. #pragma mark -
  1207. #pragma mark Media Ejecting Routines
  1208. // **************************************************************************
  1209. // Eject State Machine and associated completion routines
  1210. // **************************************************************************
  1211. void EjectTheCartridge(    UInt32 theCurrentPB, OSStatus status )
  1212. {
  1213.     OSStatus         err = noErr;
  1214.     DriveRequestPB    *ourPB = (DriveRequestPB *) theCurrentPB;
  1215.     
  1216.     switch (ourPB->currentExecutionState )
  1217.     {
  1218.         case kEjectStartState:
  1219.         {
  1220.             ourPB->currentExecutionState = kEjectAllowRemovalDone;
  1221.             err = AllowMediaRemoval ( (UInt32)ourPB, EjectTheCartridge );     // Allow for Ejects
  1222.             if ( err != noErr )
  1223.             {
  1224.                 // Either an error occurred, or the request is pending.
  1225.                 // In either case, break out of the switch statement
  1226.                 // and let the code outside the switch decide what to do.
  1227.                 break;
  1228.             }
  1229.             
  1230.             // If an immediate noErr occurred from UnlockMedia,
  1231.             // it means that the media is not locked in the drive.
  1232.             // The drive is probably a manual eject drive.
  1233.             // In any case, fall through to the kEjectAllowRemovalDone
  1234.             // state and continue execution.
  1235.         }
  1236.         /* -- break intentionally left out -- */
  1237.                 
  1238.         case kEjectAllowRemovalDone:
  1239.         {
  1240.             ourPB->currentExecutionState = kEjectCartridgeDone;
  1241.             err = EjectCartridge (    (UInt32)ourPB, EjectTheCartridge ); // Now eject the cartridge
  1242.  
  1243.             if( err == noErr )
  1244.             {
  1245.                 ourPB->currentExecutionState = kMountStartState;        // reset the machine
  1246.                 SetMountInterruptTimer();
  1247.             }
  1248.         }
  1249.         break;
  1250.         
  1251.         case kEjectCartridgeDone:
  1252.         {
  1253.             err = noErr;
  1254.             if( status == noErr )
  1255.             {
  1256.                 ourPB->currentExecutionState = kMountStartState;        // reset the machine
  1257.                 SetMountInterruptTimer();
  1258.             }
  1259.         }
  1260.         break;
  1261.     }
  1262.     
  1263.     if ( err != kRequestPending )
  1264.     {
  1265.         if ( ourPB->theIOPB != nil )
  1266.         {
  1267.             // Signal completion of the command to the operating system
  1268.             ourPB->theIOPB = nil;
  1269.             FinishCommandProcessing(ourPB->ioCommandID, noErr);
  1270.         }
  1271.     }
  1272. }
  1273.  
  1274.  
  1275.